| import lldb | |
| import json | |
| from intelpt_testcase import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbutil | |
| from lldbsuite.test.decorators import * | |
| @skipIfTargetDoesNotSupportThreads() | |
| @skipIfNoIntelPT | |
| class TestTraceStartStopMultipleThreads(TraceIntelPTTestCaseBase): | |
| @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"])) | |
| @testSBAPIAndCommands | |
| def testStartMultipleLiveThreads(self): | |
| self.build() | |
| exe = self.getBuildArtifact("a.out") | |
| self.dbg.CreateTarget(exe) | |
| self.expect("b main") | |
| self.expect("b 6") | |
| self.expect("b 11") | |
| self.expect("r") | |
| self.traceStartProcess() | |
| self.expect("continue") | |
| self.expect("thread trace dump instructions", substrs=["main.cpp:9"]) | |
| # We'll see here the second thread | |
| self.expect("continue") | |
| self.expect("thread trace dump instructions", substrs=["main.cpp:4"]) | |
| self.traceStopProcess() | |
| @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"])) | |
| @testSBAPIAndCommands | |
| def testStartMultipleLiveThreadsWithStops(self): | |
| self.build() | |
| exe = self.getBuildArtifact("a.out") | |
| self.dbg.CreateTarget(exe) | |
| self.expect("b main") | |
| self.expect("b 6") | |
| self.expect("b 11") | |
| self.expect("r") | |
| self.traceStartProcess() | |
| # We'll see here the first thread | |
| self.expect("continue") | |
| # We are in thread 2 | |
| self.expect("thread trace dump instructions", substrs=["main.cpp:9"]) | |
| self.expect("thread trace dump instructions 2", substrs=["main.cpp:9"]) | |
| # We stop tracing all | |
| self.expect("thread trace stop all") | |
| # The trace is still in memory | |
| self.expect("thread trace dump instructions 2", substrs=["main.cpp:9"]) | |
| # We'll stop at the next breakpoint in thread 3, thread 2 and 3 will be alive, but only 3 traced. | |
| self.expect("continue") | |
| self.expect("thread trace dump instructions", substrs=["main.cpp:4"]) | |
| self.expect("thread trace dump instructions 3", substrs=["main.cpp:4"]) | |
| self.expect( | |
| "thread trace dump instructions 1", substrs=["not traced"], error=True | |
| ) | |
| self.expect( | |
| "thread trace dump instructions 2", substrs=["not traced"], error=True | |
| ) | |
| self.traceStopProcess() | |
| @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"])) | |
| def testStartMultipleLiveThreadsWithThreadStartAll(self): | |
| self.build() | |
| exe = self.getBuildArtifact("a.out") | |
| target = self.dbg.CreateTarget(exe) | |
| self.expect("b main") | |
| self.expect("b 6") | |
| self.expect("b 11") | |
| self.expect("r") | |
| self.expect("continue") | |
| # We are in thread 2 | |
| self.expect("thread trace start all") | |
| # Now we have instructions in thread's 2 trace | |
| self.expect("n") | |
| self.expect("thread trace dump instructions 2", substrs=["main.cpp:11"]) | |
| # We stop tracing all | |
| self.runCmd("thread trace stop all") | |
| # The trace is still in memory | |
| self.expect("thread trace dump instructions 2", substrs=["main.cpp:11"]) | |
| # We'll stop at the next breakpoint in thread 3, and nothing should be traced | |
| self.expect("continue") | |
| self.expect( | |
| "thread trace dump instructions 3", substrs=["not traced"], error=True | |
| ) | |
| self.expect( | |
| "thread trace dump instructions 1", substrs=["not traced"], error=True | |
| ) | |
| self.expect( | |
| "thread trace dump instructions 2", substrs=["not traced"], error=True | |
| ) | |
| @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"])) | |
| @testSBAPIAndCommands | |
| def testStartMultipleLiveThreadsWithSmallTotalLimit(self): | |
| self.build() | |
| exe = self.getBuildArtifact("a.out") | |
| self.dbg.CreateTarget(exe) | |
| self.expect("b main") | |
| self.expect("r") | |
| # trace the entire process with enough total size for 1 thread trace | |
| self.traceStartProcess(processBufferSizeLimit=5000) | |
| # we get the stop event when trace 2 appears and can't be traced | |
| self.expect("c", substrs=["Thread", "can't be traced"]) | |
| # we get the stop event when trace 3 appears and can't be traced | |
| self.expect("c", substrs=["Thread", "can't be traced"]) | |
| self.traceStopProcess() | |
| @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"])) | |
| @testSBAPIAndCommands | |
| def testStartPerCpuSession(self): | |
| self.skipIfPerCpuTracingIsNotSupported() | |
| self.build() | |
| exe = self.getBuildArtifact("a.out") | |
| self.dbg.CreateTarget(exe) | |
| self.expect("b main") | |
| self.expect("r") | |
| # We should fail if we hit the total buffer limit. Useful if the number | |
| # of cpus is huge. | |
| self.traceStartProcess( | |
| error="True", | |
| processBufferSizeLimit=100, | |
| perCpuTracing=True, | |
| substrs=[ | |
| "The process can't be traced because the process trace size " | |
| "limit has been reached. Consider retracing with a higher limit." | |
| ], | |
| ) | |
| self.traceStartProcess(perCpuTracing=True) | |
| self.traceStopProcess() | |
| self.traceStartProcess(perCpuTracing=True) | |
| # We can't support multiple per-cpu tracing sessions. | |
| self.traceStartProcess( | |
| error=True, | |
| perCpuTracing=True, | |
| substrs=["Process currently traced. Stop process tracing first"], | |
| ) | |
| # We can't support tracing per thread is per cpu is enabled. | |
| self.traceStartThread( | |
| error="True", substrs=["Thread with tid ", "is currently traced"] | |
| ) | |
| # We can't stop individual thread when per cpu is enabled. | |
| self.traceStopThread( | |
| error="True", | |
| substrs=[ | |
| "Can't stop tracing an individual thread when per-cpu process tracing is enabled" | |
| ], | |
| ) | |
| # We move forward a little bit to collect some data | |
| self.expect("b 19") | |
| self.expect("c") | |
| # We will assert that the trace state will contain valid context switch and intel pt trace buffer entries. | |
| # Besides that, we need to get tsc-to-nanos conversion information. | |
| # We first parse the json response from the custom packet | |
| self.runCmd( | |
| """process plugin packet send 'jLLDBTraceGetState:{"type":"intel-pt"}]'""" | |
| ) | |
| response_header = "response: " | |
| output = None | |
| for line in self.res.GetOutput().splitlines(): | |
| if line.find(response_header) != -1: | |
| response = line[ | |
| line.find(response_header) + len(response_header) : | |
| ].strip() | |
| output = json.loads(response) | |
| self.assertIsNotNone(output) | |
| self.assertIn("cpus", output) | |
| self.assertIn("tscPerfZeroConversion", output) | |
| found_non_empty_context_switch = False | |
| for cpu in output["cpus"]: | |
| context_switch_size = None | |
| ipt_trace_size = None | |
| for binary_data in cpu["binaryData"]: | |
| if binary_data["kind"] == "iptTrace": | |
| ipt_trace_size = binary_data["size"] | |
| elif binary_data["kind"] == "perfContextSwitchTrace": | |
| context_switch_size = binary_data["size"] | |
| self.assertIsNotNone(context_switch_size) | |
| self.assertIsNotNone(ipt_trace_size) | |
| if context_switch_size > 0: | |
| found_non_empty_context_switch = True | |
| # We must have captured the context switch of when the target resumed | |
| self.assertTrue(found_non_empty_context_switch) | |
| self.expect("thread trace dump instructions") | |
| self.traceStopProcess() |