blob: d555c51a650b748ec483898c50d9314e75b5d6bc [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#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License version 2 as
11# published by the Free Software Foundation.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License along
19# with this program; if not, write to the Free Software Foundation, Inc.,
20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21#
22
23import sys
24import os
25import os.path
26import subprocess
27import re
28import argparse
29import logging
30import tempfile
31import shutil
32import signal
33import fnmatch
34
35scripts_lib_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'lib'))
36sys.path.insert(0, scripts_lib_path)
37import scriptutils
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050038import argparse_oe
Patrick Williamsf1e5d692016-03-30 15:21:19 -050039logger = scriptutils.logger_create('devtool-stress')
40
41def select_recipes(args):
42 import bb.tinfoil
43 tinfoil = bb.tinfoil.Tinfoil()
44 tinfoil.prepare(False)
45
Patrick Williamsc0f7c042017-02-23 20:41:17 -060046 pkg_pn = tinfoil.cooker.recipecaches[''].pkg_pn
47 (latest_versions, preferred_versions) = bb.providers.findProviders(tinfoil.config_data, tinfoil.cooker.recipecaches[''], pkg_pn)
Patrick Williamsf1e5d692016-03-30 15:21:19 -050048
49 skip_classes = args.skip_classes.split(',')
50
51 recipelist = []
52 for pn in sorted(pkg_pn):
53 pref = preferred_versions[pn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -060054 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 -050055 for cls in skip_classes:
56 if cls in inherits:
57 break
58 else:
59 recipelist.append(pn)
60
61 tinfoil.shutdown()
62
63 resume_from = args.resume_from
64 if resume_from:
65 if not resume_from in recipelist:
66 print('%s is not a testable recipe' % resume_from)
67 return 1
68 if args.only:
69 only = args.only.split(',')
70 for onlyitem in only:
71 for pn in recipelist:
72 if fnmatch.fnmatch(pn, onlyitem):
73 break
74 else:
75 print('%s does not match any testable recipe' % onlyitem)
76 return 1
77 else:
78 only = None
79 if args.skip:
80 skip = args.skip.split(',')
81 else:
82 skip = []
83
84 recipes = []
85 for pn in recipelist:
86 if resume_from:
87 if pn == resume_from:
88 resume_from = None
89 else:
90 continue
91
92 if args.only:
93 for item in only:
94 if fnmatch.fnmatch(pn, item):
95 break
96 else:
97 continue
98
99 skipit = False
100 for item in skip:
101 if fnmatch.fnmatch(pn, item):
102 skipit = True
103 if skipit:
104 continue
105
106 recipes.append(pn)
107
108 return recipes
109
110
111def stress_extract(args):
112 import bb.process
113
114 recipes = select_recipes(args)
115
116 failures = 0
117 tmpdir = tempfile.mkdtemp()
118 os.setpgrp()
119 try:
120 for pn in recipes:
121 sys.stdout.write('Testing %s ' % (pn + ' ').ljust(40, '.'))
122 sys.stdout.flush()
123 failed = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600124 skipped = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500125
126 srctree = os.path.join(tmpdir, pn)
127 try:
128 bb.process.run('devtool extract %s %s' % (pn, srctree))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600129 except bb.process.ExecutionError as exc:
130 if exc.exitcode == 4:
131 skipped = 'incompatible'
132 else:
133 failed = True
134 with open('stress_%s_extract.log' % pn, 'w') as f:
135 f.write(str(exc))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500136
137 if os.path.exists(srctree):
138 shutil.rmtree(srctree)
139
140 if failed:
141 print('failed')
142 failures += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600143 elif skipped:
144 print('skipped (%s)' % skipped)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500145 else:
146 print('ok')
147 except KeyboardInterrupt:
148 # We want any child processes killed. This is crude, but effective.
149 os.killpg(0, signal.SIGTERM)
150
151 if failures:
152 return 1
153 else:
154 return 0
155
156
157def stress_modify(args):
158 import bb.process
159
160 recipes = select_recipes(args)
161
162 failures = 0
163 tmpdir = tempfile.mkdtemp()
164 os.setpgrp()
165 try:
166 for pn in recipes:
167 sys.stdout.write('Testing %s ' % (pn + ' ').ljust(40, '.'))
168 sys.stdout.flush()
169 failed = False
170 reset = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600171 skipped = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500172
173 srctree = os.path.join(tmpdir, pn)
174 try:
175 bb.process.run('devtool modify -x %s %s' % (pn, srctree))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600176 except bb.process.ExecutionError as exc:
177 if exc.exitcode == 4:
178 skipped = 'incompatible'
179 else:
180 with open('stress_%s_modify.log' % pn, 'w') as f:
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500181 f.write(str(exc))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600182 failed = 'modify'
183 reset = False
184
185 if not skipped:
186 if not failed:
187 try:
188 bb.process.run('bitbake -c install %s' % pn)
189 except bb.process.CmdError as exc:
190 with open('stress_%s_install.log' % pn, 'w') as f:
191 f.write(str(exc))
192 failed = 'build'
193 if reset:
194 try:
195 bb.process.run('devtool reset %s' % pn)
196 except bb.process.CmdError as exc:
197 print('devtool reset failed: %s' % str(exc))
198 break
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500199
200 if os.path.exists(srctree):
201 shutil.rmtree(srctree)
202
203 if failed:
204 print('failed (%s)' % failed)
205 failures += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600206 elif skipped:
207 print('skipped (%s)' % skipped)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500208 else:
209 print('ok')
210 except KeyboardInterrupt:
211 # We want any child processes killed. This is crude, but effective.
212 os.killpg(0, signal.SIGTERM)
213
214 if failures:
215 return 1
216 else:
217 return 0
218
219
220def main():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500221 parser = argparse_oe.ArgumentParser(description="devtool stress tester",
222 epilog="Use %(prog)s <subcommand> --help to get help on a specific command")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500223 parser.add_argument('-d', '--debug', help='Enable debug output', action='store_true')
224 parser.add_argument('-r', '--resume-from', help='Resume from specified recipe', metavar='PN')
225 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 -0600226 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 -0500227 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')
228 subparsers = parser.add_subparsers(title='subcommands', metavar='<subcommand>')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600229 subparsers.required = True
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500230
231 parser_modify = subparsers.add_parser('modify',
232 help='Run "devtool modify" followed by a build with bitbake on matching recipes',
233 description='Runs "devtool modify" followed by a build with bitbake on matching recipes')
234 parser_modify.set_defaults(func=stress_modify)
235
236 parser_extract = subparsers.add_parser('extract',
237 help='Run "devtool extract" on matching recipes',
238 description='Runs "devtool extract" on matching recipes')
239 parser_extract.set_defaults(func=stress_extract)
240
241 args = parser.parse_args()
242
243 if args.debug:
244 logger.setLevel(logging.DEBUG)
245
246 import scriptpath
247 bitbakepath = scriptpath.add_bitbake_lib_path()
248 if not bitbakepath:
249 logger.error("Unable to find bitbake by searching parent directory of this script or PATH")
250 return 1
251 logger.debug('Found bitbake path: %s' % bitbakepath)
252
253 ret = args.func(args)
254
255if __name__ == "__main__":
256 main()