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
|
#!/usr/bin/env python3
"""analyze-kconfig - Generate a model for KConfig selections
analyze-kconfig builds a model determining system attributes
(e.g. ROM or RAM usage) based on KConfig configuration variables.
Only boolean variables are supported at the moment.
"""
import argparse
import json
import kconfiglib
import logging
import os
from dfatool.loader import KConfigAttributes
from dfatool.model import KConfigModel
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter, description=__doc__
)
parser.add_argument(
"--export-tree",
type=str,
help="Export decision tree model to file",
metavar="FILE",
)
parser.add_argument(
"--config",
type=str,
help="Show model results for symbols in .config file",
metavar="FILE",
)
parser.add_argument(
"--attribute", choices=["rom", "ram"], default="rom", help="Model attribute"
)
parser.add_argument(
"--max-loss",
type=float,
help="Maximum acceptable model loss for DecisionTree Leaves",
default=10,
)
parser.add_argument(
"--log-level",
default=logging.INFO,
type=lambda level: getattr(logging, level.upper()),
help="Set log level",
)
parser.add_argument("kconfig_path", type=str, help="Path to Kconfig file")
parser.add_argument(
"model",
type=str,
help="Path to experiment results directory or model.json file",
)
args = parser.parse_args()
if isinstance(args.log_level, int):
logging.basicConfig(level=args.log_level)
else:
print(f"Invalid log level. Setting log level to INFO.", file=sys.stderr)
if os.path.isdir(args.model):
data = KConfigAttributes(args.kconfig_path, args.model)
model = KConfigModel.from_benchmark(data, args.attribute)
if args.max_loss:
model.max_loss = args.max_loss
model.build_tree()
else:
with open(args.model, "r") as f:
model = KConfigModel.from_json(json.load(f))
if args.export_tree:
with open(args.export_tree, "w") as f:
json.dump(model.to_json(), f)
if args.config:
kconf = kconfiglib.Kconfig(args.kconfig_path)
kconf.load_config(args.config)
print(f"Model result for .config: {model.value_for_config(kconf)}")
for symbol in model.symbols:
kconf2 = kconfiglib.Kconfig(args.kconfig_path)
kconf2.load_config(args.config)
kconf_sym = kconf2.syms[symbol]
if kconf_sym.tri_value == 0 and 2 in kconf_sym.assignable:
kconf_sym.set_value(2)
elif kconf_sym.tri_value == 2 and 0 in kconf_sym.assignable:
kconf_sym.set_value(0)
else:
continue
# specific to multipass:
# Do not suggest changes which affect the application
skip = False
num_changes = 0
changed_symbols = list()
for i, csymbol in enumerate(model.symbols):
if kconf.syms[csymbol].tri_value != kconf2.syms[csymbol].tri_value:
num_changes += 1
changed_symbols.append(csymbol)
if (
csymbol.startswith("app_")
and kconf.syms[csymbol].tri_value
!= kconf2.syms[csymbol].tri_value
):
skip = True
break
if skip:
continue
try:
model_diff = model.value_for_config(kconf2) - model.value_for_config(
kconf
)
if kconf_sym.choice:
print(
f"Setting {kconf_sym.choice.name} to {kconf_sym.name} changes {num_changes:2d} symbols, model change: {model_diff:+5.0f}"
)
else:
print(
f"Setting {symbol} to {kconf_sym.str_value} changes {num_changes:2d} symbols, model change: {model_diff:+5.0f}"
)
except TypeError:
if kconf_sym.choice:
print(
f"Setting {kconf_sym.choice.name} to {kconf_sym.name} changes {num_changes:2d} symbols, model is undefined"
)
else:
print(
f"Setting {symbol} to {kconf_sym.str_value} changes {num_changes:2d} symbols, model is undefined"
)
for changed_symbol in changed_symbols:
print(
f" {changed_symbol:30s} -> {kconf2.syms[changed_symbol].str_value}"
)
if __name__ == "__main__":
main()
|