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