xref: /llvm-project/lldb/test/API/commands/trace/TestTraceSave.py (revision 1eeeab82c6eb185f5139e633a59c2dbcb15616e4)
1602497d6SWalter Erquinigoimport lldb
2fc5ef57cSWalter Erquinigoimport json
3602497d6SWalter Erquinigofrom intelpt_testcase import *
4602497d6SWalter Erquinigofrom lldbsuite.test.lldbtest import *
5602497d6SWalter Erquinigofrom lldbsuite.test import lldbutil
6602497d6SWalter Erquinigofrom lldbsuite.test.decorators import *
7602497d6SWalter Erquinigo
82238dcc3SJonas Devlieghere
9fc5ef57cSWalter Erquinigodef find(predicate, seq):
10fc5ef57cSWalter Erquinigo    for item in seq:
11fc5ef57cSWalter Erquinigo        if predicate(item):
12fc5ef57cSWalter Erquinigo            return item
13fc5ef57cSWalter Erquinigo
14602497d6SWalter Erquinigo
152238dcc3SJonas Devlieghereclass TestTraceSave(TraceIntelPTTestCaseBase):
16602497d6SWalter Erquinigo    def testErrorMessages(self):
17602497d6SWalter Erquinigo        # We first check the output when there are no targets
182238dcc3SJonas Devlieghere        self.expect(
192238dcc3SJonas Devlieghere            "trace save",
202238dcc3SJonas Devlieghere            substrs=[
212238dcc3SJonas Devlieghere                "error: invalid target, create a target using the 'target create' command"
222238dcc3SJonas Devlieghere            ],
232238dcc3SJonas Devlieghere            error=True,
242238dcc3SJonas Devlieghere        )
25602497d6SWalter Erquinigo
26602497d6SWalter Erquinigo        # We now check the output when there's a non-running target
272238dcc3SJonas Devlieghere        self.expect(
282238dcc3SJonas Devlieghere            "target create "
292238dcc3SJonas Devlieghere            + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")
302238dcc3SJonas Devlieghere        )
31602497d6SWalter Erquinigo
322238dcc3SJonas Devlieghere        self.expect(
332238dcc3SJonas Devlieghere            "trace save",
34b7d525adSWalter Erquinigo            substrs=["error: Command requires a current process."],
352238dcc3SJonas Devlieghere            error=True,
362238dcc3SJonas Devlieghere        )
37602497d6SWalter Erquinigo
38602497d6SWalter Erquinigo        # Now we check the output when there's a running target without a trace
39602497d6SWalter Erquinigo        self.expect("b main")
40602497d6SWalter Erquinigo        self.expect("run")
41602497d6SWalter Erquinigo
422238dcc3SJonas Devlieghere        self.expect(
432238dcc3SJonas Devlieghere            "trace save", substrs=["error: Process is not being traced"], error=True
442238dcc3SJonas Devlieghere        )
45602497d6SWalter Erquinigo
46602497d6SWalter Erquinigo    def testSaveToInvalidDir(self):
472238dcc3SJonas Devlieghere        self.expect(
482238dcc3SJonas Devlieghere            "target create "
492238dcc3SJonas Devlieghere            + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")
502238dcc3SJonas Devlieghere        )
51602497d6SWalter Erquinigo        self.expect("b main")
52602497d6SWalter Erquinigo        self.expect("r")
53602497d6SWalter Erquinigo        self.expect("thread trace start")
54602497d6SWalter Erquinigo        self.expect("n")
55602497d6SWalter Erquinigo
56602497d6SWalter Erquinigo        # Check the output when saving without providing the directory argument
572238dcc3SJonas Devlieghere        self.expect(
582238dcc3SJonas Devlieghere            "trace save ",
592238dcc3SJonas Devlieghere            substrs=[
602238dcc3SJonas Devlieghere                "error: a single path to a directory where the trace bundle will be created is required"
612238dcc3SJonas Devlieghere            ],
622238dcc3SJonas Devlieghere            error=True,
632238dcc3SJonas Devlieghere        )
64602497d6SWalter Erquinigo
65602497d6SWalter Erquinigo        # Check the output when saving to an invalid directory
662238dcc3SJonas Devlieghere        self.expect(
672238dcc3SJonas Devlieghere            "trace save /", substrs=["error: couldn't write to the file"], error=True
682238dcc3SJonas Devlieghere        )
69602497d6SWalter Erquinigo
70602497d6SWalter Erquinigo    def testSaveWhenNotLiveTrace(self):
712238dcc3SJonas Devlieghere        self.expect(
722238dcc3SJonas Devlieghere            "trace load -v "
732238dcc3SJonas Devlieghere            + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
742238dcc3SJonas Devlieghere            substrs=["intel-pt"],
752238dcc3SJonas Devlieghere        )
76602497d6SWalter Erquinigo
77602497d6SWalter Erquinigo        # Check the output when not doing live tracing
782238dcc3SJonas Devlieghere        self.expect(
792238dcc3SJonas Devlieghere            "trace save "
802238dcc3SJonas Devlieghere            + os.path.join(self.getBuildDir(), "intelpt-trace", "trace_not_live_dir")
812238dcc3SJonas Devlieghere        )
82602497d6SWalter Erquinigo
836a5355e8SWalter Erquinigo    def testSaveMultiCpuTrace(self):
842238dcc3SJonas Devlieghere        """
856a5355e8SWalter Erquinigo        This test starts a per-cpu tracing session, then saves the session to disk, and
86fc5ef57cSWalter Erquinigo        finally it loads it again.
872238dcc3SJonas Devlieghere        """
886a5355e8SWalter Erquinigo        self.skipIfPerCpuTracingIsNotSupported()
89fc5ef57cSWalter Erquinigo
902238dcc3SJonas Devlieghere        self.expect(
912238dcc3SJonas Devlieghere            "target create "
922238dcc3SJonas Devlieghere            + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")
932238dcc3SJonas Devlieghere        )
94fc5ef57cSWalter Erquinigo        self.expect("b main")
95fc5ef57cSWalter Erquinigo        self.expect("r")
966a5355e8SWalter Erquinigo        self.expect("process trace start --per-cpu-tracing")
97fc5ef57cSWalter Erquinigo        self.expect("b 7")
98fc5ef57cSWalter Erquinigo
99fc5ef57cSWalter Erquinigo        output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save")
100b532dd54SWalter Erquinigo        self.expect("trace save " + output_dir)
101fc5ef57cSWalter Erquinigo
102fc5ef57cSWalter Erquinigo        def checkSessionBundle(session_file_path):
103fc5ef57cSWalter Erquinigo            with open(session_file_path) as session_file:
104fc5ef57cSWalter Erquinigo                session = json.load(session_file)
105fc5ef57cSWalter Erquinigo                # We expect tsc conversion info
1069c246882SJordan Rupprecht                self.assertIn("tscPerfZeroConversion", session)
1076a5355e8SWalter Erquinigo                # We expect at least one cpu
1086a5355e8SWalter Erquinigo                self.assertGreater(len(session["cpus"]), 0)
109fc5ef57cSWalter Erquinigo
110fc5ef57cSWalter Erquinigo                # We expect the required trace files to be created
1116a5355e8SWalter Erquinigo                for cpu in session["cpus"]:
1126a5355e8SWalter Erquinigo                    cpu_files_prefix = os.path.join(output_dir, "cpus", str(cpu["id"]))
1136a5355e8SWalter Erquinigo                    self.assertTrue(os.path.exists(cpu_files_prefix + ".intelpt_trace"))
1142238dcc3SJonas Devlieghere                    self.assertTrue(
1152238dcc3SJonas Devlieghere                        os.path.exists(cpu_files_prefix + ".perf_context_switch_trace")
1162238dcc3SJonas Devlieghere                    )
117fc5ef57cSWalter Erquinigo
118fc5ef57cSWalter Erquinigo                # We expect at least one one process
119fc5ef57cSWalter Erquinigo                self.assertGreater(len(session["processes"]), 0)
120fc5ef57cSWalter Erquinigo                for process in session["processes"]:
121fc5ef57cSWalter Erquinigo                    # We expect at least one thread
122fc5ef57cSWalter Erquinigo                    self.assertGreater(len(process["threads"]), 0)
123fc5ef57cSWalter Erquinigo                    # We don't expect thread traces
124fc5ef57cSWalter Erquinigo                    for thread in process["threads"]:
1252238dcc3SJonas Devlieghere                        self.assertTrue(
1262238dcc3SJonas Devlieghere                            ("iptTrace" not in thread) or (thread["iptTrace"] is None)
1272238dcc3SJonas Devlieghere                        )
128fc5ef57cSWalter Erquinigo
129fc5ef57cSWalter Erquinigo        original_trace_session_file = os.path.join(output_dir, "trace.json")
130fc5ef57cSWalter Erquinigo        checkSessionBundle(original_trace_session_file)
131fc5ef57cSWalter Erquinigo
132fc5ef57cSWalter Erquinigo        output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save")
133fc5ef57cSWalter Erquinigo        self.expect("trace load " + os.path.join(output_dir, "trace.json"))
1342238dcc3SJonas Devlieghere        output_copy_dir = os.path.join(
1352238dcc3SJonas Devlieghere            self.getBuildDir(), "intelpt-trace", "copy_trace_save"
1362238dcc3SJonas Devlieghere        )
137b532dd54SWalter Erquinigo        self.expect("trace save " + output_copy_dir)
138fc5ef57cSWalter Erquinigo
139fc5ef57cSWalter Erquinigo        # We now check that the new bundle is correct on its own
140fc5ef57cSWalter Erquinigo        copied_trace_session_file = os.path.join(output_copy_dir, "trace.json")
141fc5ef57cSWalter Erquinigo        checkSessionBundle(copied_trace_session_file)
142fc5ef57cSWalter Erquinigo
143fc5ef57cSWalter Erquinigo        # We finally check that the new bundle has the same information as the original one
144fc5ef57cSWalter Erquinigo        with open(original_trace_session_file) as original_file:
145fc5ef57cSWalter Erquinigo            original = json.load(original_file)
146fc5ef57cSWalter Erquinigo            with open(copied_trace_session_file) as copy_file:
147fc5ef57cSWalter Erquinigo                copy = json.load(copy_file)
148fc5ef57cSWalter Erquinigo
149fc5ef57cSWalter Erquinigo                self.assertEqual(len(original["processes"]), len(copy["processes"]))
150fc5ef57cSWalter Erquinigo
151fc5ef57cSWalter Erquinigo                for process in original["processes"]:
1522238dcc3SJonas Devlieghere                    copied_process = find(
1532238dcc3SJonas Devlieghere                        lambda proc: proc["pid"] == process["pid"], copy["processes"]
1542238dcc3SJonas Devlieghere                    )
1559c246882SJordan Rupprecht                    self.assertIsNotNone(copied_process)
156fc5ef57cSWalter Erquinigo
157fc5ef57cSWalter Erquinigo                    for thread in process["threads"]:
1582238dcc3SJonas Devlieghere                        copied_thread = find(
1592238dcc3SJonas Devlieghere                            lambda thr: thr["tid"] == thread["tid"],
1602238dcc3SJonas Devlieghere                            copied_process["threads"],
1612238dcc3SJonas Devlieghere                        )
1629c246882SJordan Rupprecht                        self.assertIsNotNone(copied_thread)
163fc5ef57cSWalter Erquinigo
1646a5355e8SWalter Erquinigo                for cpu in original["cpus"]:
1656a5355e8SWalter Erquinigo                    copied_cpu = find(lambda cor: cor["id"] == cpu["id"], copy["cpus"])
1669c246882SJordan Rupprecht                    self.assertIsNotNone(copied_cpu)
167602497d6SWalter Erquinigo
168602497d6SWalter Erquinigo    def testSaveTrace(self):
1692238dcc3SJonas Devlieghere        self.expect(
1702238dcc3SJonas Devlieghere            "target create "
1712238dcc3SJonas Devlieghere            + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")
1722238dcc3SJonas Devlieghere        )
173602497d6SWalter Erquinigo        self.expect("b main")
174602497d6SWalter Erquinigo        self.expect("r")
175602497d6SWalter Erquinigo        self.expect("thread trace start")
176602497d6SWalter Erquinigo        self.expect("b 7")
177602497d6SWalter Erquinigo
178602497d6SWalter Erquinigo        ci = self.dbg.GetCommandInterpreter()
179602497d6SWalter Erquinigo        res = lldb.SBCommandReturnObject()
180602497d6SWalter Erquinigo
181602497d6SWalter Erquinigo        ci.HandleCommand("thread trace dump instructions -c 10 --forwards", res)
182*1eeeab82SJordan Rupprecht        self.assertTrue(res.Succeeded())
183602497d6SWalter Erquinigo        first_ten_instructions = res.GetOutput()
184602497d6SWalter Erquinigo
185602497d6SWalter Erquinigo        ci.HandleCommand("thread trace dump instructions -c 10", res)
186*1eeeab82SJordan Rupprecht        self.assertTrue(res.Succeeded())
187602497d6SWalter Erquinigo        last_ten_instructions = res.GetOutput()
188602497d6SWalter Erquinigo
189602497d6SWalter Erquinigo        # Now, save the trace to <trace_copy_dir>
1902238dcc3SJonas Devlieghere        self.expect(
1912238dcc3SJonas Devlieghere            "trace save "
1922238dcc3SJonas Devlieghere            + os.path.join(self.getBuildDir(), "intelpt-trace", "trace_copy_dir")
1932238dcc3SJonas Devlieghere        )
194602497d6SWalter Erquinigo
195602497d6SWalter Erquinigo        # Load the trace just saved
1962238dcc3SJonas Devlieghere        self.expect(
1972238dcc3SJonas Devlieghere            "trace load -v "
1982238dcc3SJonas Devlieghere            + os.path.join(
1992238dcc3SJonas Devlieghere                self.getBuildDir(), "intelpt-trace", "trace_copy_dir", "trace.json"
2002238dcc3SJonas Devlieghere            ),
2012238dcc3SJonas Devlieghere            substrs=["intel-pt"],
2022238dcc3SJonas Devlieghere        )
203602497d6SWalter Erquinigo
204602497d6SWalter Erquinigo        # Compare with instructions saved at the first time
205602497d6SWalter Erquinigo        ci.HandleCommand("thread trace dump instructions -c 10 --forwards", res)
206*1eeeab82SJordan Rupprecht        self.assertTrue(res.Succeeded())
207602497d6SWalter Erquinigo        self.assertEqual(res.GetOutput(), first_ten_instructions)
208602497d6SWalter Erquinigo
209602497d6SWalter Erquinigo        ci.HandleCommand("thread trace dump instructions -c 10", res)
210*1eeeab82SJordan Rupprecht        self.assertTrue(res.Succeeded())
211602497d6SWalter Erquinigo        self.assertEqual(res.GetOutput(), last_ten_instructions)
2126fb744beSWalter Erquinigo
2136fb744beSWalter Erquinigo    def testSaveKernelTrace(self):
2142238dcc3SJonas Devlieghere        original_trace_file = os.path.join(
2152238dcc3SJonas Devlieghere            self.getSourceDir(), "intelpt-kernel-trace", "trace.json"
2162238dcc3SJonas Devlieghere        )
2176fb744beSWalter Erquinigo        copied_trace_dir = os.path.join(self.getBuildDir(), "intelpt-kernel-trace")
2186fb744beSWalter Erquinigo        copied_trace_file = os.path.join(copied_trace_dir, "trace.json")
2196fb744beSWalter Erquinigo
2206fb744beSWalter Erquinigo        self.expect("trace load -v " + original_trace_file, substrs=["intel-pt"])
2216fb744beSWalter Erquinigo        self.expect("trace save " + copied_trace_dir)
2226fb744beSWalter Erquinigo
2236fb744beSWalter Erquinigo        # We finally check that the new json has the same information as the original one
2246fb744beSWalter Erquinigo        with open(original_trace_file) as original_file:
2256fb744beSWalter Erquinigo            original_file = json.load(original_file)
2266fb744beSWalter Erquinigo            with open(copied_trace_file) as copy_file:
2276fb744beSWalter Erquinigo                copy_file = json.load(copy_file)
2289c246882SJordan Rupprecht                self.assertIn("kernel", copy_file)
2296fb744beSWalter Erquinigo
2302238dcc3SJonas Devlieghere                self.assertEqual(
2312238dcc3SJonas Devlieghere                    os.path.basename(original_file["kernel"]["file"]),
2322238dcc3SJonas Devlieghere                    os.path.basename(copy_file["kernel"]["file"]),
2332238dcc3SJonas Devlieghere                )
234