1#!/bin/bash
2if [[ $# -le 1 ]]; then
3	echo "Usage: $0 <executable> [<addresses>] REFS..."
4	exit 1
5fi
6target="$1"
7shift
8
9addresses=""
10if [[ -e "$1" ]]; then
11	addresses="$1"
12	shift
13fi
14
15# path to "us"
16# readlink -f, but more portable:
17dirname=$(perl -e 'use Cwd "abs_path";print abs_path(shift)' "$(dirname "$0")")
18
19# http://stackoverflow.com/a/2358432/472927
20{
21	# compile all refs
22	pushd "$dirname" > /dev/null
23	# if the user has some local changes, preserve them
24	nstashed=$(git stash list | wc -l)
25	echo "==> Stashing any local modifications"
26	git stash --keep-index > /dev/null
27	popstash() {
28		# http://stackoverflow.com/q/24520791/472927
29		if [[ "$(git stash list | wc -l)" -ne "$nstashed" ]]; then
30			echo "==> Restoring stashed state"
31			git stash pop > /dev/null
32		fi
33	}
34	# if the user has added stuff to the index, abort
35	if ! git diff-index --quiet HEAD --; then
36		echo "Refusing to overwrite outstanding git changes"
37		popstash
38		exit 2
39	fi
40	current=$(git symbolic-ref --short HEAD)
41	for ref in "$@"; do
42		echo "==> Compiling $ref"
43		git checkout -q "$ref"
44		commit=$(git rev-parse HEAD)
45		fn="target/release/addr2line-$commit"
46		if [[ ! -e "$fn" ]]; then
47			cargo build --release --example addr2line
48			cp target/release/examples/addr2line "$fn"
49		fi
50		if [[ "$ref" != "$commit" ]]; then
51			ln -sfn "addr2line-$commit" target/release/addr2line-"$ref"
52		fi
53	done
54	git checkout -q "$current"
55	popstash
56	popd > /dev/null
57
58	# get us some addresses to look up
59	if [[ -z "$addresses" ]]; then
60		echo "==> Looking for benchmarking addresses (this may take a while)"
61		addresses=$(mktemp tmp.XXXXXXXXXX)
62		objdump -C -x --disassemble -l "$target" \
63			| grep -P '0[048]:' \
64			| awk '{print $1}' \
65			| sed 's/:$//' \
66			> "$addresses"
67		echo "  -> Addresses stored in $addresses; you should re-use it next time"
68	fi
69
70	run() {
71		func="$1"
72		name="$2"
73		cmd="$3"
74		args="$4"
75		printf "%s\t%s\t" "$name" "$func"
76		if [[ "$cmd" =~ llvm-symbolizer ]]; then
77			/usr/bin/time -f '%e\t%M' "$cmd" $args -obj="$target" < "$addresses" 2>&1 >/dev/null
78		else
79			/usr/bin/time -f '%e\t%M' "$cmd" $args -e "$target" < "$addresses" 2>&1 >/dev/null
80		fi
81	}
82
83	# run without functions
84	log1=$(mktemp tmp.XXXXXXXXXX)
85	echo "==> Benchmarking"
86	run nofunc binutils addr2line >> "$log1"
87	#run nofunc elfutils eu-addr2line >> "$log1"
88	run nofunc llvm-sym llvm-symbolizer -functions=none >> "$log1"
89	for ref in "$@"; do
90		run nofunc "$ref" "$dirname/target/release/addr2line-$ref" >> "$log1"
91	done
92	cat "$log1" | column -t
93
94	# run with functions
95	log2=$(mktemp tmp.XXXXXXXXXX)
96	echo "==> Benchmarking with -f"
97	run func binutils addr2line "-f -i" >> "$log2"
98	#run func elfutils eu-addr2line "-f -i"  >> "$log2"
99	run func llvm-sym llvm-symbolizer "-functions=linkage -demangle=0" >> "$log2"
100	for ref in "$@"; do
101		run func "$ref" "$dirname/target/release/addr2line-$ref" "-f -i" >> "$log2"
102	done
103	cat "$log2" | column -t
104	cat "$log2" >> "$log1"; rm "$log2"
105
106	echo "==> Plotting"
107	Rscript --no-readline --no-restore --no-save "$dirname/bench.plot.r" < "$log1"
108
109	echo "==> Cleaning up"
110	rm "$log1"
111	exit 0
112}
113