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