Add two csmith benching scripts
This commit is contained in:
parent
df2d6179a8
commit
2f91f732cb
3 changed files with 130 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -33,3 +33,4 @@
|
|||
|
||||
dwarf-assembly
|
||||
__pycache__
|
||||
platform.info
|
||||
|
|
5
benching/csmith/csmith-bench.sh
Executable file
5
benching/csmith/csmith-bench.sh
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
csmith "$@" | \
|
||||
sed 's/#include "csmith.h"/#include "bench.h"\n#include "csmith.h"/g' |\
|
||||
sed 's/return /bench_unwinding(); return/g'
|
124
benching/csmith/gen_call_graph.py
Executable file
124
benching/csmith/gen_call_graph.py
Executable file
|
@ -0,0 +1,124 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
""" Generates the call graph (in dot format) of a C code generated by CSmith.
|
||||
|
||||
This does not parse C code, but performs only string lookups. In particular, it
|
||||
assumes functions are named `func_[0-9]+` or `main`, and that a function
|
||||
implementation is of the form (whitespaces meaningful)
|
||||
|
||||
(static)? RETURN_TYPE func_[0-9]+(.*)
|
||||
{
|
||||
...
|
||||
}
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
def build_graph(prog):
|
||||
func_declare_re = re.compile(r'(?:static )?\S.* (func_\d+|main) ?\(.*\)$')
|
||||
func_call_re = re.compile(r'func_\d+')
|
||||
|
||||
graph = {}
|
||||
cur_function = None
|
||||
|
||||
for line in prog:
|
||||
func_declare_groups = func_declare_re.match(line)
|
||||
if func_declare_groups:
|
||||
func_name = func_declare_groups.group(1)
|
||||
cur_function = func_name
|
||||
graph[func_name] = []
|
||||
|
||||
elif line == '}':
|
||||
cur_function = None
|
||||
|
||||
else:
|
||||
if cur_function is None:
|
||||
continue # Not interresting outside of functions
|
||||
|
||||
last_find_pos = 0
|
||||
call_match = func_call_re.search(line, pos=last_find_pos)
|
||||
|
||||
while call_match is not None:
|
||||
graph[cur_function].append(call_match.group(0))
|
||||
last_find_pos = call_match.end()
|
||||
call_match = func_call_re.search(line, pos=last_find_pos)
|
||||
|
||||
reachable = set()
|
||||
def mark_reachable(node):
|
||||
nonlocal reachable, graph
|
||||
if node in reachable:
|
||||
return
|
||||
reachable.add(node)
|
||||
|
||||
for child in graph[node]:
|
||||
mark_reachable(child)
|
||||
mark_reachable('main')
|
||||
|
||||
delete = []
|
||||
for node in graph:
|
||||
if node not in reachable:
|
||||
delete.append(node)
|
||||
for node in delete:
|
||||
print('> Deleted: {}'.format(node), file=sys.stderr)
|
||||
graph.pop(node)
|
||||
|
||||
return graph
|
||||
|
||||
|
||||
def dump_graph(graph):
|
||||
print('digraph prog {')
|
||||
|
||||
for node in graph:
|
||||
for call in graph[node]:
|
||||
if call in graph:
|
||||
print('\t{} -> {}'.format(node, call))
|
||||
else:
|
||||
print('Wtf is {} (called from {})?'.format(node, call),
|
||||
file=sys.stderr)
|
||||
print('}')
|
||||
|
||||
|
||||
def dump_stats(graph, out_file):
|
||||
entry_point = 'main'
|
||||
|
||||
depth_of = {}
|
||||
def find_depth(node):
|
||||
nonlocal depth_of
|
||||
|
||||
if node in depth_of:
|
||||
return depth_of[node]
|
||||
|
||||
callees = graph[node]
|
||||
if callees:
|
||||
depth = max(map(find_depth, callees)) + 1
|
||||
else:
|
||||
depth = 1
|
||||
depth_of[node] = depth
|
||||
return depth
|
||||
|
||||
print("Call chain max depth: {}".format(find_depth(entry_point)),
|
||||
file=out_file)
|
||||
|
||||
|
||||
def get_prog_lines():
|
||||
def do_read(handle):
|
||||
return handle.readlines()
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
with open(sys.argv[1], 'r') as handle:
|
||||
return do_read(handle)
|
||||
else:
|
||||
return do_read(sys.stdin)
|
||||
|
||||
|
||||
def main():
|
||||
prog = get_prog_lines()
|
||||
graph = build_graph(prog)
|
||||
dump_graph(graph)
|
||||
dump_stats(graph, out_file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in a new issue