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    \[3\] {ADDRESS_REGEX}    cmpl .*
104    \[4\] {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5'''])
105
106        # We stop tracing
107        self.expect("thread trace stop")
108
109        # We can't stop twice
110        self.expect("thread trace stop", error=True,
111            substrs=["error: Thread ", "not currently traced"])
112
113        # We trace again from scratch, this time letting LLDB to pick the current
114        # thread
115        self.expect("thread trace start")
116        self.expect("n")
117        self.expect("thread trace dump instructions",
118            patterns=[f'''thread #1: tid = .*, total instructions = 1
119  a.out`main \+ 20 at main.cpp:5
120    \[0\] {ADDRESS_REGEX}    xorl'''])
121
122        self.expect("c")
123        # Now the process has finished, so the commands should fail
124        self.expect("thread trace start", error=True,
125            substrs=["error: Process must be launched"])
126
127        self.expect("thread trace stop", error=True,
128            substrs=["error: Process must be launched"])
129