import lldb import json from intelpt_testcase import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil from lldbsuite.test.decorators import * def find(predicate, seq): for item in seq: if predicate(item): return item class TestTraceSave(TraceIntelPTTestCaseBase): def testErrorMessages(self): # We first check the output when there are no targets self.expect( "trace save", substrs=[ "error: invalid target, create a target using the 'target create' command" ], error=True, ) # We now check the output when there's a non-running target self.expect( "target create " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out") ) self.expect( "trace save", substrs=["error: Command requires a current process."], error=True, ) # Now we check the output when there's a running target without a trace self.expect("b main") self.expect("run") self.expect( "trace save", substrs=["error: Process is not being traced"], error=True ) def testSaveToInvalidDir(self): self.expect( "target create " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out") ) self.expect("b main") self.expect("r") self.expect("thread trace start") self.expect("n") # Check the output when saving without providing the directory argument self.expect( "trace save ", substrs=[ "error: a single path to a directory where the trace bundle will be created is required" ], error=True, ) # Check the output when saving to an invalid directory self.expect( "trace save /", substrs=["error: couldn't write to the file"], error=True ) def testSaveWhenNotLiveTrace(self): self.expect( "trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"), substrs=["intel-pt"], ) # Check the output when not doing live tracing self.expect( "trace save " + os.path.join(self.getBuildDir(), "intelpt-trace", "trace_not_live_dir") ) def testSaveMultiCpuTrace(self): """ This test starts a per-cpu tracing session, then saves the session to disk, and finally it loads it again. """ self.skipIfPerCpuTracingIsNotSupported() self.expect( "target create " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out") ) self.expect("b main") self.expect("r") self.expect("process trace start --per-cpu-tracing") self.expect("b 7") output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save") self.expect("trace save " + output_dir) def checkSessionBundle(session_file_path): with open(session_file_path) as session_file: session = json.load(session_file) # We expect tsc conversion info self.assertTrue("tscPerfZeroConversion" in session) # We expect at least one cpu self.assertGreater(len(session["cpus"]), 0) # We expect the required trace files to be created for cpu in session["cpus"]: cpu_files_prefix = os.path.join(output_dir, "cpus", str(cpu["id"])) self.assertTrue(os.path.exists(cpu_files_prefix + ".intelpt_trace")) self.assertTrue( os.path.exists(cpu_files_prefix + ".perf_context_switch_trace") ) # We expect at least one one process self.assertGreater(len(session["processes"]), 0) for process in session["processes"]: # We expect at least one thread self.assertGreater(len(process["threads"]), 0) # We don't expect thread traces for thread in process["threads"]: self.assertTrue( ("iptTrace" not in thread) or (thread["iptTrace"] is None) ) original_trace_session_file = os.path.join(output_dir, "trace.json") checkSessionBundle(original_trace_session_file) output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save") self.expect("trace load " + os.path.join(output_dir, "trace.json")) output_copy_dir = os.path.join( self.getBuildDir(), "intelpt-trace", "copy_trace_save" ) self.expect("trace save " + output_copy_dir) # We now check that the new bundle is correct on its own copied_trace_session_file = os.path.join(output_copy_dir, "trace.json") checkSessionBundle(copied_trace_session_file) # We finally check that the new bundle has the same information as the original one with open(original_trace_session_file) as original_file: original = json.load(original_file) with open(copied_trace_session_file) as copy_file: copy = json.load(copy_file) self.assertEqual(len(original["processes"]), len(copy["processes"])) for process in original["processes"]: copied_process = find( lambda proc: proc["pid"] == process["pid"], copy["processes"] ) self.assertTrue(copied_process is not None) for thread in process["threads"]: copied_thread = find( lambda thr: thr["tid"] == thread["tid"], copied_process["threads"], ) self.assertTrue(copied_thread is not None) for cpu in original["cpus"]: copied_cpu = find(lambda cor: cor["id"] == cpu["id"], copy["cpus"]) self.assertTrue(copied_cpu is not None) def testSaveTrace(self): self.expect( "target create " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out") ) self.expect("b main") self.expect("r") self.expect("thread trace start") self.expect("b 7") ci = self.dbg.GetCommandInterpreter() res = lldb.SBCommandReturnObject() ci.HandleCommand("thread trace dump instructions -c 10 --forwards", res) self.assertEqual(res.Succeeded(), True) first_ten_instructions = res.GetOutput() ci.HandleCommand("thread trace dump instructions -c 10", res) self.assertEqual(res.Succeeded(), True) last_ten_instructions = res.GetOutput() # Now, save the trace to self.expect( "trace save " + os.path.join(self.getBuildDir(), "intelpt-trace", "trace_copy_dir") ) # Load the trace just saved self.expect( "trace load -v " + os.path.join( self.getBuildDir(), "intelpt-trace", "trace_copy_dir", "trace.json" ), substrs=["intel-pt"], ) # Compare with instructions saved at the first time ci.HandleCommand("thread trace dump instructions -c 10 --forwards", res) self.assertEqual(res.Succeeded(), True) self.assertEqual(res.GetOutput(), first_ten_instructions) ci.HandleCommand("thread trace dump instructions -c 10", res) self.assertEqual(res.Succeeded(), True) self.assertEqual(res.GetOutput(), last_ten_instructions) def testSaveKernelTrace(self): original_trace_file = os.path.join( self.getSourceDir(), "intelpt-kernel-trace", "trace.json" ) copied_trace_dir = os.path.join(self.getBuildDir(), "intelpt-kernel-trace") copied_trace_file = os.path.join(copied_trace_dir, "trace.json") self.expect("trace load -v " + original_trace_file, substrs=["intel-pt"]) self.expect("trace save " + copied_trace_dir) # We finally check that the new json has the same information as the original one with open(original_trace_file) as original_file: original_file = json.load(original_file) with open(copied_trace_file) as copy_file: copy_file = json.load(copy_file) self.assertTrue("kernel" in copy_file) self.assertEqual( os.path.basename(original_file["kernel"]["file"]), os.path.basename(copy_file["kernel"]["file"]), )