devtool-stress.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #!/usr/bin/env python3
  2. # devtool stress tester
  3. #
  4. # Written by: Paul Eggleton <paul.eggleton@linux.intel.com>
  5. #
  6. # Copyright 2015 Intel Corporation
  7. #
  8. # SPDX-License-Identifier: GPL-2.0-only
  9. #
  10. import sys
  11. import os
  12. import os.path
  13. import subprocess
  14. import re
  15. import argparse
  16. import logging
  17. import tempfile
  18. import shutil
  19. import signal
  20. import fnmatch
  21. scripts_lib_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'lib'))
  22. sys.path.insert(0, scripts_lib_path)
  23. import scriptutils
  24. import argparse_oe
  25. logger = scriptutils.logger_create('devtool-stress')
  26. def select_recipes(args):
  27. import bb.tinfoil
  28. tinfoil = bb.tinfoil.Tinfoil()
  29. tinfoil.prepare(False)
  30. pkg_pn = tinfoil.cooker.recipecaches[''].pkg_pn
  31. (latest_versions, preferred_versions) = bb.providers.findProviders(tinfoil.config_data, tinfoil.cooker.recipecaches[''], pkg_pn)
  32. skip_classes = args.skip_classes.split(',')
  33. recipelist = []
  34. for pn in sorted(pkg_pn):
  35. pref = preferred_versions[pn]
  36. inherits = [os.path.splitext(os.path.basename(f))[0] for f in tinfoil.cooker.recipecaches[''].inherits[pref[1]]]
  37. for cls in skip_classes:
  38. if cls in inherits:
  39. break
  40. else:
  41. recipelist.append(pn)
  42. tinfoil.shutdown()
  43. resume_from = args.resume_from
  44. if resume_from:
  45. if not resume_from in recipelist:
  46. print('%s is not a testable recipe' % resume_from)
  47. return 1
  48. if args.only:
  49. only = args.only.split(',')
  50. for onlyitem in only:
  51. for pn in recipelist:
  52. if fnmatch.fnmatch(pn, onlyitem):
  53. break
  54. else:
  55. print('%s does not match any testable recipe' % onlyitem)
  56. return 1
  57. else:
  58. only = None
  59. if args.skip:
  60. skip = args.skip.split(',')
  61. else:
  62. skip = []
  63. recipes = []
  64. for pn in recipelist:
  65. if resume_from:
  66. if pn == resume_from:
  67. resume_from = None
  68. else:
  69. continue
  70. if args.only:
  71. for item in only:
  72. if fnmatch.fnmatch(pn, item):
  73. break
  74. else:
  75. continue
  76. skipit = False
  77. for item in skip:
  78. if fnmatch.fnmatch(pn, item):
  79. skipit = True
  80. if skipit:
  81. continue
  82. recipes.append(pn)
  83. return recipes
  84. def stress_extract(args):
  85. import bb.process
  86. recipes = select_recipes(args)
  87. failures = 0
  88. tmpdir = tempfile.mkdtemp()
  89. os.setpgrp()
  90. try:
  91. for pn in recipes:
  92. sys.stdout.write('Testing %s ' % (pn + ' ').ljust(40, '.'))
  93. sys.stdout.flush()
  94. failed = False
  95. skipped = None
  96. srctree = os.path.join(tmpdir, pn)
  97. try:
  98. bb.process.run('devtool extract %s %s' % (pn, srctree))
  99. except bb.process.ExecutionError as exc:
  100. if exc.exitcode == 4:
  101. skipped = 'incompatible'
  102. else:
  103. failed = True
  104. with open('stress_%s_extract.log' % pn, 'w') as f:
  105. f.write(str(exc))
  106. if os.path.exists(srctree):
  107. shutil.rmtree(srctree)
  108. if failed:
  109. print('failed')
  110. failures += 1
  111. elif skipped:
  112. print('skipped (%s)' % skipped)
  113. else:
  114. print('ok')
  115. except KeyboardInterrupt:
  116. # We want any child processes killed. This is crude, but effective.
  117. os.killpg(0, signal.SIGTERM)
  118. if failures:
  119. return 1
  120. else:
  121. return 0
  122. def stress_modify(args):
  123. import bb.process
  124. recipes = select_recipes(args)
  125. failures = 0
  126. tmpdir = tempfile.mkdtemp()
  127. os.setpgrp()
  128. try:
  129. for pn in recipes:
  130. sys.stdout.write('Testing %s ' % (pn + ' ').ljust(40, '.'))
  131. sys.stdout.flush()
  132. failed = False
  133. reset = True
  134. skipped = None
  135. srctree = os.path.join(tmpdir, pn)
  136. try:
  137. bb.process.run('devtool modify -x %s %s' % (pn, srctree))
  138. except bb.process.ExecutionError as exc:
  139. if exc.exitcode == 4:
  140. skipped = 'incompatible'
  141. else:
  142. with open('stress_%s_modify.log' % pn, 'w') as f:
  143. f.write(str(exc))
  144. failed = 'modify'
  145. reset = False
  146. if not skipped:
  147. if not failed:
  148. try:
  149. bb.process.run('bitbake -c install %s' % pn)
  150. except bb.process.CmdError as exc:
  151. with open('stress_%s_install.log' % pn, 'w') as f:
  152. f.write(str(exc))
  153. failed = 'build'
  154. if reset:
  155. try:
  156. bb.process.run('devtool reset %s' % pn)
  157. except bb.process.CmdError as exc:
  158. print('devtool reset failed: %s' % str(exc))
  159. break
  160. if os.path.exists(srctree):
  161. shutil.rmtree(srctree)
  162. if failed:
  163. print('failed (%s)' % failed)
  164. failures += 1
  165. elif skipped:
  166. print('skipped (%s)' % skipped)
  167. else:
  168. print('ok')
  169. except KeyboardInterrupt:
  170. # We want any child processes killed. This is crude, but effective.
  171. os.killpg(0, signal.SIGTERM)
  172. if failures:
  173. return 1
  174. else:
  175. return 0
  176. def main():
  177. parser = argparse_oe.ArgumentParser(description="devtool stress tester",
  178. epilog="Use %(prog)s <subcommand> --help to get help on a specific command")
  179. parser.add_argument('-d', '--debug', help='Enable debug output', action='store_true')
  180. parser.add_argument('-r', '--resume-from', help='Resume from specified recipe', metavar='PN')
  181. parser.add_argument('-o', '--only', help='Only test specified recipes (comma-separated without spaces, wildcards allowed)', metavar='PNLIST')
  182. 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')
  183. 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')
  184. subparsers = parser.add_subparsers(title='subcommands', metavar='<subcommand>')
  185. subparsers.required = True
  186. parser_modify = subparsers.add_parser('modify',
  187. help='Run "devtool modify" followed by a build with bitbake on matching recipes',
  188. description='Runs "devtool modify" followed by a build with bitbake on matching recipes')
  189. parser_modify.set_defaults(func=stress_modify)
  190. parser_extract = subparsers.add_parser('extract',
  191. help='Run "devtool extract" on matching recipes',
  192. description='Runs "devtool extract" on matching recipes')
  193. parser_extract.set_defaults(func=stress_extract)
  194. args = parser.parse_args()
  195. if args.debug:
  196. logger.setLevel(logging.DEBUG)
  197. import scriptpath
  198. bitbakepath = scriptpath.add_bitbake_lib_path()
  199. if not bitbakepath:
  200. logger.error("Unable to find bitbake by searching parent directory of this script or PATH")
  201. return 1
  202. logger.debug('Found bitbake path: %s' % bitbakepath)
  203. ret = args.func(args)
  204. if __name__ == "__main__":
  205. main()