1import lldb
2from intelpt_testcase import *
3from lldbsuite.test.lldbtest import *
4from lldbsuite.test import lldbutil
5from lldbsuite.test.decorators import *
6
7class TestTraceTimestampCounters(TraceIntelPTTestCaseBase):
8
9    @testSBAPIAndCommands
10    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
11    def testTscPerThread(self):
12        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
13        self.expect("b main")
14        self.expect("r")
15
16        self.traceStartThread(enableTsc=True)
17
18        self.expect("n")
19        self.expect("thread trace dump instructions -t -c 1",
20            patterns=[": \[\d+.\d+ ns\] 0x0000000000400511    movl"])
21
22    @testSBAPIAndCommands
23    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
24    def testMultipleTscsPerThread(self):
25        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
26        self.expect("b main")
27        self.expect("r")
28
29        self.traceStartThread(enableTsc=True)
30
31        # After each stop there'll be a new TSC
32        self.expect("si")
33        self.expect("si")
34        self.expect("si")
35
36        # We'll get the most recent instructions, with at least 3 different TSCs
37        self.runCmd("thread trace dump instructions -t --raw --forward")
38        id_to_timestamp = {}
39        for line in self.res.GetOutput().splitlines():
40            m = re.search("    (.+): \[(.+)\ ns].*", line)
41            if m:
42                id_to_timestamp[int(m.group(1))] = m.group(2)
43        self.assertEqual(len(id_to_timestamp), 3)
44
45        # We check that the values are right when dumping a specific id
46        for id, timestamp in id_to_timestamp.items():
47            self.expect(f"thread trace dump instructions -t --id {id} -c 1",
48                substrs=[f"{id}: [{timestamp} ns]"])
49
50    @testSBAPIAndCommands
51    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
52    def testTscPerProcess(self):
53        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
54        self.expect("b main")
55        self.expect("r")
56
57        self.traceStartProcess(enableTsc=True)
58
59        self.expect("n")
60        self.expect("thread trace dump instructions -t -c 1",
61            patterns=[": \[\d+.\d+ ns\] 0x0000000000400511    movl"])
62
63        self.expect("thread trace dump instructions -t -c 1 --pretty-json",
64            patterns=['''"timestamp_ns": "\d+.\d+"'''])
65
66    @testSBAPIAndCommands
67    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
68    def testDumpingAfterTracingWithoutTsc(self):
69        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
70        self.expect("b main")
71        self.expect("r")
72
73        self.traceStartThread(enableTsc=False)
74
75        self.expect("n")
76        self.expect("thread trace dump instructions -t -c 1",
77            patterns=[": \[unavailable\] 0x0000000000400511    movl"])
78
79        self.expect("thread trace dump instructions -t -c 1 --json",
80            substrs=['''"timestamp_ns":null'''])
81
82    @testSBAPIAndCommands
83    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
84    def testPSBPeriod(self):
85        def isPSBSupported():
86            caps_file = "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc"
87            if not os.path.exists(caps_file):
88                return False
89            with open(caps_file, "r") as f:
90                val = int(f.readline())
91                if val != 1:
92                    return False
93            return True
94
95        def getValidPSBValues():
96            values_file = "/sys/bus/event_source/devices/intel_pt/caps/psb_periods"
97            values = []
98            with open(values_file, "r") as f:
99                mask = int(f.readline(), 16)
100                for i in range(0, 32):
101                    if (1 << i) & mask:
102                        values.append(i)
103            return values
104
105
106        if not isPSBSupported():
107            self.skipTest("PSB period unsupported")
108
109        valid_psb_values = getValidPSBValues()
110        # 0 should always be valid, and it's assumed by lldb-server
111        self.assertEqual(valid_psb_values[0], 0)
112
113        self.expect("file " + (os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")))
114        self.expect("b main")
115        self.expect("r")
116
117        # it's enough to test with two valid values
118        for psb_period in (valid_psb_values[0], valid_psb_values[-1]):
119            # we first test at thread level
120            self.traceStartThread(psbPeriod=psb_period)
121            self.traceStopThread()
122
123            # we now test at process level
124            self.traceStartProcess(psbPeriod=psb_period)
125            self.traceStopProcess()
126
127        # we now test invalid values
128        self.traceStartThread(psbPeriod=valid_psb_values[-1] + 1, error=True,
129            substrs=["Invalid psb_period. Valid values are: 0"])
130
131        # TODO: dump the perf_event_attr.config as part of the upcoming "trace dump info"
132        # command and check that the psb period is included there.
133