1import lldb
2from intelpt_testcase import *
3from lldbsuite.test.lldbtest import *
4from lldbsuite.test import lldbutil
5from lldbsuite.test.decorators import *
6
7class TestTraceStartStop(TraceIntelPTTestCaseBase):
8
9    def expectGenericHelpMessageForStartCommand(self):
10        self.expect("help thread trace start",
11            substrs=["Syntax: thread trace start [<trace-options>]"])
12
13    @testSBAPIAndCommands
14    def testStartStopSessionFileThreads(self):
15        # it should fail for processes from json session files
16        self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"))
17
18        # the help command should be the generic one, as it's not a live process
19        self.expectGenericHelpMessageForStartCommand()
20
21        self.traceStartThread(error=True)
22
23        self.traceStopThread(error=True)
24
25    @testSBAPIAndCommands
26    def testStartWithNoProcess(self):
27        self.traceStartThread(error=True)
28
29    @testSBAPIAndCommands
30    def testStartSessionWithWrongSize(self):
31        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
32        self.expect("b main")
33        self.expect("r")
34
35        self.traceStartThread(
36            error=True, iptTraceSize=2000,
37            substrs=["The intel pt trace size must be a power of 2", "It was 2000"])
38
39        self.traceStartThread(
40            error=True, iptTraceSize=5000,
41            substrs=["The intel pt trace size must be a power of 2", "It was 5000"])
42
43        self.traceStartThread(
44            error=True, iptTraceSize=0,
45            substrs=["The intel pt trace size must be a power of 2", "It was 0"])
46
47        self.traceStartThread(iptTraceSize=1048576)
48
49    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
50    def testSBAPIHelp(self):
51        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
52        self.expect("b main")
53        self.expect("r")
54
55        help = self.getTraceOrCreate().GetStartConfigurationHelp()
56        self.assertIn("iptTraceSize", help)
57        self.assertIn("processBufferSizeLimit", help)
58
59    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
60    def testStoppingAThread(self):
61        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
62        self.expect("b main")
63        self.expect("r")
64        self.expect("thread trace start")
65        self.expect("n")
66        self.expect("thread trace dump instructions", substrs=["""0x0000000000400511    movl   $0x0, -0x4(%rbp)
67    no more data"""])
68        # process stopping should stop the thread
69        self.expect("process trace stop")
70        self.expect("n")
71        self.expect("thread trace dump instructions", substrs=["not traced"], error=True)
72
73
74    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
75    def testStartStopLiveThreads(self):
76        # The help command should be the generic one if there's no process running
77        self.expectGenericHelpMessageForStartCommand()
78
79        self.expect("thread trace start", error=True,
80            substrs=["error: Process not available"])
81
82        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
83        self.expect("b main")
84
85        self.expect("thread trace start", error=True,
86            substrs=["error: Process not available"])
87
88        # The help command should be the generic one if there's still no process running
89        self.expectGenericHelpMessageForStartCommand()
90
91        self.expect("r")
92
93        # This fails because "trace start" hasn't been called yet
94        self.expect("thread trace stop", error=True,
95            substrs=["error: Process is not being traced"])
96
97
98        # the help command should be the intel-pt one now
99        self.expect("help thread trace start",
100            substrs=["Start tracing one or more threads with intel-pt.",
101                     "Syntax: thread trace start [<thread-index> <thread-index> ...] [<intel-pt-options>]"])
102
103        # We start tracing with a small buffer size
104        self.expect("thread trace start 1 --size 4096")
105
106        # We fail if we try to trace again
107        self.expect("thread trace start", error=True,
108            substrs=["error: Thread ", "already traced"])
109
110        # We can reconstruct the single instruction executed in the first line
111        self.expect("n")
112        self.expect("thread trace dump instructions -f",
113            patterns=[f'''thread #1: tid = .*
114  a.out`main \+ 4 at main.cpp:2
115    0: {ADDRESS_REGEX}    movl'''])
116
117        # We can reconstruct the instructions up to the second line
118        self.expect("n")
119        self.expect("thread trace dump instructions -f",
120            patterns=[f'''thread #1: tid = .*
121  a.out`main \+ 4 at main.cpp:2
122    0: {ADDRESS_REGEX}    movl .*
123  a.out`main \+ 11 at main.cpp:4
124    2: {ADDRESS_REGEX}    movl .*
125    4: {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
126    6: {ADDRESS_REGEX}    cmpl .*
127    8: {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5'''])
128
129        self.expect("thread trace dump instructions",
130            patterns=[f'''thread #1: tid = .*
131  a.out`main \+ 32 at main.cpp:4
132    8: {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5
133    6: {ADDRESS_REGEX}    cmpl .*
134    4: {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
135    2: {ADDRESS_REGEX}    movl .*
136  a.out`main \+ 4 at main.cpp:2
137    0: {ADDRESS_REGEX}    movl .* '''])
138
139        # We stop tracing
140        self.expect("thread trace stop")
141
142        # We can't stop twice
143        self.expect("thread trace stop", error=True,
144            substrs=["error: Thread ", "not currently traced"])
145
146        # We trace again from scratch, this time letting LLDB to pick the current
147        # thread
148        self.expect("thread trace start")
149        self.expect("n")
150        self.expect("thread trace dump instructions -f",
151            patterns=[f'''thread #1: tid = .*
152  a.out`main \+ 20 at main.cpp:5
153    0: {ADDRESS_REGEX}    xorl'''])
154
155        self.expect("thread trace dump instructions",
156            patterns=[f'''thread #1: tid = .*
157  a.out`main \+ 20 at main.cpp:5
158    0: {ADDRESS_REGEX}    xorl'''])
159
160        self.expect("c")
161        # Now the process has finished, so the commands should fail
162        self.expect("thread trace start", error=True,
163            substrs=["error: Process must be launched"])
164
165        self.expect("thread trace stop", error=True,
166            substrs=["error: Process must be launched"])
167
168        # We should be able to trace the program if we relaunch it
169        # For this, we'll trace starting at a different point in the new
170        # process.
171        self.expect("breakpoint disable")
172        self.expect("b main.cpp:4")
173        self.expect("r")
174        self.expect("thread trace start")
175        # We can reconstruct the single instruction executed in the first line
176        self.expect("si")
177        self.expect("thread trace dump instructions -c 1",
178            patterns=[f'''thread #1: tid = .*
179  a.out`main \+ 11 at main.cpp:4'''])
180