1import lldb
2from lldbsuite.test.lldbtest import *
3from lldbsuite.test import lldbutil
4from lldbsuite.test.decorators import *
5
6ADDRESS_REGEX = '0x[0-9a-fA-F]*'
7
8class TestTraceStartStop(TestBase):
9
10    mydir = TestBase.compute_mydir(__file__)
11    NO_DEBUG_INFO_TESTCASE = True
12
13    def setUp(self):
14        TestBase.setUp(self)
15        if 'intel-pt' not in configuration.enabled_plugins:
16            self.skipTest("The intel-pt test plugin is not enabled")
17
18    def expectGenericHelpMessageForStartCommand(self):
19        self.expect("help thread trace start",
20            substrs=["Syntax: thread trace start [<trace-options>]"])
21
22    def testStartStopSessionFileThreads(self):
23        # it should fail for processes from json session files
24        self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"))
25        self.expect("thread trace start", error=True,
26            substrs=["error: Process must be alive"])
27
28        # the help command should be the generic one, as it's not a live process
29        self.expectGenericHelpMessageForStartCommand()
30
31        self.expect("thread trace stop", error=True)
32
33    def testStartWithNoProcess(self):
34        self.expect("thread trace start", error=True,
35            substrs=["error: Process not available."])
36
37
38    def testStartSessionWithWrongSize(self):
39        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
40        self.expect("b main")
41        self.expect("r")
42        self.expect("thread trace start -s 2000", error=True,
43            substrs=["The trace buffer size must be a power of 2", "It was 2000"])
44        self.expect("thread trace start -s 5000", error=True,
45            substrs=["The trace buffer size must be a power of 2", "It was 5000"])
46        self.expect("thread trace start -s 0", error=True,
47            substrs=["The trace buffer size must be a power of 2", "It was 0"])
48        self.expect("thread trace start -s 1048576")
49
50
51    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
52    def testStartStopLiveThreads(self):
53        # The help command should be the generic one if there's no process running
54        self.expectGenericHelpMessageForStartCommand()
55
56        self.expect("thread trace start", error=True,
57            substrs=["error: Process not available"])
58
59        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
60        self.expect("b main")
61
62        self.expect("thread trace start", error=True,
63            substrs=["error: Process not available"])
64
65        # The help command should be the generic one if there's still no process running
66        self.expectGenericHelpMessageForStartCommand()
67
68        self.expect("r")
69
70        # This fails because "trace start" hasn't been called yet
71        self.expect("thread trace stop", error=True,
72            substrs=["error: Process is not being traced"])
73
74
75        # the help command should be the intel-pt one now
76        self.expect("help thread trace start",
77            substrs=["Start tracing one or more threads with intel-pt.",
78                     "Syntax: thread trace start [<thread-index> <thread-index> ...] [<intel-pt-options>]"])
79
80        # We start tracing with a small buffer size
81        self.expect("thread trace start 1 --size 4096")
82
83        # We fail if we try to trace again
84        self.expect("thread trace start", error=True,
85            substrs=["error: Thread ", "already traced"])
86
87        # We can reconstruct the single instruction executed in the first line
88        self.expect("n")
89        self.expect("thread trace dump instructions",
90            patterns=[f'''thread #1: tid = .*, total instructions = 1
91  a.out`main \+ 4 at main.cpp:2
92    \[0\] {ADDRESS_REGEX}    movl'''])
93
94        # We can reconstruct the instructions up to the second line
95        self.expect("n")
96        self.expect("thread trace dump instructions",
97            patterns=[f'''thread #1: tid = .*, total instructions = 5
98  a.out`main \+ 4 at main.cpp:2
99    \[0\] {ADDRESS_REGEX}    movl .*
100  a.out`main \+ 11 at main.cpp:4
101    \[1\] {ADDRESS_REGEX}    movl .*
102    \[2\] {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
103  a.out`main \+ 28 at main.cpp:4
104    \[3\] {ADDRESS_REGEX}    cmpl .*
105    \[4\] {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5'''])
106
107        # We stop tracing
108        self.expect("thread trace stop")
109
110        # We can't stop twice
111        self.expect("thread trace stop", error=True,
112            substrs=["error: Thread ", "not currently traced"])
113
114        # We trace again from scratch, this time letting LLDB to pick the current
115        # thread
116        self.expect("thread trace start")
117        self.expect("n")
118        self.expect("thread trace dump instructions",
119            patterns=[f'''thread #1: tid = .*, total instructions = 1
120  a.out`main \+ 20 at main.cpp:5
121    \[0\] {ADDRESS_REGEX}    xorl'''])
122
123        self.expect("c")
124        # Now the process has finished, so the commands should fail
125        self.expect("thread trace start", error=True,
126            substrs=["error: Process must be launched"])
127
128        self.expect("thread trace stop", error=True,
129            substrs=["error: Process must be launched"])
130