diff --git a/.gitignore b/.gitignore index cd1f905..18fc9c2 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ dwarf-assembly __pycache__ +platform.info diff --git a/benching/csmith/csmith-bench.sh b/benching/csmith/csmith-bench.sh new file mode 100755 index 0000000..be30995 --- /dev/null +++ b/benching/csmith/csmith-bench.sh @@ -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' diff --git a/benching/csmith/gen_call_graph.py b/benching/csmith/gen_call_graph.py new file mode 100755 index 0000000..bef0d4b --- /dev/null +++ b/benching/csmith/gen_call_graph.py @@ -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()