devtool-stress.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License version 2 as
  10. # published by the Free Software Foundation.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License along
  18. # with this program; if not, write to the Free Software Foundation, Inc.,
  19. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. #
  21. import sys
  22. import os
  23. import os.path
  24. import subprocess
  25. import re
  26. import argparse
  27. import logging
  28. import tempfile
  29. import shutil
  30. import signal
  31. import fnmatch
  32. scripts_lib_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'lib'))
  33. sys.path.insert(0, scripts_lib_path)
  34. import scriptutils
  35. import argparse_oe
  36. logger = scriptutils.logger_create('devtool-stress')
  37. def select_recipes(args):
  38. import bb.tinfoil
  39. tinfoil = bb.tinfoil.Tinfoil()
  40. tinfoil.prepare(False)
  41. pkg_pn = tinfoil.cooker.recipecache.pkg_pn
  42. (latest_versions, preferred_versions) = bb.providers.findProviders(tinfoil.config_data, tinfoil.cooker.recipecache, pkg_pn)
  43. skip_classes = args.skip_classes.split(',')
  44. recipelist = []
  45. for pn in sorted(pkg_pn):
  46. pref = preferred_versions[pn]
  47. inherits = [os.path.splitext(os.path.basename(f))[0] for f in tinfoil.cooker.recipecache.inherits[pref[1]]]
  48. for cls in skip_classes:
  49. if cls in inherits:
  50. break
  51. else:
  52. recipelist.append(pn)
  53. tinfoil.shutdown()
  54. resume_from = args.resume_from
  55. if resume_from:
  56. if not resume_from in recipelist:
  57. print('%s is not a testable recipe' % resume_from)
  58. return 1
  59. if args.only:
  60. only = args.only.split(',')
  61. for onlyitem in only:
  62. for pn in recipelist:
  63. if fnmatch.fnmatch(pn, onlyitem):
  64. break
  65. else:
  66. print('%s does not match any testable recipe' % onlyitem)
  67. return 1
  68. else:
  69. only = None
  70. if args.skip:
  71. skip = args.skip.split(',')
  72. else:
  73. skip = []
  74. recipes = []
  75. for pn in recipelist:
  76. if resume_from:
  77. if pn == resume_from:
  78. resume_from = None
  79. else:
  80. continue
  81. if args.only:
  82. for item in only:
  83. if fnmatch.fnmatch(pn, item):
  84. break
  85. else:
  86. continue
  87. skipit = False
  88. for item in skip:
  89. if fnmatch.fnmatch(pn, item):
  90. skipit = True
  91. if skipit:
  92. continue
  93. recipes.append(pn)
  94. return recipes
  95. def stress_extract(args):
  96. import bb.process
  97. recipes = select_recipes(args)
  98. failures = 0
  99. tmpdir = tempfile.mkdtemp()
  100. os.setpgrp()
  101. try:
  102. for pn in recipes:
  103. sys.stdout.write('Testing %s ' % (pn + ' ').ljust(40, '.'))
  104. sys.stdout.flush()
  105. failed = False
  106. srctree = os.path.join(tmpdir, pn)
  107. try:
  108. bb.process.run('devtool extract %s %s' % (pn, srctree))
  109. except bb.process.CmdError as exc:
  110. failed = True
  111. with open('stress_%s_extract.log' % pn, 'w') as f:
  112. f.write(str(exc))
  113. if os.path.exists(srctree):
  114. shutil.rmtree(srctree)
  115. if failed:
  116. print('failed')
  117. failures += 1
  118. else:
  119. print('ok')
  120. except KeyboardInterrupt:
  121. # We want any child processes killed. This is crude, but effective.
  122. os.killpg(0, signal.SIGTERM)
  123. if failures:
  124. return 1
  125. else:
  126. return 0
  127. def stress_modify(args):
  128. import bb.process
  129. recipes = select_recipes(args)
  130. failures = 0
  131. tmpdir = tempfile.mkdtemp()
  132. os.setpgrp()
  133. try:
  134. for pn in recipes:
  135. sys.stdout.write('Testing %s ' % (pn + ' ').ljust(40, '.'))
  136. sys.stdout.flush()
  137. failed = False
  138. reset = True
  139. srctree = os.path.join(tmpdir, pn)
  140. try:
  141. bb.process.run('devtool modify -x %s %s' % (pn, srctree))
  142. except bb.process.CmdError as exc:
  143. with open('stress_%s_modify.log' % pn, 'w') as f:
  144. f.write(str(exc))
  145. failed = 'modify'
  146. reset = False
  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. else:
  166. print('ok')
  167. except KeyboardInterrupt:
  168. # We want any child processes killed. This is crude, but effective.
  169. os.killpg(0, signal.SIGTERM)
  170. if failures:
  171. return 1
  172. else:
  173. return 0
  174. def main():
  175. parser = argparse_oe.ArgumentParser(description="devtool stress tester",
  176. epilog="Use %(prog)s <subcommand> --help to get help on a specific command")
  177. parser.add_argument('-d', '--debug', help='Enable debug output', action='store_true')
  178. parser.add_argument('-r', '--resume-from', help='Resume from specified recipe', metavar='PN')
  179. parser.add_argument('-o', '--only', help='Only test specified recipes (comma-separated without spaces, wildcards allowed)', metavar='PNLIST')
  180. parser.add_argument('-s', '--skip', help='Skip specified recipes (comma-separated without spaces, wildcards allowed)', metavar='PNLIST')
  181. 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')
  182. subparsers = parser.add_subparsers(title='subcommands', metavar='<subcommand>')
  183. parser_modify = subparsers.add_parser('modify',
  184. help='Run "devtool modify" followed by a build with bitbake on matching recipes',
  185. description='Runs "devtool modify" followed by a build with bitbake on matching recipes')
  186. parser_modify.set_defaults(func=stress_modify)
  187. parser_extract = subparsers.add_parser('extract',
  188. help='Run "devtool extract" on matching recipes',
  189. description='Runs "devtool extract" on matching recipes')
  190. parser_extract.set_defaults(func=stress_extract)
  191. args = parser.parse_args()
  192. if args.debug:
  193. logger.setLevel(logging.DEBUG)
  194. import scriptpath
  195. bitbakepath = scriptpath.add_bitbake_lib_path()
  196. if not bitbakepath:
  197. logger.error("Unable to find bitbake by searching parent directory of this script or PATH")
  198. return 1
  199. logger.debug('Found bitbake path: %s' % bitbakepath)
  200. ret = args.func(args)
  201. if __name__ == "__main__":
  202. main()