1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
Build.dependencies({"git", "make", "rustc", "cargo"})
Build.metric(
"nightly version",
string.gsub(Build.check_output({"rustc", "--version"}).stdout, '^%s*(.*)%s*$', '%1')
)
Step.start("crate")
Step.push("build")
Build.run({"cargo", "build"}) -- `run` automatically records stdout and stderr to log files named after the command
Step.advance("test")
-- default features include "behavior" which gets the relatively slow (and KVM-requiring) behavior validation tests
Build.run({"cargo", "test", "--features", "_debug_internal_asserts", "--", "--skip", "kvm"}, {name="test stdlib/fmt"}) -- artifacts are stored under `name` if that's present
-- but no default features means these aren't built in!
Build.run({"cargo", "test", "--no-default-features"}, {name="test nostdlib/nofmt"})
Build.run({"cargo", "test", "--no-default-features", "--features", "fmt"}, {name="test nostdlib/fmt"})
Build.run({"cargo", "test", "--no-default-features", "--features", "std"}, {name="test nostdlib/std"})
Build.run({"cargo", "test", "--no-default-features", "--features", "unstable"}, {name="test nostdlib/unstable"})
Build.run({"cargo", "test", "--no-default-features", "--features", "std,fmt"}, {name="test nostdlib/std+fmt"})
Build.run({"cargo", "test", "--no-default-features", "--features", "std,fmt,unstable"}, {name="test nostdlib/std+fmt+unstable"})
Step.advance("test-kvm")
Build.run({"cargo", "test", "--release", "--no-default-features", "--features", "behavior"}, {name="test nostdlib/behavior"})
Build.run({"cargo", "test", "--release", "--no-default-features", "--features", "behavior,fmt"}, {name="test nostdlib/behavior+fmt"})
Build.run({"cargo", "test", "--release", "--no-default-features", "--features", "behavior,std"}, {name="test nostdlib/behavior+std"})
Build.run({"cargo", "test", "--release", "--no-default-features", "--features", "behavior,fmt,std,_debug_internal_asserts"}, {name="test nostdlib/behavior+fmt+std"})
Build.run({"cargo", "test", "--release", "--no-default-features", "--features", "behavior,fmt,std,unstable,_debug_internal_asserts"}, {name="test nostdlib/behavior+fmt+std+unstable"})
-- the interesting benchmarking happens through disas-bench, but the in-tree capstone bench isn't gone *quite* yet.
Step.start("bench")
Build.run({"cargo", "test", "--no-default-features", "capstone_bench"}, {name="bench smoketest"})
Step.start("ffi")
Step.push("build")
Build.run({"rustup", "component", "add", "rust-src", "--toolchain", "nightly-x86_64-unknown-linux-gnu"})
Build.run({"cargo", "+nightly", "build", "-Z", "build-std", "--release", "--no-default-features", "--target", Build.environment.vars.native_rust_triple}, {cwd="ffi/"})
Step.advance("validate")
sopath = "ffi/target/" .. Build.environment.vars.native_rust_triple .. "/release/libyaxpeax_x86_ffi_long_mode.so"
Build.run({"ls", sopath})
Build.metric(
"libyaxpeax_x86_ffi_long_mode.so size (bytes)",
Build.environment.size(sopath)
)
Build.artifact(sopath)
-- now run some perf numbers...
Step.start("perf")
Build.run({"git", "clone", "https://github.com/iximeow/disas-bench.git", "disas-bench"})
Build.run({"git", "submodule", "update", "--recursive", "--init"}, {cwd="disas-bench"})
Build.run({"git", "remote", "add", "dev", "../../.."}, {cwd="disas-bench/libs/yaxpeax"})
Build.run({"git", "fetch", "-a", "dev"}, {cwd="disas-bench/libs/yaxpeax"})
Build.run({"git", "checkout", Build.sha}, {cwd="disas-bench/libs/yaxpeax"})
Step.push("build")
Build.run({"make", "make-bench-yaxpeax"}, {cwd="disas-bench/bench/yaxpeax"})
Build.metric(
"bench-yaxpeax-fmt size (bytes)",
Build.environment.size("disas-bench/bench/yaxpeax/bench-yaxpeax-fmt")
)
Build.metric(
"bench-yaxpeax-no-fmt size (bytes)",
Build.environment.size("disas-bench/bench/yaxpeax/bench-yaxpeax-no-fmt")
)
-- fmt
Step.advance("fmt")
bench_start = Build.now_ms()
Build.run({"./bench-yaxpeax-fmt", "20", "0x400", "0x2460400", "../../input/xul.dll"}, {cwd="disas-bench/bench/yaxpeax"})
bench_end = Build.now_ms()
Build.metric("fmt runtime (ms)", bench_end - bench_start)
-- no-fmt
Step.advance("no-fmt")
bench_start = Build.now_ms()
Build.run({"./bench-yaxpeax-no-fmt", "20", "0x400", "0x2460400", "../../input/xul.dll"}, {cwd="disas-bench/bench/yaxpeax"})
bench_end = Build.now_ms()
Build.metric("no-fmt runtime (ms)", bench_end - bench_start)
-- perf
if Build.environment.has("perf") then
perf_setting = Build.check_output({"cat", "/proc/sys/kernel/perf_event_paranoid"})
-- TODO: roll this up into some perf tools in the lua env. for now, if perf
-- event paranoid is >2 then we'll probably just fail the build trying and
-- failing to run perf.
perf_out = Build.check_output({"perf", "stat", "-x", ";", "-e", "cycles,instructions", "./bench-yaxpeax-no-fmt", "20", "0x400", "0x2460400", "../../input/xul.dll"}, {cwd="disas-bench/bench/yaxpeax"})
measurements = {}
for count, unit, name in perf_out.stderr:gmatch("([^;]*);([^;]*);([^;]*)[^\n]*\n?") do
measurements[name] = tonumber(count)
end
insts, good, bad, ms = perf_out.stdout:match("Disassembled (%d*) instructions %((%d*) valid, (%d*) bad%), (%d*) ms")
measurements["decoded"] = tonumber(insts)
measurements["elapsed_ms"] = tonumber(ms)
local instructions_name = "instructions:u"
local cycles_name = "cycles:u"
if measurements[instructions_name] == nil then
instructions_name = "instructions"
cycles_name = "cycles"
end
ipc = measurements[instructions_name] / measurements[cycles_name]
Build.metric("no-fmt IPC", string.format("%.3f", ipc))
inst_per_decode = measurements[instructions_name] / measurements["decoded"]
Build.metric("no-fmt instructions/decode", string.format("%.1f", inst_per_decode))
ms_per_decode = measurements["elapsed_ms"] / measurements["decoded"]
Build.metric("no-fmt ns/decode", string.format("%.2f", ms_per_decode * 1000000))
perf_out = Build.check_output({"perf", "stat", "-x", ";", "-e", "cycles,instructions", "./bench-yaxpeax-fmt", "20", "0x400", "0x2460400", "../../input/xul.dll"}, {cwd="disas-bench/bench/yaxpeax"})
measurements = {}
for count, unit, name in perf_out.stderr:gmatch("([^;]*);([^;]*);([^;]*)[^\n]*\n?") do
measurements[name] = tonumber(count)
end
insts, good, bad, ms = perf_out.stdout:match("Disassembled (%d*) instructions %((%d*) valid, (%d*) bad%), (%d*) ms")
measurements["decoded"] = tonumber(insts)
measurements["elapsed_ms"] = tonumber(ms)
ipc = measurements[instructions_name] / measurements[cycles_name]
Build.metric("fmt IPC", string.format("%.3f", ipc))
inst_per_decode = measurements[instructions_name] / measurements["decoded"]
Build.metric("fmt instructions/decode+display", string.format("%.1f", inst_per_decode))
ms_per_decode = measurements["elapsed_ms"] / measurements["decoded"]
Build.metric("fmt ns/decode+display", string.format("%.2f", ms_per_decode * 1000000))
end
|