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