blob: 81046ecf49f697a97a65e091fff00c08c4cfd784 [file] [log] [blame]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001#!/usr/bin/env python3
Patrick Williamsf1e5d692016-03-30 15:21:19 -05002
3# devtool stress tester
4#
5# Written by: Paul Eggleton <paul.eggleton@linux.intel.com>
6#
7# Copyright 2015 Intel Corporation
8#
Brad Bishopc342db32019-05-15 21:57:59 -04009# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsf1e5d692016-03-30 15:21:19 -050010#
11
12import sys
13import os
14import os.path
15import subprocess
16import re
17import argparse
18import logging
19import tempfile
20import shutil
21import signal
22import fnmatch
23
24scripts_lib_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'lib'))
25sys.path.insert(0, scripts_lib_path)
26import scriptutils
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050027import argparse_oe
Patrick Williamsf1e5d692016-03-30 15:21:19 -050028logger = scriptutils.logger_create('devtool-stress')
29
30def select_recipes(args):
31 import bb.tinfoil
32 tinfoil = bb.tinfoil.Tinfoil()
33 tinfoil.prepare(False)
34
Patrick Williamsc0f7c042017-02-23 20:41:17 -060035 pkg_pn = tinfoil.cooker.recipecaches[''].pkg_pn
36 (latest_versions, preferred_versions) = bb.providers.findProviders(tinfoil.config_data, tinfoil.cooker.recipecaches[''], pkg_pn)
Patrick Williamsf1e5d692016-03-30 15:21:19 -050037
38 skip_classes = args.skip_classes.split(',')
39
40 recipelist = []
41 for pn in sorted(pkg_pn):
42 pref = preferred_versions[pn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -060043 inherits = [os.path.splitext(os.path.basename(f))[0] for f in tinfoil.cooker.recipecaches[''].inherits[pref[1]]]
Patrick Williamsf1e5d692016-03-30 15:21:19 -050044 for cls in skip_classes:
45 if cls in inherits:
46 break
47 else:
48 recipelist.append(pn)
49
50 tinfoil.shutdown()
51
52 resume_from = args.resume_from
53 if resume_from:
54 if not resume_from in recipelist:
55 print('%s is not a testable recipe' % resume_from)
56 return 1
57 if args.only:
58 only = args.only.split(',')
59 for onlyitem in only:
60 for pn in recipelist:
61 if fnmatch.fnmatch(pn, onlyitem):
62 break
63 else:
64 print('%s does not match any testable recipe' % onlyitem)
65 return 1
66 else:
67 only = None
68 if args.skip:
69 skip = args.skip.split(',')
70 else:
71 skip = []
72
73 recipes = []
74 for pn in recipelist:
75 if resume_from:
76 if pn == resume_from:
77 resume_from = None
78 else:
79 continue
80
81 if args.only:
82 for item in only:
83 if fnmatch.fnmatch(pn, item):
84 break
85 else:
86 continue
87
88 skipit = False
89 for item in skip:
90 if fnmatch.fnmatch(pn, item):
91 skipit = True
92 if skipit:
93 continue
94
95 recipes.append(pn)
96
97 return recipes
98
99
100def stress_extract(args):
101 import bb.process
102
103 recipes = select_recipes(args)
104
105 failures = 0
106 tmpdir = tempfile.mkdtemp()
107 os.setpgrp()
108 try:
109 for pn in recipes:
110 sys.stdout.write('Testing %s ' % (pn + ' ').ljust(40, '.'))
111 sys.stdout.flush()
112 failed = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600113 skipped = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500114
115 srctree = os.path.join(tmpdir, pn)
116 try:
117 bb.process.run('devtool extract %s %s' % (pn, srctree))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600118 except bb.process.ExecutionError as exc:
119 if exc.exitcode == 4:
120 skipped = 'incompatible'
121 else:
122 failed = True
123 with open('stress_%s_extract.log' % pn, 'w') as f:
124 f.write(str(exc))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500125
126 if os.path.exists(srctree):
127 shutil.rmtree(srctree)
128
129 if failed:
130 print('failed')
131 failures += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600132 elif skipped:
133 print('skipped (%s)' % skipped)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500134 else:
135 print('ok')
136 except KeyboardInterrupt:
137 # We want any child processes killed. This is crude, but effective.
138 os.killpg(0, signal.SIGTERM)
139
140 if failures:
141 return 1
142 else:
143 return 0
144
145
146def stress_modify(args):
147 import bb.process
148
149 recipes = select_recipes(args)
150
151 failures = 0
152 tmpdir = tempfile.mkdtemp()
153 os.setpgrp()
154 try:
155 for pn in recipes:
156 sys.stdout.write('Testing %s ' % (pn + ' ').ljust(40, '.'))
157 sys.stdout.flush()
158 failed = False
159 reset = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600160 skipped = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500161
162 srctree = os.path.join(tmpdir, pn)
163 try:
164 bb.process.run('devtool modify -x %s %s' % (pn, srctree))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600165 except bb.process.ExecutionError as exc:
166 if exc.exitcode == 4:
167 skipped = 'incompatible'
168 else:
169 with open('stress_%s_modify.log' % pn, 'w') as f:
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500170 f.write(str(exc))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600171 failed = 'modify'
172 reset = False
173
174 if not skipped:
175 if not failed:
176 try:
177 bb.process.run('bitbake -c install %s' % pn)
178 except bb.process.CmdError as exc:
179 with open('stress_%s_install.log' % pn, 'w') as f:
180 f.write(str(exc))
181 failed = 'build'
182 if reset:
183 try:
184 bb.process.run('devtool reset %s' % pn)
185 except bb.process.CmdError as exc:
186 print('devtool reset failed: %s' % str(exc))
187 break
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500188
189 if os.path.exists(srctree):
190 shutil.rmtree(srctree)
191
192 if failed:
193 print('failed (%s)' % failed)
194 failures += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600195 elif skipped:
196 print('skipped (%s)' % skipped)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500197 else:
198 print('ok')
199 except KeyboardInterrupt:
200 # We want any child processes killed. This is crude, but effective.
201 os.killpg(0, signal.SIGTERM)
202
203 if failures:
204 return 1
205 else:
206 return 0
207
208
209def main():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500210 parser = argparse_oe.ArgumentParser(description="devtool stress tester",
211 epilog="Use %(prog)s <subcommand> --help to get help on a specific command")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500212 parser.add_argument('-d', '--debug', help='Enable debug output', action='store_true')
213 parser.add_argument('-r', '--resume-from', help='Resume from specified recipe', metavar='PN')
214 parser.add_argument('-o', '--only', help='Only test specified recipes (comma-separated without spaces, wildcards allowed)', metavar='PNLIST')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600215 parser.add_argument('-s', '--skip', help='Skip specified recipes (comma-separated without spaces, wildcards allowed)', metavar='PNLIST', default='gcc-source-*,kernel-devsrc,package-index,perf,meta-world-pkgdata,glibc-locale,glibc-mtrace,glibc-scripts,os-release')
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500216 parser.add_argument('-c', '--skip-classes', help='Skip recipes inheriting specified classes (comma-separated) - default %(default)s', metavar='CLASSLIST', default='native,nativesdk,cross,cross-canadian,image,populate_sdk,meta,packagegroup')
217 subparsers = parser.add_subparsers(title='subcommands', metavar='<subcommand>')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600218 subparsers.required = True
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500219
220 parser_modify = subparsers.add_parser('modify',
221 help='Run "devtool modify" followed by a build with bitbake on matching recipes',
222 description='Runs "devtool modify" followed by a build with bitbake on matching recipes')
223 parser_modify.set_defaults(func=stress_modify)
224
225 parser_extract = subparsers.add_parser('extract',
226 help='Run "devtool extract" on matching recipes',
227 description='Runs "devtool extract" on matching recipes')
228 parser_extract.set_defaults(func=stress_extract)
229
230 args = parser.parse_args()
231
232 if args.debug:
233 logger.setLevel(logging.DEBUG)
234
235 import scriptpath
236 bitbakepath = scriptpath.add_bitbake_lib_path()
237 if not bitbakepath:
238 logger.error("Unable to find bitbake by searching parent directory of this script or PATH")
239 return 1
240 logger.debug('Found bitbake path: %s' % bitbakepath)
241
242 ret = args.func(args)
243
244if __name__ == "__main__":
245 main()