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