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
|
#!/usr/bin/env python3
"""explore-kconfig - Obtain build attributes of configuration variants
explore-kconfig obtains build attributes such as ROM or RAM usage of
configuration variants for a given software project. It works on random
configurations (--random) or in the neighbourhood of existing configurations
(--neighbourhood).
Supported projects must be configurable via kconfig and provide a command which
outputs a JSON dict of build attributes on stdout. Use
--{clean,build,attribute}-command to configure explore-kconfig for a project.
explore-kconfig places the experiment results (containing configurations, build
logs, and correspondnig attributes) in the current working directory. Use
analyze-kconfig to build a model once data acquisition is complete.
"""
import argparse
import logging
import os
import sys
from dfatool import kconfig
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter, description=__doc__
)
parser.add_argument(
"--neighbourhood",
type=str,
help="Explore neighbourhood of provided .config file(s)",
)
parser.add_argument(
"--log-level",
default=logging.INFO,
type=lambda level: getattr(logging, level.upper()),
help="Set log level",
)
parser.add_argument(
"--enumerate",
action="store_true",
help="Enumerate all valid configurations (DEPRECATED and UNMAINTAINED)",
)
parser.add_argument(
"--random",
type=int,
help="Explore a number of random configurations (make randconfig)",
)
parser.add_argument(
"--random-int",
action="store_true",
help="Randomize integers after running make randconfig",
)
parser.add_argument(
"--with-neighbourhood",
action="store_true",
help="Explore neighbourhood of successful random configurations",
)
parser.add_argument(
"--repeatable",
action="store_true",
help="Allow repeated measurements of already benchmarked configurations",
)
parser.add_argument(
"--repeat",
type=int,
metavar="N",
help="Run each benchmark N times. Implies --repeatable",
)
parser.add_argument(
"--clean-command", type=str, help="Clean command", default="make clean"
)
parser.add_argument(
"--build-command", type=str, help="Build command", default="make"
)
parser.add_argument(
"--attribute-command",
type=str,
help="Attribute extraction command",
default="make nfpvalues",
)
parser.add_argument(
"--randconfig-command",
type=str,
help="Randconfig command for --random",
default="make randconfig",
)
parser.add_argument(
"--kconfig-file", type=str, help="Kconfig file", default="Kconfig"
)
parser.add_argument("project_root", type=str, help="Project root directory")
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)
kconf = kconfig.KConfig(args.project_root)
if args.clean_command:
kconf.clean_command = args.clean_command
if args.build_command:
kconf.build_command = args.build_command
if args.attribute_command:
kconf.attribute_command = args.attribute_command
if args.randconfig_command:
kconf.randconfig_command = args.randconfig_command
if args.kconfig_file:
kconf.kconfig = args.kconfig_file
if args.repeatable:
kconf.repeatable = args.repeatable
if args.repeat:
kconf.repeat = args.repeat - 1
kconf.repeatable = True
kconf.run_nfpkeys()
if args.enumerate:
kconf.enumerate()
if args.random:
num_successful = 0
# Assumption: At least 1% of builds are successful
for i in range(args.random * 100):
logging.info(f"Running randconfig {num_successful+1} of {args.random}")
status = kconf.run_randconfig(with_random_int=args.random_int)
if status["success"]:
num_successful += 1
if args.with_neighbourhood and status["success"]:
config_filename = status["config_path"]
logging.info(f"Exploring neighbourhood of {config_filename}")
kconf.run_exploration_from_file(
config_filename, with_initial_config=False
)
if num_successful == args.random:
break
if args.neighbourhood:
if os.path.isfile(args.neighbourhood):
kconf.run_exploration_from_file(args.neighbourhood)
elif os.path.isdir(args.neighbourhood):
for filename in os.listdir(args.neighbourhood):
config_filename = f"{args.neighbourhood}/{filename}"
logging.info(f"Exploring neighbourhood of {config_filename}")
kconf.run_exploration_from_file(config_filename)
else:
print(
f"--neighbourhod: Error: {args.neighbourhood} must be a file or directory, but is neither",
file=sys.stderr,
)
if __name__ == "__main__":
main()
|