| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1 | #!/usr/bin/env python3 | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 2 |  | 
 | 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 |  | 
 | 23 | import sys | 
 | 24 | import os | 
 | 25 | import os.path | 
 | 26 | import subprocess | 
 | 27 | import re | 
 | 28 | import argparse | 
 | 29 | import logging | 
 | 30 | import tempfile | 
 | 31 | import shutil | 
 | 32 | import signal | 
 | 33 | import fnmatch | 
 | 34 |  | 
 | 35 | scripts_lib_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'lib')) | 
 | 36 | sys.path.insert(0, scripts_lib_path) | 
 | 37 | import scriptutils | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 38 | import argparse_oe | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 39 | logger = scriptutils.logger_create('devtool-stress') | 
 | 40 |  | 
 | 41 | def select_recipes(args): | 
 | 42 |     import bb.tinfoil | 
 | 43 |     tinfoil = bb.tinfoil.Tinfoil() | 
 | 44 |     tinfoil.prepare(False) | 
 | 45 |  | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 46 |     pkg_pn = tinfoil.cooker.recipecaches[''].pkg_pn | 
 | 47 |     (latest_versions, preferred_versions) = bb.providers.findProviders(tinfoil.config_data, tinfoil.cooker.recipecaches[''], pkg_pn) | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 48 |  | 
 | 49 |     skip_classes = args.skip_classes.split(',') | 
 | 50 |  | 
 | 51 |     recipelist = [] | 
 | 52 |     for pn in sorted(pkg_pn): | 
 | 53 |         pref = preferred_versions[pn] | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 54 |         inherits = [os.path.splitext(os.path.basename(f))[0] for f in tinfoil.cooker.recipecaches[''].inherits[pref[1]]] | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 55 |         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 |  | 
 | 111 | def 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 Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 124 |             skipped = None | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 125 |  | 
 | 126 |             srctree = os.path.join(tmpdir, pn) | 
 | 127 |             try: | 
 | 128 |                 bb.process.run('devtool extract %s %s' % (pn, srctree)) | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 129 |             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 Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 136 |  | 
 | 137 |             if os.path.exists(srctree): | 
 | 138 |                 shutil.rmtree(srctree) | 
 | 139 |  | 
 | 140 |             if failed: | 
 | 141 |                 print('failed') | 
 | 142 |                 failures += 1 | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 143 |             elif skipped: | 
 | 144 |                 print('skipped (%s)' % skipped) | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 145 |             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 |  | 
 | 157 | def 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 Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 171 |             skipped = None | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 172 |  | 
 | 173 |             srctree = os.path.join(tmpdir, pn) | 
 | 174 |             try: | 
 | 175 |                 bb.process.run('devtool modify -x %s %s' % (pn, srctree)) | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 176 |             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 Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 181 |                         f.write(str(exc)) | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 182 |                     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 Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 199 |  | 
 | 200 |             if os.path.exists(srctree): | 
 | 201 |                 shutil.rmtree(srctree) | 
 | 202 |  | 
 | 203 |             if failed: | 
 | 204 |                 print('failed (%s)' % failed) | 
 | 205 |                 failures += 1 | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 206 |             elif skipped: | 
 | 207 |                 print('skipped (%s)' % skipped) | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 208 |             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 |  | 
 | 220 | def main(): | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 221 |     parser = argparse_oe.ArgumentParser(description="devtool stress tester", | 
 | 222 |                                         epilog="Use %(prog)s <subcommand> --help to get help on a specific command") | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 223 |     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 Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 226 |     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 Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 227 |     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 Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 229 |     subparsers.required = True | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 230 |  | 
 | 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 |  | 
 | 255 | if __name__ == "__main__": | 
 | 256 |     main() |