forked from bytecodealliance/wasm-micro-runtime
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtrans_wasm_func_name.py
More file actions
213 lines (166 loc) · 6.96 KB
/
trans_wasm_func_name.py
File metadata and controls
213 lines (166 loc) · 6.96 KB
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
"""
It is used to translate jitted functions' names(in out.folded) to coorespond name in name section in .wasm
Usage:
After
```
$ perf script -i perf.data > out.perf
# fold call stacks
$ ./FlameGraph/stackcollapse-perf.pl out.perf > out.folded
```
Add a step:
```
# translate jitted functions' names
$ python translate_wasm_function_name.py --wabt_home <wabt-installation> --folded out.folded <.wasm>
# out.folded -> out.folded.translated
$ ls out.folded.translated
```
Then
```
# generate flamegraph
$ ./FlameGraph/flamegraph.pl out.folded.translated > perf.wasm.svg
```
"""
import argparse
import os
from pathlib import Path
import re
import shlex
import subprocess
def preflight_check(wabt_home: Path) -> Path:
"""
if wasm-objdump exists in wabt_home
"""
wasm_objdump_bin = wabt_home.joinpath("bin", "wasm-objdump")
if not wasm_objdump_bin.exists():
raise RuntimeError(f"wasm-objdump not found in {wabt_home}")
return wasm_objdump_bin
def collect_import_section_content(wasm_objdump_bin: Path, wasm_file: Path) -> dict:
"""
execute "wasm_objdump_bin -j Import -x <wasm_file>" and return a dict like {function: X, global: Y, memory: Z, table: N}
"""
assert wasm_objdump_bin.exists()
assert wasm_file.exists()
command = f"{wasm_objdump_bin} -j Import -x {wasm_file}"
p = subprocess.run(
shlex.split(command),
capture_output=True,
check=False,
text=True,
universal_newlines=True,
)
if p.stderr:
print("No content in import section")
return {}
import_section = {}
for line in p.stdout.split(os.linesep):
line = line.strip()
if not line:
continue
if re.search(r"^-\s+func", line):
import_section.update(function=import_section.get("function", 0) + 1)
else:
pass
assert len(import_section) > 0, "failed to retrive content of import section"
return import_section
def collect_name_section_content(wasm_objdump_bin: Path, wasm_file: Path) -> dict:
"""
execute "wasm_objdump_bin -j name -x wasm_file" and store the output in a dict
{1: xxxx, 2: yyyy, 3: zzzz}
"""
assert wasm_objdump_bin.exists()
assert wasm_file.exists()
command = f"{wasm_objdump_bin} -j name -x {wasm_file}"
p = subprocess.run(
shlex.split(command),
capture_output=True,
check=False,
text=True,
universal_newlines=True,
)
if p.stderr:
raise RuntimeError(f"not found name section in {wasm_file}")
name_section = {}
for line in p.stdout.split(os.linesep):
line = line.strip()
if not line:
continue
# - func[0] <__imported_wasi_snapshot_preview1_fd_close>
if line.startswith("- func"):
m = re.match(r"- func\[(\d+)\] <(.+)>", line)
assert m
func_index, func_name = m.groups()
name_section.update({int(func_index): func_name})
assert name_section
return name_section
def replace_function_name(
import_section: dict, name_section: dict, folded_in: str, folded_out: str
) -> None:
"""
read content in <folded_in>. each line will be like:
quiche::BalsaFrame::ProcessHeaders;non-virtual thunk to Envoy::Http::Http1::BalsaParser::MessageDone;Envoy::Http::Http1::ConnectionImpl::onMessageComplete;Envoy::Http::Http1::ConnectionImpl::onMessageCompleteImpl;Envoy::Http::Http1::ServerConnectionImpl::onMessageCompleteBase;Envoy::Http::ConnectionManagerImpl::ActiveStream::decodeHeaders;Envoy::Http::FilterManager::decodeHeaders;virtual thunk to Envoy::Extensions::Common::Wasm::Context::decodeHeaders;proxy_wasm::ContextBase::onRequestHeaders;proxy_wasm::wamr::Wamr::getModuleFunctionImpl<proxy_wasm::Word, proxy_wasm::Word, proxy_wasm::Word, proxy_wasm::Word>;wasm_func_call;wasm_runtime_call_wasm;wasm_call_function;call_wasm_with_hw_bound_check;wasm_interp_call_wasm;llvm_jit_call_func_bytecode;wasm_runtime_invoke_native;push_args_end;aot_func_internal#3302;aot_func_internal#3308;asm_sysvec_apic_timer_interrupt;sysvec_apic_timer_interrupt;__sysvec_apic_timer_interrupt;hrtimer_interrupt;__hrtimer_run_queues;__remove_hrtimer;rb_next 1110899
symbol names are spearated by ";"
if there is a symbol named like "aot_func#XXX" or "aot_func_internal#XXX", it will be replaced with the function name in name section by index
"""
folded_in = Path(folded_in)
assert folded_in.exists()
folded_out = Path(folded_out)
import_function_count = import_section.get("function", 0)
with folded_in.open("rt", encoding="utf-8") as f_in, folded_out.open(
"wt", encoding="utf-8"
) as f_out:
precheck_mode = False
for line in f_in:
line = line.strip()
if "aot_func_internal" in line:
precheck_mode = True
f_in.seek(0)
for line in f_in:
new_line = []
line = line.strip()
m = re.match(r"(.*) (\d+)", line)
syms, samples = m.groups()
for sym in syms.split(";"):
m = re.match(r"aot_func(_internal)?#(\d+)", sym)
if not m:
new_line.append(sym)
continue
func_idx = int(m.groups()[-1]) + import_function_count
if func_idx in name_section:
wasm_func_name = f"[Wasm] {name_section[func_idx]}"
else:
wasm_func_name = (
f"[Wasm] function[{func_idx + import_function_count}]"
)
if precheck_mode:
# aot_func_internal -> xxx
# aot_func --> xxx_precheck
wasm_func_name += "_precheck" if not m.groups()[0] else ""
else:
# aot_func --> xxx
pass
new_line.append(wasm_func_name)
line = ";".join(new_line)
line += f" {samples}"
f_out.write(line + os.linesep)
print(f"⚙️ {folded_in} -> {folded_out}")
def main(wabt_home: str, wasm_file: str, folded: str) -> None:
wabt_home = Path(wabt_home)
wasm_file = Path(wasm_file)
wasm_objdump_bin = preflight_check(wabt_home)
import_section = collect_import_section_content(wasm_objdump_bin, wasm_file)
name_section = collect_name_section_content(wasm_objdump_bin, wasm_file)
replace_function_name(import_section, name_section, folded, folded + ".translated")
if __name__ == "__main__":
argparse = argparse.ArgumentParser()
argparse.add_argument(
"--folded", help="stackcollapse-perf.pl generated, like out.folded"
)
argparse.add_argument("wasm_file", help="wasm file")
argparse.add_argument("--wabt_home", help="wabt home, like /opt/wabt-1.0.33")
args = argparse.parse_args()
main(args.wabt_home, args.wasm_file, args.folded)