236 lines · python
1import lldb2import json3from intelpt_testcase import *4from lldbsuite.test.lldbtest import *5from lldbsuite.test import lldbutil6from lldbsuite.test.decorators import *7 8 9def find(predicate, seq):10 for item in seq:11 if predicate(item):12 return item13 14 15class TestTraceSave(TraceIntelPTTestCaseBase):16 def testErrorMessages(self):17 # We first check the output when there are no targets18 self.expect(19 "trace save",20 substrs=[21 "error: invalid target, create a target using the 'target create' command"22 ],23 error=True,24 )25 26 # We now check the output when there's a non-running target27 self.expect(28 "target create "29 + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")30 )31 32 self.expect(33 "trace save",34 substrs=["error: Command requires a current process."],35 error=True,36 )37 38 # Now we check the output when there's a running target without a trace39 self.expect("b main")40 self.expect("run")41 42 self.expect(43 "trace save", substrs=["error: Process is not being traced"], error=True44 )45 46 @skipIfNoIntelPT47 def testSaveToInvalidDir(self):48 self.expect(49 "target create "50 + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")51 )52 self.expect("b main")53 self.expect("r")54 self.expect("thread trace start")55 self.expect("n")56 57 # Check the output when saving without providing the directory argument58 self.expect(59 "trace save ",60 substrs=[61 "error: a single path to a directory where the trace bundle will be created is required"62 ],63 error=True,64 )65 66 # Check the output when saving to an invalid directory67 self.expect(68 "trace save /", substrs=["error: couldn't write to the file"], error=True69 )70 71 def testSaveWhenNotLiveTrace(self):72 self.expect(73 "trace load -v "74 + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),75 substrs=["intel-pt"],76 )77 78 # Check the output when not doing live tracing79 self.expect(80 "trace save "81 + os.path.join(self.getBuildDir(), "intelpt-trace", "trace_not_live_dir")82 )83 84 def testSaveMultiCpuTrace(self):85 """86 This test starts a per-cpu tracing session, then saves the session to disk, and87 finally it loads it again.88 """89 self.skipIfPerCpuTracingIsNotSupported()90 91 self.expect(92 "target create "93 + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")94 )95 self.expect("b main")96 self.expect("r")97 self.expect("process trace start --per-cpu-tracing")98 self.expect("b 7")99 100 output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save")101 self.expect("trace save " + output_dir)102 103 def checkSessionBundle(session_file_path):104 with open(session_file_path) as session_file:105 session = json.load(session_file)106 # We expect tsc conversion info107 self.assertIn("tscPerfZeroConversion", session)108 # We expect at least one cpu109 self.assertGreater(len(session["cpus"]), 0)110 111 # We expect the required trace files to be created112 for cpu in session["cpus"]:113 cpu_files_prefix = os.path.join(output_dir, "cpus", str(cpu["id"]))114 self.assertTrue(os.path.exists(cpu_files_prefix + ".intelpt_trace"))115 self.assertTrue(116 os.path.exists(cpu_files_prefix + ".perf_context_switch_trace")117 )118 119 # We expect at least one one process120 self.assertGreater(len(session["processes"]), 0)121 for process in session["processes"]:122 # We expect at least one thread123 self.assertGreater(len(process["threads"]), 0)124 # We don't expect thread traces125 for thread in process["threads"]:126 self.assertTrue(127 ("iptTrace" not in thread) or (thread["iptTrace"] is None)128 )129 130 original_trace_session_file = os.path.join(output_dir, "trace.json")131 checkSessionBundle(original_trace_session_file)132 133 output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save")134 self.expect("trace load " + os.path.join(output_dir, "trace.json"))135 output_copy_dir = os.path.join(136 self.getBuildDir(), "intelpt-trace", "copy_trace_save"137 )138 self.expect("trace save " + output_copy_dir)139 140 # We now check that the new bundle is correct on its own141 copied_trace_session_file = os.path.join(output_copy_dir, "trace.json")142 checkSessionBundle(copied_trace_session_file)143 144 # We finally check that the new bundle has the same information as the original one145 with open(original_trace_session_file) as original_file:146 original = json.load(original_file)147 with open(copied_trace_session_file) as copy_file:148 copy = json.load(copy_file)149 150 self.assertEqual(len(original["processes"]), len(copy["processes"]))151 152 for process in original["processes"]:153 copied_process = find(154 lambda proc: proc["pid"] == process["pid"], copy["processes"]155 )156 self.assertIsNotNone(copied_process)157 158 for thread in process["threads"]:159 copied_thread = find(160 lambda thr: thr["tid"] == thread["tid"],161 copied_process["threads"],162 )163 self.assertIsNotNone(copied_thread)164 165 for cpu in original["cpus"]:166 copied_cpu = find(lambda cor: cor["id"] == cpu["id"], copy["cpus"])167 self.assertIsNotNone(copied_cpu)168 169 @skipIfNoIntelPT170 def testSaveTrace(self):171 self.expect(172 "target create "173 + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")174 )175 self.expect("b main")176 self.expect("r")177 self.expect("thread trace start")178 self.expect("b 7")179 180 ci = self.dbg.GetCommandInterpreter()181 res = lldb.SBCommandReturnObject()182 183 ci.HandleCommand("thread trace dump instructions -c 10 --forwards", res)184 self.assertTrue(res.Succeeded())185 first_ten_instructions = res.GetOutput()186 187 ci.HandleCommand("thread trace dump instructions -c 10", res)188 self.assertTrue(res.Succeeded())189 last_ten_instructions = res.GetOutput()190 191 # Now, save the trace to <trace_copy_dir>192 self.expect(193 "trace save "194 + os.path.join(self.getBuildDir(), "intelpt-trace", "trace_copy_dir")195 )196 197 # Load the trace just saved198 self.expect(199 "trace load -v "200 + os.path.join(201 self.getBuildDir(), "intelpt-trace", "trace_copy_dir", "trace.json"202 ),203 substrs=["intel-pt"],204 )205 206 # Compare with instructions saved at the first time207 ci.HandleCommand("thread trace dump instructions -c 10 --forwards", res)208 self.assertTrue(res.Succeeded())209 self.assertEqual(res.GetOutput(), first_ten_instructions)210 211 ci.HandleCommand("thread trace dump instructions -c 10", res)212 self.assertTrue(res.Succeeded())213 self.assertEqual(res.GetOutput(), last_ten_instructions)214 215 def testSaveKernelTrace(self):216 original_trace_file = os.path.join(217 self.getSourceDir(), "intelpt-kernel-trace", "trace.json"218 )219 copied_trace_dir = os.path.join(self.getBuildDir(), "intelpt-kernel-trace")220 copied_trace_file = os.path.join(copied_trace_dir, "trace.json")221 222 self.expect("trace load -v " + original_trace_file, substrs=["intel-pt"])223 self.expect("trace save " + copied_trace_dir)224 225 # We finally check that the new json has the same information as the original one226 with open(original_trace_file) as original_file:227 original_file = json.load(original_file)228 with open(copied_trace_file) as copy_file:229 copy_file = json.load(copy_file)230 self.assertIn("kernel", copy_file)231 232 self.assertEqual(233 os.path.basename(original_file["kernel"]["file"]),234 os.path.basename(copy_file["kernel"]["file"]),235 )236