Skip to content

Commit 4061eb7

Browse files
authored
feat(cli): bug report synthesis (#131)
1 parent 07a3638 commit 4061eb7

File tree

5 files changed

+84
-1
lines changed

5 files changed

+84
-1
lines changed

doc/cli.md

+20
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,26 @@ yes | nnsmith.model_gen model.type=torch mgen.method=symbolic-cinit \
121121
debug.viz=true
122122
```
123123

124+
## Synthesize bug reports
125+
126+
`nnsmith.report_syn` can synthesize bug reports given a model (read from filesystem) and a backend target (user provided).
127+
It prints a self-contained Python script:
128+
129+
1. For most of the cases, it should be able to directly reproduce the bug.
130+
2. If not, it should serve as a good starting point and developers can modify it a bit to reproduce the bug.
131+
132+
> **Note**
133+
>
134+
> This is an experimental feature and only works for PyTorch models.
135+
136+
> **Warning**
137+
>
138+
> `nnsmith.report_syn` is not guaranteed to reproduce the bug. For strict bug reproduction, please use `nnsmith.model_exec` instead.
139+
140+
```shell
141+
nnsmith.report_syn backend.type="pt2 backend@inductor" model.type=torch model.path=nnsmith_output/model.pth
142+
```
143+
124144
## Misc
125145

126146
TensorFlow logging can be very noisy. Use `TF_CPP_MIN_LOG_LEVEL=3` as environmental variable to depress that.

nnsmith/cli/report_syn.py

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import os
2+
3+
import hydra
4+
from omegaconf import DictConfig, ListConfig
5+
6+
from nnsmith.backends import BackendFactory
7+
from nnsmith.logging import RENDER_LOG
8+
from nnsmith.materialize import Model, Render
9+
10+
11+
@hydra.main(version_base=None, config_path="../config", config_name="main")
12+
def main(cfg: DictConfig):
13+
RENDER_LOG.warning(
14+
"The duty of `nnsmith.report_syn` is to produce a BASIC but executable Python script. It may not reproduce the original bug as the report may not use the original seed, input data, and output oracles. If you want to more strictly reproduce the bug, please use `nnsmith.model_exec`."
15+
)
16+
17+
cmp_cfg = cfg["cmp"]
18+
model_cfg = cfg["model"]
19+
ModelType = Model.init(model_cfg["type"], cfg["backend"]["target"])
20+
21+
if isinstance(model_cfg["path"], ListConfig):
22+
model_paths = model_cfg["path"]
23+
else:
24+
model_paths = [model_cfg["path"]]
25+
26+
for model_path in model_paths:
27+
model = ModelType.load(model_path)
28+
29+
oracle_path = None
30+
# Check if we can directly use oracle from `oracle.pkl`
31+
if "auto" == cmp_cfg["oracle"]:
32+
model_basename = os.path.basename(os.path.normpath(model_path))
33+
oracle_path = model_path.replace(model_basename, "oracle.pkl")
34+
if not os.path.exists(oracle_path):
35+
oracle_path = None
36+
elif cmp_cfg["oracle"] is not None:
37+
oracle_path = cmp_cfg["oracle"]
38+
39+
if not os.path.exists(oracle_path):
40+
oracle_path = None
41+
42+
this_fac = BackendFactory.init(
43+
cfg["backend"]["type"],
44+
target=cfg["backend"]["target"],
45+
optmax=cfg["backend"]["optmax"],
46+
parse_name=True,
47+
)
48+
49+
render = Render()
50+
render.emit_model(model)
51+
render.emit_input(model, oracle_path)
52+
render.emit_backend(this_fac)
53+
54+
print("#", "-" * 20)
55+
print(f"# {model_path}")
56+
print(render.render())
57+
print("#", "-" * 20)
58+
59+
60+
if __name__ == "__main__":
61+
main()

nnsmith/logging.py

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
SMT_LOG = logging.getLogger("smt")
77
EXEC_LOG = logging.getLogger("exec")
88
DTEST_LOG = logging.getLogger("dtest")
9+
RENDER_LOG = logging.getLogger("render")
910
CORE_LOG = logging.getLogger("core")
1011

1112
TF_LOG = logging.getLogger("gen|tf")

nnsmith/materialize/torch/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def import_libs(self) -> List[str]:
190190

191191
def emit_input(self, inp_name: str, path: Optional[PathLike] = None):
192192
if path is not None: # Assume NumPy tensors as inputs
193-
return f"{inp_name} = [v for _, v in pickle.load(open('{path}', 'rb'))['input']]"
193+
return f"{inp_name} = [v for _, v in pickle.load(open('{path}', 'rb'))['input'].items()]"
194194

195195
# Path is None. Generate inputs from scratch.
196196
tensor_text = []

setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ console_scripts =
4242
nnsmith.model_exec = nnsmith.cli.model_exec:main
4343
nnsmith.dtype_test = nnsmith.cli.dtype_test:main
4444
nnsmith.fuzz = nnsmith.cli.fuzz:main
45+
nnsmith.report_syn = nnsmith.cli.report_syn:main

0 commit comments

Comments
 (0)