xref: /OK3568_Linux_fs/kernel/tools/perf/tests/attr.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun
3*4882a593Smuzhiyunfrom __future__ import print_function
4*4882a593Smuzhiyun
5*4882a593Smuzhiyunimport os
6*4882a593Smuzhiyunimport sys
7*4882a593Smuzhiyunimport glob
8*4882a593Smuzhiyunimport optparse
9*4882a593Smuzhiyunimport tempfile
10*4882a593Smuzhiyunimport logging
11*4882a593Smuzhiyunimport shutil
12*4882a593Smuzhiyun
13*4882a593Smuzhiyuntry:
14*4882a593Smuzhiyun    import configparser
15*4882a593Smuzhiyunexcept ImportError:
16*4882a593Smuzhiyun    import ConfigParser as configparser
17*4882a593Smuzhiyun
18*4882a593Smuzhiyundef data_equal(a, b):
19*4882a593Smuzhiyun    # Allow multiple values in assignment separated by '|'
20*4882a593Smuzhiyun    a_list = a.split('|')
21*4882a593Smuzhiyun    b_list = b.split('|')
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun    for a_item in a_list:
24*4882a593Smuzhiyun        for b_item in b_list:
25*4882a593Smuzhiyun            if (a_item == b_item):
26*4882a593Smuzhiyun                return True
27*4882a593Smuzhiyun            elif (a_item == '*') or (b_item == '*'):
28*4882a593Smuzhiyun                return True
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun    return False
31*4882a593Smuzhiyun
32*4882a593Smuzhiyunclass Fail(Exception):
33*4882a593Smuzhiyun    def __init__(self, test, msg):
34*4882a593Smuzhiyun        self.msg = msg
35*4882a593Smuzhiyun        self.test = test
36*4882a593Smuzhiyun    def getMsg(self):
37*4882a593Smuzhiyun        return '\'%s\' - %s' % (self.test.path, self.msg)
38*4882a593Smuzhiyun
39*4882a593Smuzhiyunclass Notest(Exception):
40*4882a593Smuzhiyun    def __init__(self, test, arch):
41*4882a593Smuzhiyun        self.arch = arch
42*4882a593Smuzhiyun        self.test = test
43*4882a593Smuzhiyun    def getMsg(self):
44*4882a593Smuzhiyun        return '[%s] \'%s\'' % (self.arch, self.test.path)
45*4882a593Smuzhiyun
46*4882a593Smuzhiyunclass Unsup(Exception):
47*4882a593Smuzhiyun    def __init__(self, test):
48*4882a593Smuzhiyun        self.test = test
49*4882a593Smuzhiyun    def getMsg(self):
50*4882a593Smuzhiyun        return '\'%s\'' % self.test.path
51*4882a593Smuzhiyun
52*4882a593Smuzhiyunclass Event(dict):
53*4882a593Smuzhiyun    terms = [
54*4882a593Smuzhiyun        'cpu',
55*4882a593Smuzhiyun        'flags',
56*4882a593Smuzhiyun        'type',
57*4882a593Smuzhiyun        'size',
58*4882a593Smuzhiyun        'config',
59*4882a593Smuzhiyun        'sample_period',
60*4882a593Smuzhiyun        'sample_type',
61*4882a593Smuzhiyun        'read_format',
62*4882a593Smuzhiyun        'disabled',
63*4882a593Smuzhiyun        'inherit',
64*4882a593Smuzhiyun        'pinned',
65*4882a593Smuzhiyun        'exclusive',
66*4882a593Smuzhiyun        'exclude_user',
67*4882a593Smuzhiyun        'exclude_kernel',
68*4882a593Smuzhiyun        'exclude_hv',
69*4882a593Smuzhiyun        'exclude_idle',
70*4882a593Smuzhiyun        'mmap',
71*4882a593Smuzhiyun        'comm',
72*4882a593Smuzhiyun        'freq',
73*4882a593Smuzhiyun        'inherit_stat',
74*4882a593Smuzhiyun        'enable_on_exec',
75*4882a593Smuzhiyun        'task',
76*4882a593Smuzhiyun        'watermark',
77*4882a593Smuzhiyun        'precise_ip',
78*4882a593Smuzhiyun        'mmap_data',
79*4882a593Smuzhiyun        'sample_id_all',
80*4882a593Smuzhiyun        'exclude_host',
81*4882a593Smuzhiyun        'exclude_guest',
82*4882a593Smuzhiyun        'exclude_callchain_kernel',
83*4882a593Smuzhiyun        'exclude_callchain_user',
84*4882a593Smuzhiyun        'wakeup_events',
85*4882a593Smuzhiyun        'bp_type',
86*4882a593Smuzhiyun        'config1',
87*4882a593Smuzhiyun        'config2',
88*4882a593Smuzhiyun        'branch_sample_type',
89*4882a593Smuzhiyun        'sample_regs_user',
90*4882a593Smuzhiyun        'sample_stack_user',
91*4882a593Smuzhiyun    ]
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun    def add(self, data):
94*4882a593Smuzhiyun        for key, val in data:
95*4882a593Smuzhiyun            log.debug("      %s = %s" % (key, val))
96*4882a593Smuzhiyun            self[key] = val
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun    def __init__(self, name, data, base):
99*4882a593Smuzhiyun        log.debug("    Event %s" % name);
100*4882a593Smuzhiyun        self.name  = name;
101*4882a593Smuzhiyun        self.group = ''
102*4882a593Smuzhiyun        self.add(base)
103*4882a593Smuzhiyun        self.add(data)
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun    def equal(self, other):
106*4882a593Smuzhiyun        for t in Event.terms:
107*4882a593Smuzhiyun            log.debug("      [%s] %s %s" % (t, self[t], other[t]));
108*4882a593Smuzhiyun            if t not in self or t not in other:
109*4882a593Smuzhiyun                return False
110*4882a593Smuzhiyun            if not data_equal(self[t], other[t]):
111*4882a593Smuzhiyun                return False
112*4882a593Smuzhiyun        return True
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun    def optional(self):
115*4882a593Smuzhiyun        if 'optional' in self and self['optional'] == '1':
116*4882a593Smuzhiyun            return True
117*4882a593Smuzhiyun        return False
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun    def diff(self, other):
120*4882a593Smuzhiyun        for t in Event.terms:
121*4882a593Smuzhiyun            if t not in self or t not in other:
122*4882a593Smuzhiyun                continue
123*4882a593Smuzhiyun            if not data_equal(self[t], other[t]):
124*4882a593Smuzhiyun                log.warning("expected %s=%s, got %s" % (t, self[t], other[t]))
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun# Test file description needs to have following sections:
127*4882a593Smuzhiyun# [config]
128*4882a593Smuzhiyun#   - just single instance in file
129*4882a593Smuzhiyun#   - needs to specify:
130*4882a593Smuzhiyun#     'command' - perf command name
131*4882a593Smuzhiyun#     'args'    - special command arguments
132*4882a593Smuzhiyun#     'ret'     - expected command return value (0 by default)
133*4882a593Smuzhiyun#     'arch'    - architecture specific test (optional)
134*4882a593Smuzhiyun#                 comma separated list, ! at the beginning
135*4882a593Smuzhiyun#                 negates it.
136*4882a593Smuzhiyun#
137*4882a593Smuzhiyun# [eventX:base]
138*4882a593Smuzhiyun#   - one or multiple instances in file
139*4882a593Smuzhiyun#   - expected values assignments
140*4882a593Smuzhiyunclass Test(object):
141*4882a593Smuzhiyun    def __init__(self, path, options):
142*4882a593Smuzhiyun        parser = configparser.SafeConfigParser()
143*4882a593Smuzhiyun        parser.read(path)
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun        log.warning("running '%s'" % path)
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun        self.path     = path
148*4882a593Smuzhiyun        self.test_dir = options.test_dir
149*4882a593Smuzhiyun        self.perf     = options.perf
150*4882a593Smuzhiyun        self.command  = parser.get('config', 'command')
151*4882a593Smuzhiyun        self.args     = parser.get('config', 'args')
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun        try:
154*4882a593Smuzhiyun            self.ret  = parser.get('config', 'ret')
155*4882a593Smuzhiyun        except:
156*4882a593Smuzhiyun            self.ret  = 0
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun        try:
159*4882a593Smuzhiyun            self.arch  = parser.get('config', 'arch')
160*4882a593Smuzhiyun            log.warning("test limitation '%s'" % self.arch)
161*4882a593Smuzhiyun        except:
162*4882a593Smuzhiyun            self.arch  = ''
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun        self.expect   = {}
165*4882a593Smuzhiyun        self.result   = {}
166*4882a593Smuzhiyun        log.debug("  loading expected events");
167*4882a593Smuzhiyun        self.load_events(path, self.expect)
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun    def is_event(self, name):
170*4882a593Smuzhiyun        if name.find("event") == -1:
171*4882a593Smuzhiyun            return False
172*4882a593Smuzhiyun        else:
173*4882a593Smuzhiyun            return True
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun    def skip_test(self, myarch):
176*4882a593Smuzhiyun        # If architecture not set always run test
177*4882a593Smuzhiyun        if self.arch == '':
178*4882a593Smuzhiyun            # log.warning("test for arch %s is ok" % myarch)
179*4882a593Smuzhiyun            return False
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun        # Allow multiple values in assignment separated by ','
182*4882a593Smuzhiyun        arch_list = self.arch.split(',')
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun        # Handle negated list such as !s390x,ppc
185*4882a593Smuzhiyun        if arch_list[0][0] == '!':
186*4882a593Smuzhiyun            arch_list[0] = arch_list[0][1:]
187*4882a593Smuzhiyun            log.warning("excluded architecture list %s" % arch_list)
188*4882a593Smuzhiyun            for arch_item in arch_list:
189*4882a593Smuzhiyun                # log.warning("test for %s arch is %s" % (arch_item, myarch))
190*4882a593Smuzhiyun                if arch_item == myarch:
191*4882a593Smuzhiyun                    return True
192*4882a593Smuzhiyun            return False
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun        for arch_item in arch_list:
195*4882a593Smuzhiyun            # log.warning("test for architecture '%s' current '%s'" % (arch_item, myarch))
196*4882a593Smuzhiyun            if arch_item == myarch:
197*4882a593Smuzhiyun                return False
198*4882a593Smuzhiyun        return True
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun    def load_events(self, path, events):
201*4882a593Smuzhiyun        parser_event = configparser.SafeConfigParser()
202*4882a593Smuzhiyun        parser_event.read(path)
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun        # The event record section header contains 'event' word,
205*4882a593Smuzhiyun        # optionaly followed by ':' allowing to load 'parent
206*4882a593Smuzhiyun        # event' first as a base
207*4882a593Smuzhiyun        for section in filter(self.is_event, parser_event.sections()):
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun            parser_items = parser_event.items(section);
210*4882a593Smuzhiyun            base_items   = {}
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun            # Read parent event if there's any
213*4882a593Smuzhiyun            if (':' in section):
214*4882a593Smuzhiyun                base = section[section.index(':') + 1:]
215*4882a593Smuzhiyun                parser_base = configparser.SafeConfigParser()
216*4882a593Smuzhiyun                parser_base.read(self.test_dir + '/' + base)
217*4882a593Smuzhiyun                base_items = parser_base.items('event')
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun            e = Event(section, parser_items, base_items)
220*4882a593Smuzhiyun            events[section] = e
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun    def run_cmd(self, tempdir):
223*4882a593Smuzhiyun        junk1, junk2, junk3, junk4, myarch = (os.uname())
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun        if self.skip_test(myarch):
226*4882a593Smuzhiyun            raise Notest(self, myarch)
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun        cmd = "PERF_TEST_ATTR=%s %s %s -o %s/perf.data %s" % (tempdir,
229*4882a593Smuzhiyun              self.perf, self.command, tempdir, self.args)
230*4882a593Smuzhiyun        ret = os.WEXITSTATUS(os.system(cmd))
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun        log.info("  '%s' ret '%s', expected '%s'" % (cmd, str(ret), str(self.ret)))
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun        if not data_equal(str(ret), str(self.ret)):
235*4882a593Smuzhiyun            raise Unsup(self)
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun    def compare(self, expect, result):
238*4882a593Smuzhiyun        match = {}
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun        log.debug("  compare");
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun        # For each expected event find all matching
243*4882a593Smuzhiyun        # events in result. Fail if there's not any.
244*4882a593Smuzhiyun        for exp_name, exp_event in expect.items():
245*4882a593Smuzhiyun            exp_list = []
246*4882a593Smuzhiyun            res_event = {}
247*4882a593Smuzhiyun            log.debug("    matching [%s]" % exp_name)
248*4882a593Smuzhiyun            for res_name, res_event in result.items():
249*4882a593Smuzhiyun                log.debug("      to [%s]" % res_name)
250*4882a593Smuzhiyun                if (exp_event.equal(res_event)):
251*4882a593Smuzhiyun                    exp_list.append(res_name)
252*4882a593Smuzhiyun                    log.debug("    ->OK")
253*4882a593Smuzhiyun                else:
254*4882a593Smuzhiyun                    log.debug("    ->FAIL");
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun            log.debug("    match: [%s] matches %s" % (exp_name, str(exp_list)))
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun            # we did not any matching event - fail
259*4882a593Smuzhiyun            if not exp_list:
260*4882a593Smuzhiyun                if exp_event.optional():
261*4882a593Smuzhiyun                    log.debug("    %s does not match, but is optional" % exp_name)
262*4882a593Smuzhiyun                else:
263*4882a593Smuzhiyun                    if not res_event:
264*4882a593Smuzhiyun                        log.debug("    res_event is empty");
265*4882a593Smuzhiyun                    else:
266*4882a593Smuzhiyun                        exp_event.diff(res_event)
267*4882a593Smuzhiyun                    raise Fail(self, 'match failure');
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun            match[exp_name] = exp_list
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun        # For each defined group in the expected events
272*4882a593Smuzhiyun        # check we match the same group in the result.
273*4882a593Smuzhiyun        for exp_name, exp_event in expect.items():
274*4882a593Smuzhiyun            group = exp_event.group
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun            if (group == ''):
277*4882a593Smuzhiyun                continue
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun            for res_name in match[exp_name]:
280*4882a593Smuzhiyun                res_group = result[res_name].group
281*4882a593Smuzhiyun                if res_group not in match[group]:
282*4882a593Smuzhiyun                    raise Fail(self, 'group failure')
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun                log.debug("    group: [%s] matches group leader %s" %
285*4882a593Smuzhiyun                         (exp_name, str(match[group])))
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun        log.debug("  matched")
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun    def resolve_groups(self, events):
290*4882a593Smuzhiyun        for name, event in events.items():
291*4882a593Smuzhiyun            group_fd = event['group_fd'];
292*4882a593Smuzhiyun            if group_fd == '-1':
293*4882a593Smuzhiyun                continue;
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun            for iname, ievent in events.items():
296*4882a593Smuzhiyun                if (ievent['fd'] == group_fd):
297*4882a593Smuzhiyun                    event.group = iname
298*4882a593Smuzhiyun                    log.debug('[%s] has group leader [%s]' % (name, iname))
299*4882a593Smuzhiyun                    break;
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun    def run(self):
302*4882a593Smuzhiyun        tempdir = tempfile.mkdtemp();
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun        try:
305*4882a593Smuzhiyun            # run the test script
306*4882a593Smuzhiyun            self.run_cmd(tempdir);
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun            # load events expectation for the test
309*4882a593Smuzhiyun            log.debug("  loading result events");
310*4882a593Smuzhiyun            for f in glob.glob(tempdir + '/event*'):
311*4882a593Smuzhiyun                self.load_events(f, self.result);
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun            # resolve group_fd to event names
314*4882a593Smuzhiyun            self.resolve_groups(self.expect);
315*4882a593Smuzhiyun            self.resolve_groups(self.result);
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun            # do the expectation - results matching - both ways
318*4882a593Smuzhiyun            self.compare(self.expect, self.result)
319*4882a593Smuzhiyun            self.compare(self.result, self.expect)
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun        finally:
322*4882a593Smuzhiyun            # cleanup
323*4882a593Smuzhiyun            shutil.rmtree(tempdir)
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun
326*4882a593Smuzhiyundef run_tests(options):
327*4882a593Smuzhiyun    for f in glob.glob(options.test_dir + '/' + options.test):
328*4882a593Smuzhiyun        try:
329*4882a593Smuzhiyun            Test(f, options).run()
330*4882a593Smuzhiyun        except Unsup as obj:
331*4882a593Smuzhiyun            log.warning("unsupp  %s" % obj.getMsg())
332*4882a593Smuzhiyun        except Notest as obj:
333*4882a593Smuzhiyun            log.warning("skipped %s" % obj.getMsg())
334*4882a593Smuzhiyun
335*4882a593Smuzhiyundef setup_log(verbose):
336*4882a593Smuzhiyun    global log
337*4882a593Smuzhiyun    level = logging.CRITICAL
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun    if verbose == 1:
340*4882a593Smuzhiyun        level = logging.WARNING
341*4882a593Smuzhiyun    if verbose == 2:
342*4882a593Smuzhiyun        level = logging.INFO
343*4882a593Smuzhiyun    if verbose >= 3:
344*4882a593Smuzhiyun        level = logging.DEBUG
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun    log = logging.getLogger('test')
347*4882a593Smuzhiyun    log.setLevel(level)
348*4882a593Smuzhiyun    ch  = logging.StreamHandler()
349*4882a593Smuzhiyun    ch.setLevel(level)
350*4882a593Smuzhiyun    formatter = logging.Formatter('%(message)s')
351*4882a593Smuzhiyun    ch.setFormatter(formatter)
352*4882a593Smuzhiyun    log.addHandler(ch)
353*4882a593Smuzhiyun
354*4882a593SmuzhiyunUSAGE = '''%s [OPTIONS]
355*4882a593Smuzhiyun  -d dir  # tests dir
356*4882a593Smuzhiyun  -p path # perf binary
357*4882a593Smuzhiyun  -t test # single test
358*4882a593Smuzhiyun  -v      # verbose level
359*4882a593Smuzhiyun''' % sys.argv[0]
360*4882a593Smuzhiyun
361*4882a593Smuzhiyundef main():
362*4882a593Smuzhiyun    parser = optparse.OptionParser(usage=USAGE)
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun    parser.add_option("-t", "--test",
365*4882a593Smuzhiyun                      action="store", type="string", dest="test")
366*4882a593Smuzhiyun    parser.add_option("-d", "--test-dir",
367*4882a593Smuzhiyun                      action="store", type="string", dest="test_dir")
368*4882a593Smuzhiyun    parser.add_option("-p", "--perf",
369*4882a593Smuzhiyun                      action="store", type="string", dest="perf")
370*4882a593Smuzhiyun    parser.add_option("-v", "--verbose",
371*4882a593Smuzhiyun                      default=0, action="count", dest="verbose")
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun    options, args = parser.parse_args()
374*4882a593Smuzhiyun    if args:
375*4882a593Smuzhiyun        parser.error('FAILED wrong arguments %s' %  ' '.join(args))
376*4882a593Smuzhiyun        return -1
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun    setup_log(options.verbose)
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun    if not options.test_dir:
381*4882a593Smuzhiyun        print('FAILED no -d option specified')
382*4882a593Smuzhiyun        sys.exit(-1)
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun    if not options.test:
385*4882a593Smuzhiyun        options.test = 'test*'
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun    try:
388*4882a593Smuzhiyun        run_tests(options)
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun    except Fail as obj:
391*4882a593Smuzhiyun        print("FAILED %s" % obj.getMsg())
392*4882a593Smuzhiyun        sys.exit(-1)
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun    sys.exit(0)
395*4882a593Smuzhiyun
396*4882a593Smuzhiyunif __name__ == '__main__':
397*4882a593Smuzhiyun    main()
398