xref: /freebsd-src/contrib/llvm-project/lldb/source/Target/TraceDumper.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
181ad6265SDimitry Andric //===-- TraceDumper.cpp ---------------------------------------------------===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric 
981ad6265SDimitry Andric #include "lldb/Target/TraceDumper.h"
1081ad6265SDimitry Andric #include "lldb/Core/Module.h"
1181ad6265SDimitry Andric #include "lldb/Symbol/CompileUnit.h"
1281ad6265SDimitry Andric #include "lldb/Symbol/Function.h"
1381ad6265SDimitry Andric #include "lldb/Target/ExecutionContext.h"
1481ad6265SDimitry Andric #include "lldb/Target/Process.h"
1581ad6265SDimitry Andric #include "lldb/Target/SectionLoadList.h"
16bdd1243dSDimitry Andric #include <optional>
1781ad6265SDimitry Andric 
1881ad6265SDimitry Andric using namespace lldb;
1981ad6265SDimitry Andric using namespace lldb_private;
2081ad6265SDimitry Andric using namespace llvm;
2181ad6265SDimitry Andric 
2281ad6265SDimitry Andric /// \return
23bdd1243dSDimitry Andric ///   The given string or \b std::nullopt if it's empty.
24bdd1243dSDimitry Andric static std::optional<const char *> ToOptionalString(const char *s) {
2581ad6265SDimitry Andric   if (!s)
26bdd1243dSDimitry Andric     return std::nullopt;
2781ad6265SDimitry Andric   return s;
2881ad6265SDimitry Andric }
29bdd1243dSDimitry Andric 
30bdd1243dSDimitry Andric static const char *GetModuleName(const SymbolContext &sc) {
31bdd1243dSDimitry Andric   if (!sc.module_sp)
32bdd1243dSDimitry Andric     return nullptr;
33bdd1243dSDimitry Andric   return sc.module_sp->GetFileSpec().GetFilename().AsCString();
34bdd1243dSDimitry Andric }
35bdd1243dSDimitry Andric 
3681ad6265SDimitry Andric /// \return
3781ad6265SDimitry Andric ///   The module name (basename if the module is a file, or the actual name if
3881ad6265SDimitry Andric ///   it's a virtual module), or \b nullptr if no name nor module was found.
3981ad6265SDimitry Andric static const char *GetModuleName(const TraceDumper::TraceItem &item) {
40bdd1243dSDimitry Andric   if (!item.symbol_info)
4181ad6265SDimitry Andric     return nullptr;
42bdd1243dSDimitry Andric   return GetModuleName(item.symbol_info->sc);
4381ad6265SDimitry Andric }
4481ad6265SDimitry Andric 
4581ad6265SDimitry Andric // This custom LineEntry validator is neded because some line_entries have
4681ad6265SDimitry Andric // 0 as line, which is meaningless. Notice that LineEntry::IsValid only
4781ad6265SDimitry Andric // checks that line is not LLDB_INVALID_LINE_NUMBER, i.e. UINT32_MAX.
4881ad6265SDimitry Andric static bool IsLineEntryValid(const LineEntry &line_entry) {
4981ad6265SDimitry Andric   return line_entry.IsValid() && line_entry.line > 0;
5081ad6265SDimitry Andric }
5181ad6265SDimitry Andric 
5281ad6265SDimitry Andric /// \return
5381ad6265SDimitry Andric ///     \b true if the provided line entries match line, column and source file.
5481ad6265SDimitry Andric ///     This function assumes that the line entries are valid.
5581ad6265SDimitry Andric static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) {
5681ad6265SDimitry Andric   if (a.line != b.line)
5781ad6265SDimitry Andric     return false;
5881ad6265SDimitry Andric   if (a.column != b.column)
5981ad6265SDimitry Andric     return false;
60*0fca6ea1SDimitry Andric   return a.GetFile() == b.GetFile();
6181ad6265SDimitry Andric }
6281ad6265SDimitry Andric 
6381ad6265SDimitry Andric /// Compare the symbol contexts of the provided \a SymbolInfo
6481ad6265SDimitry Andric /// objects.
6581ad6265SDimitry Andric ///
6681ad6265SDimitry Andric /// \return
6781ad6265SDimitry Andric ///     \a true if both instructions belong to the same scope level analized
6881ad6265SDimitry Andric ///     in the following order:
6981ad6265SDimitry Andric ///       - module
7081ad6265SDimitry Andric ///       - symbol
7181ad6265SDimitry Andric ///       - function
72bdd1243dSDimitry Andric ///       - inlined function
73bdd1243dSDimitry Andric ///       - source line info
7481ad6265SDimitry Andric static bool
7581ad6265SDimitry Andric IsSameInstructionSymbolContext(const TraceDumper::SymbolInfo &prev_insn,
76bdd1243dSDimitry Andric                                const TraceDumper::SymbolInfo &insn,
77bdd1243dSDimitry Andric                                bool check_source_line_info = true) {
7881ad6265SDimitry Andric   // module checks
7981ad6265SDimitry Andric   if (insn.sc.module_sp != prev_insn.sc.module_sp)
8081ad6265SDimitry Andric     return false;
8181ad6265SDimitry Andric 
8281ad6265SDimitry Andric   // symbol checks
8381ad6265SDimitry Andric   if (insn.sc.symbol != prev_insn.sc.symbol)
8481ad6265SDimitry Andric     return false;
8581ad6265SDimitry Andric 
8681ad6265SDimitry Andric   // function checks
8781ad6265SDimitry Andric   if (!insn.sc.function && !prev_insn.sc.function)
88bdd1243dSDimitry Andric     return true; // This means two dangling instruction in the same module. We
89bdd1243dSDimitry Andric                  // can assume they are part of the same unnamed symbol
9081ad6265SDimitry Andric   else if (insn.sc.function != prev_insn.sc.function)
9181ad6265SDimitry Andric     return false;
9281ad6265SDimitry Andric 
93bdd1243dSDimitry Andric   Block *inline_block_a =
94bdd1243dSDimitry Andric       insn.sc.block ? insn.sc.block->GetContainingInlinedBlock() : nullptr;
95bdd1243dSDimitry Andric   Block *inline_block_b = prev_insn.sc.block
96bdd1243dSDimitry Andric                               ? prev_insn.sc.block->GetContainingInlinedBlock()
97bdd1243dSDimitry Andric                               : nullptr;
98bdd1243dSDimitry Andric   if (inline_block_a != inline_block_b)
99bdd1243dSDimitry Andric     return false;
100bdd1243dSDimitry Andric 
10181ad6265SDimitry Andric   // line entry checks
102bdd1243dSDimitry Andric   if (!check_source_line_info)
103bdd1243dSDimitry Andric     return true;
104bdd1243dSDimitry Andric 
10581ad6265SDimitry Andric   const bool curr_line_valid = IsLineEntryValid(insn.sc.line_entry);
10681ad6265SDimitry Andric   const bool prev_line_valid = IsLineEntryValid(prev_insn.sc.line_entry);
10781ad6265SDimitry Andric   if (curr_line_valid && prev_line_valid)
10881ad6265SDimitry Andric     return FileLineAndColumnMatches(insn.sc.line_entry,
10981ad6265SDimitry Andric                                     prev_insn.sc.line_entry);
11081ad6265SDimitry Andric   return curr_line_valid == prev_line_valid;
11181ad6265SDimitry Andric }
11281ad6265SDimitry Andric 
11381ad6265SDimitry Andric class OutputWriterCLI : public TraceDumper::OutputWriter {
11481ad6265SDimitry Andric public:
11581ad6265SDimitry Andric   OutputWriterCLI(Stream &s, const TraceDumperOptions &options, Thread &thread)
11681ad6265SDimitry Andric       : m_s(s), m_options(options) {
11781ad6265SDimitry Andric     m_s.Format("thread #{0}: tid = {1}\n", thread.GetIndexID(), thread.GetID());
11881ad6265SDimitry Andric   };
11981ad6265SDimitry Andric 
12081ad6265SDimitry Andric   void NoMoreData() override { m_s << "    no more data\n"; }
12181ad6265SDimitry Andric 
122bdd1243dSDimitry Andric   void FunctionCallForest(
123bdd1243dSDimitry Andric       const std::vector<TraceDumper::FunctionCallUP> &forest) override {
124bdd1243dSDimitry Andric     for (size_t i = 0; i < forest.size(); i++) {
125bdd1243dSDimitry Andric       m_s.Format("\n[call tree #{0}]\n", i);
126bdd1243dSDimitry Andric       DumpFunctionCallTree(*forest[i]);
127bdd1243dSDimitry Andric     }
128bdd1243dSDimitry Andric   }
129bdd1243dSDimitry Andric 
13081ad6265SDimitry Andric   void TraceItem(const TraceDumper::TraceItem &item) override {
13181ad6265SDimitry Andric     if (item.symbol_info) {
13281ad6265SDimitry Andric       if (!item.prev_symbol_info ||
13381ad6265SDimitry Andric           !IsSameInstructionSymbolContext(*item.prev_symbol_info,
13481ad6265SDimitry Andric                                           *item.symbol_info)) {
13581ad6265SDimitry Andric         m_s << "  ";
13681ad6265SDimitry Andric         const char *module_name = GetModuleName(item);
13781ad6265SDimitry Andric         if (!module_name)
13881ad6265SDimitry Andric           m_s << "(none)";
13981ad6265SDimitry Andric         else if (!item.symbol_info->sc.function && !item.symbol_info->sc.symbol)
14081ad6265SDimitry Andric           m_s.Format("{0}`(none)", module_name);
14181ad6265SDimitry Andric         else
14281ad6265SDimitry Andric           item.symbol_info->sc.DumpStopContext(
14381ad6265SDimitry Andric               &m_s, item.symbol_info->exe_ctx.GetTargetPtr(),
14481ad6265SDimitry Andric               item.symbol_info->address,
14581ad6265SDimitry Andric               /*show_fullpaths=*/false,
14681ad6265SDimitry Andric               /*show_module=*/true, /*show_inlined_frames=*/false,
14781ad6265SDimitry Andric               /*show_function_arguments=*/true,
14881ad6265SDimitry Andric               /*show_function_name=*/true);
14981ad6265SDimitry Andric         m_s << "\n";
15081ad6265SDimitry Andric       }
15181ad6265SDimitry Andric     }
15281ad6265SDimitry Andric 
15381ad6265SDimitry Andric     if (item.error && !m_was_prev_instruction_an_error)
15481ad6265SDimitry Andric       m_s << "    ...missing instructions\n";
15581ad6265SDimitry Andric 
15681ad6265SDimitry Andric     m_s.Format("    {0}: ", item.id);
15781ad6265SDimitry Andric 
158972a253aSDimitry Andric     if (m_options.show_timestamps) {
159972a253aSDimitry Andric       m_s.Format("[{0}] ", item.timestamp
160972a253aSDimitry Andric                                ? formatv("{0:3} ns", *item.timestamp).str()
161972a253aSDimitry Andric                                : "unavailable");
16281ad6265SDimitry Andric     }
16381ad6265SDimitry Andric 
16481ad6265SDimitry Andric     if (item.event) {
16581ad6265SDimitry Andric       m_s << "(event) " << TraceCursor::EventKindToString(*item.event);
166972a253aSDimitry Andric       switch (*item.event) {
167972a253aSDimitry Andric       case eTraceEventCPUChanged:
168753f127fSDimitry Andric         m_s.Format(" [new CPU={0}]",
169753f127fSDimitry Andric                    item.cpu_id ? std::to_string(*item.cpu_id) : "unavailable");
170972a253aSDimitry Andric         break;
171972a253aSDimitry Andric       case eTraceEventHWClockTick:
172972a253aSDimitry Andric         m_s.Format(" [{0}]", item.hw_clock ? std::to_string(*item.hw_clock)
173972a253aSDimitry Andric                                            : "unavailable");
174972a253aSDimitry Andric         break;
175972a253aSDimitry Andric       case eTraceEventDisabledHW:
176972a253aSDimitry Andric       case eTraceEventDisabledSW:
177972a253aSDimitry Andric         break;
178bdd1243dSDimitry Andric       case eTraceEventSyncPoint:
179bdd1243dSDimitry Andric         m_s.Format(" [{0}]", item.sync_point_metadata);
180bdd1243dSDimitry Andric         break;
181753f127fSDimitry Andric       }
18281ad6265SDimitry Andric     } else if (item.error) {
18381ad6265SDimitry Andric       m_s << "(error) " << *item.error;
18481ad6265SDimitry Andric     } else {
18581ad6265SDimitry Andric       m_s.Format("{0:x+16}", item.load_address);
186753f127fSDimitry Andric       if (item.symbol_info && item.symbol_info->instruction) {
18781ad6265SDimitry Andric         m_s << "    ";
188753f127fSDimitry Andric         item.symbol_info->instruction->Dump(
189753f127fSDimitry Andric             &m_s, /*max_opcode_byte_size=*/0,
19081ad6265SDimitry Andric             /*show_address=*/false,
191753f127fSDimitry Andric             /*show_bytes=*/false, m_options.show_control_flow_kind,
192753f127fSDimitry Andric             &item.symbol_info->exe_ctx, &item.symbol_info->sc,
19381ad6265SDimitry Andric             /*prev_sym_ctx=*/nullptr,
19481ad6265SDimitry Andric             /*disassembly_addr_format=*/nullptr,
19581ad6265SDimitry Andric             /*max_address_text_size=*/0);
19681ad6265SDimitry Andric       }
19781ad6265SDimitry Andric     }
19881ad6265SDimitry Andric 
19981ad6265SDimitry Andric     m_was_prev_instruction_an_error = (bool)item.error;
20081ad6265SDimitry Andric     m_s << "\n";
20181ad6265SDimitry Andric   }
20281ad6265SDimitry Andric 
20381ad6265SDimitry Andric private:
204bdd1243dSDimitry Andric   void
205bdd1243dSDimitry Andric   DumpSegmentContext(const TraceDumper::FunctionCall::TracedSegment &segment) {
206bdd1243dSDimitry Andric     if (segment.GetOwningCall().IsError()) {
207bdd1243dSDimitry Andric       m_s << "<tracing errors>";
208bdd1243dSDimitry Andric       return;
209bdd1243dSDimitry Andric     }
210bdd1243dSDimitry Andric 
211bdd1243dSDimitry Andric     const SymbolContext &first_sc = segment.GetFirstInstructionSymbolInfo().sc;
212bdd1243dSDimitry Andric     first_sc.DumpStopContext(
213bdd1243dSDimitry Andric         &m_s, segment.GetFirstInstructionSymbolInfo().exe_ctx.GetTargetPtr(),
214bdd1243dSDimitry Andric         segment.GetFirstInstructionSymbolInfo().address,
215bdd1243dSDimitry Andric         /*show_fullpaths=*/false,
216bdd1243dSDimitry Andric         /*show_module=*/true, /*show_inlined_frames=*/false,
217bdd1243dSDimitry Andric         /*show_function_arguments=*/true,
218bdd1243dSDimitry Andric         /*show_function_name=*/true);
219bdd1243dSDimitry Andric     m_s << " to ";
220bdd1243dSDimitry Andric     const SymbolContext &last_sc = segment.GetLastInstructionSymbolInfo().sc;
221bdd1243dSDimitry Andric     if (IsLineEntryValid(first_sc.line_entry) &&
222bdd1243dSDimitry Andric         IsLineEntryValid(last_sc.line_entry)) {
223bdd1243dSDimitry Andric       m_s.Format("{0}:{1}", last_sc.line_entry.line, last_sc.line_entry.column);
224bdd1243dSDimitry Andric     } else {
225bdd1243dSDimitry Andric       last_sc.DumpStopContext(
226bdd1243dSDimitry Andric           &m_s, segment.GetFirstInstructionSymbolInfo().exe_ctx.GetTargetPtr(),
227bdd1243dSDimitry Andric           segment.GetLastInstructionSymbolInfo().address,
228bdd1243dSDimitry Andric           /*show_fullpaths=*/false,
229bdd1243dSDimitry Andric           /*show_module=*/false, /*show_inlined_frames=*/false,
230bdd1243dSDimitry Andric           /*show_function_arguments=*/false,
231bdd1243dSDimitry Andric           /*show_function_name=*/false);
232bdd1243dSDimitry Andric     }
233bdd1243dSDimitry Andric   }
234bdd1243dSDimitry Andric 
235bdd1243dSDimitry Andric   void DumpUntracedContext(const TraceDumper::FunctionCall &function_call) {
236bdd1243dSDimitry Andric     if (function_call.IsError()) {
237bdd1243dSDimitry Andric       m_s << "tracing error";
238bdd1243dSDimitry Andric     }
239bdd1243dSDimitry Andric     const SymbolContext &sc = function_call.GetSymbolInfo().sc;
240bdd1243dSDimitry Andric 
241bdd1243dSDimitry Andric     const char *module_name = GetModuleName(sc);
242bdd1243dSDimitry Andric     if (!module_name)
243bdd1243dSDimitry Andric       m_s << "(none)";
244bdd1243dSDimitry Andric     else if (!sc.function && !sc.symbol)
245bdd1243dSDimitry Andric       m_s << module_name << "`(none)";
246bdd1243dSDimitry Andric     else
247bdd1243dSDimitry Andric       m_s << module_name << "`" << sc.GetFunctionName().AsCString();
248bdd1243dSDimitry Andric   }
249bdd1243dSDimitry Andric 
250bdd1243dSDimitry Andric   void DumpFunctionCallTree(const TraceDumper::FunctionCall &function_call) {
251bdd1243dSDimitry Andric     if (function_call.GetUntracedPrefixSegment()) {
252bdd1243dSDimitry Andric       m_s.Indent();
253bdd1243dSDimitry Andric       DumpUntracedContext(function_call);
254bdd1243dSDimitry Andric       m_s << "\n";
255bdd1243dSDimitry Andric 
256bdd1243dSDimitry Andric       m_s.IndentMore();
257bdd1243dSDimitry Andric       DumpFunctionCallTree(function_call.GetUntracedPrefixSegment()->GetNestedCall());
258bdd1243dSDimitry Andric       m_s.IndentLess();
259bdd1243dSDimitry Andric     }
260bdd1243dSDimitry Andric 
261bdd1243dSDimitry Andric     for (const TraceDumper::FunctionCall::TracedSegment &segment :
262bdd1243dSDimitry Andric          function_call.GetTracedSegments()) {
263bdd1243dSDimitry Andric       m_s.Indent();
264bdd1243dSDimitry Andric       DumpSegmentContext(segment);
265bdd1243dSDimitry Andric       m_s.Format("  [{0}, {1}]\n", segment.GetFirstInstructionID(),
266bdd1243dSDimitry Andric                  segment.GetLastInstructionID());
267bdd1243dSDimitry Andric 
268bdd1243dSDimitry Andric       segment.IfNestedCall([&](const TraceDumper::FunctionCall &nested_call) {
269bdd1243dSDimitry Andric         m_s.IndentMore();
270bdd1243dSDimitry Andric         DumpFunctionCallTree(nested_call);
271bdd1243dSDimitry Andric         m_s.IndentLess();
272bdd1243dSDimitry Andric       });
273bdd1243dSDimitry Andric     }
274bdd1243dSDimitry Andric   }
275bdd1243dSDimitry Andric 
27681ad6265SDimitry Andric   Stream &m_s;
27781ad6265SDimitry Andric   TraceDumperOptions m_options;
27881ad6265SDimitry Andric   bool m_was_prev_instruction_an_error = false;
27981ad6265SDimitry Andric };
28081ad6265SDimitry Andric 
28181ad6265SDimitry Andric class OutputWriterJSON : public TraceDumper::OutputWriter {
28281ad6265SDimitry Andric   /* schema:
28381ad6265SDimitry Andric     error_message: string
28481ad6265SDimitry Andric     | {
285753f127fSDimitry Andric       "event": string,
28681ad6265SDimitry Andric       "id": decimal,
28781ad6265SDimitry Andric       "tsc"?: string decimal,
288753f127fSDimitry Andric       "cpuId"? decimal,
28981ad6265SDimitry Andric     } | {
290753f127fSDimitry Andric       "error": string,
29181ad6265SDimitry Andric       "id": decimal,
29281ad6265SDimitry Andric       "tsc"?: string decimal,
29381ad6265SDimitry Andric     | {
294753f127fSDimitry Andric       "loadAddress": string decimal,
29581ad6265SDimitry Andric       "id": decimal,
296972a253aSDimitry Andric       "hwClock"?: string decimal,
297bdd1243dSDimitry Andric       "syncPointMetadata"?: string,
298972a253aSDimitry Andric       "timestamp_ns"?: string decimal,
29981ad6265SDimitry Andric       "module"?: string,
30081ad6265SDimitry Andric       "symbol"?: string,
30181ad6265SDimitry Andric       "line"?: decimal,
30281ad6265SDimitry Andric       "column"?: decimal,
30381ad6265SDimitry Andric       "source"?: string,
30481ad6265SDimitry Andric       "mnemonic"?: string,
305bdd1243dSDimitry Andric       "controlFlowKind"?: string,
30681ad6265SDimitry Andric     }
30781ad6265SDimitry Andric   */
30881ad6265SDimitry Andric public:
30981ad6265SDimitry Andric   OutputWriterJSON(Stream &s, const TraceDumperOptions &options)
31081ad6265SDimitry Andric       : m_s(s), m_options(options),
31181ad6265SDimitry Andric         m_j(m_s.AsRawOstream(),
31281ad6265SDimitry Andric             /*IndentSize=*/options.pretty_print_json ? 2 : 0) {
31381ad6265SDimitry Andric     m_j.arrayBegin();
31481ad6265SDimitry Andric   };
31581ad6265SDimitry Andric 
31681ad6265SDimitry Andric   ~OutputWriterJSON() { m_j.arrayEnd(); }
31781ad6265SDimitry Andric 
318bdd1243dSDimitry Andric   void FunctionCallForest(
319bdd1243dSDimitry Andric       const std::vector<TraceDumper::FunctionCallUP> &forest) override {
320bdd1243dSDimitry Andric     for (size_t i = 0; i < forest.size(); i++) {
321bdd1243dSDimitry Andric       m_j.object([&] { DumpFunctionCallTree(*forest[i]); });
322bdd1243dSDimitry Andric     }
323bdd1243dSDimitry Andric   }
324bdd1243dSDimitry Andric 
325bdd1243dSDimitry Andric   void DumpFunctionCallTree(const TraceDumper::FunctionCall &function_call) {
326bdd1243dSDimitry Andric     if (function_call.GetUntracedPrefixSegment()) {
327bdd1243dSDimitry Andric       m_j.attributeObject("untracedPrefixSegment", [&] {
328bdd1243dSDimitry Andric         m_j.attributeObject("nestedCall", [&] {
329bdd1243dSDimitry Andric           DumpFunctionCallTree(
330bdd1243dSDimitry Andric               function_call.GetUntracedPrefixSegment()->GetNestedCall());
331bdd1243dSDimitry Andric         });
332bdd1243dSDimitry Andric       });
333bdd1243dSDimitry Andric     }
334bdd1243dSDimitry Andric 
335bdd1243dSDimitry Andric     if (!function_call.GetTracedSegments().empty()) {
336bdd1243dSDimitry Andric       m_j.attributeArray("tracedSegments", [&] {
337bdd1243dSDimitry Andric         for (const TraceDumper::FunctionCall::TracedSegment &segment :
338bdd1243dSDimitry Andric              function_call.GetTracedSegments()) {
339bdd1243dSDimitry Andric           m_j.object([&] {
340bdd1243dSDimitry Andric             m_j.attribute("firstInstructionId",
341bdd1243dSDimitry Andric                           std::to_string(segment.GetFirstInstructionID()));
342bdd1243dSDimitry Andric             m_j.attribute("lastInstructionId",
343bdd1243dSDimitry Andric                           std::to_string(segment.GetLastInstructionID()));
344bdd1243dSDimitry Andric             segment.IfNestedCall(
345bdd1243dSDimitry Andric                 [&](const TraceDumper::FunctionCall &nested_call) {
346bdd1243dSDimitry Andric                   m_j.attributeObject(
347bdd1243dSDimitry Andric                       "nestedCall", [&] { DumpFunctionCallTree(nested_call); });
348bdd1243dSDimitry Andric                 });
349bdd1243dSDimitry Andric           });
350bdd1243dSDimitry Andric         }
351bdd1243dSDimitry Andric       });
352bdd1243dSDimitry Andric     }
353bdd1243dSDimitry Andric   }
354bdd1243dSDimitry Andric 
355753f127fSDimitry Andric   void DumpEvent(const TraceDumper::TraceItem &item) {
35681ad6265SDimitry Andric     m_j.attribute("event", TraceCursor::EventKindToString(*item.event));
357972a253aSDimitry Andric     switch (*item.event) {
358972a253aSDimitry Andric     case eTraceEventCPUChanged:
359753f127fSDimitry Andric       m_j.attribute("cpuId", item.cpu_id);
360972a253aSDimitry Andric       break;
361972a253aSDimitry Andric     case eTraceEventHWClockTick:
362972a253aSDimitry Andric       m_j.attribute("hwClock", item.hw_clock);
363972a253aSDimitry Andric       break;
364972a253aSDimitry Andric     case eTraceEventDisabledHW:
365972a253aSDimitry Andric     case eTraceEventDisabledSW:
366972a253aSDimitry Andric       break;
367bdd1243dSDimitry Andric     case eTraceEventSyncPoint:
368bdd1243dSDimitry Andric       m_j.attribute("syncPointMetadata", item.sync_point_metadata);
369bdd1243dSDimitry Andric       break;
370972a253aSDimitry Andric     }
37181ad6265SDimitry Andric   }
37281ad6265SDimitry Andric 
373753f127fSDimitry Andric   void DumpInstruction(const TraceDumper::TraceItem &item) {
37481ad6265SDimitry Andric     m_j.attribute("loadAddress", formatv("{0:x}", item.load_address));
37581ad6265SDimitry Andric     if (item.symbol_info) {
37681ad6265SDimitry Andric       m_j.attribute("module", ToOptionalString(GetModuleName(item)));
37781ad6265SDimitry Andric       m_j.attribute(
378753f127fSDimitry Andric           "symbol",
379753f127fSDimitry Andric           ToOptionalString(item.symbol_info->sc.GetFunctionName().AsCString()));
380753f127fSDimitry Andric 
381bdd1243dSDimitry Andric       if (lldb::InstructionSP instruction = item.symbol_info->instruction) {
382bdd1243dSDimitry Andric         ExecutionContext exe_ctx = item.symbol_info->exe_ctx;
383753f127fSDimitry Andric         m_j.attribute("mnemonic",
384bdd1243dSDimitry Andric                       ToOptionalString(instruction->GetMnemonic(&exe_ctx)));
385bdd1243dSDimitry Andric         if (m_options.show_control_flow_kind) {
386bdd1243dSDimitry Andric           lldb::InstructionControlFlowKind instruction_control_flow_kind =
387bdd1243dSDimitry Andric               instruction->GetControlFlowKind(&exe_ctx);
388bdd1243dSDimitry Andric           m_j.attribute("controlFlowKind",
389bdd1243dSDimitry Andric                         ToOptionalString(
390bdd1243dSDimitry Andric                             Instruction::GetNameForInstructionControlFlowKind(
391bdd1243dSDimitry Andric                                 instruction_control_flow_kind)));
392bdd1243dSDimitry Andric         }
393753f127fSDimitry Andric       }
39481ad6265SDimitry Andric 
39581ad6265SDimitry Andric       if (IsLineEntryValid(item.symbol_info->sc.line_entry)) {
39681ad6265SDimitry Andric         m_j.attribute(
39781ad6265SDimitry Andric             "source",
39881ad6265SDimitry Andric             ToOptionalString(
399*0fca6ea1SDimitry Andric                 item.symbol_info->sc.line_entry.GetFile().GetPath().c_str()));
40081ad6265SDimitry Andric         m_j.attribute("line", item.symbol_info->sc.line_entry.line);
40181ad6265SDimitry Andric         m_j.attribute("column", item.symbol_info->sc.line_entry.column);
40281ad6265SDimitry Andric       }
40381ad6265SDimitry Andric     }
404753f127fSDimitry Andric   }
405753f127fSDimitry Andric 
406753f127fSDimitry Andric   void TraceItem(const TraceDumper::TraceItem &item) override {
407753f127fSDimitry Andric     m_j.object([&] {
408753f127fSDimitry Andric       m_j.attribute("id", item.id);
409972a253aSDimitry Andric       if (m_options.show_timestamps)
410972a253aSDimitry Andric         m_j.attribute("timestamp_ns", item.timestamp
411bdd1243dSDimitry Andric                                           ? std::optional<std::string>(
412972a253aSDimitry Andric                                                 std::to_string(*item.timestamp))
413bdd1243dSDimitry Andric                                           : std::nullopt);
414753f127fSDimitry Andric 
415753f127fSDimitry Andric       if (item.event) {
416753f127fSDimitry Andric         DumpEvent(item);
417753f127fSDimitry Andric       } else if (item.error) {
418753f127fSDimitry Andric         m_j.attribute("error", *item.error);
419753f127fSDimitry Andric       } else {
420753f127fSDimitry Andric         DumpInstruction(item);
421753f127fSDimitry Andric       }
42281ad6265SDimitry Andric     });
42381ad6265SDimitry Andric   }
42481ad6265SDimitry Andric 
42581ad6265SDimitry Andric private:
42681ad6265SDimitry Andric   Stream &m_s;
42781ad6265SDimitry Andric   TraceDumperOptions m_options;
42881ad6265SDimitry Andric   json::OStream m_j;
42981ad6265SDimitry Andric };
43081ad6265SDimitry Andric 
43181ad6265SDimitry Andric static std::unique_ptr<TraceDumper::OutputWriter>
43281ad6265SDimitry Andric CreateWriter(Stream &s, const TraceDumperOptions &options, Thread &thread) {
43381ad6265SDimitry Andric   if (options.json)
43481ad6265SDimitry Andric     return std::unique_ptr<TraceDumper::OutputWriter>(
43581ad6265SDimitry Andric         new OutputWriterJSON(s, options));
43681ad6265SDimitry Andric   else
43781ad6265SDimitry Andric     return std::unique_ptr<TraceDumper::OutputWriter>(
43881ad6265SDimitry Andric         new OutputWriterCLI(s, options, thread));
43981ad6265SDimitry Andric }
44081ad6265SDimitry Andric 
441bdd1243dSDimitry Andric TraceDumper::TraceDumper(lldb::TraceCursorSP cursor_sp, Stream &s,
44281ad6265SDimitry Andric                          const TraceDumperOptions &options)
443bdd1243dSDimitry Andric     : m_cursor_sp(std::move(cursor_sp)), m_options(options),
44481ad6265SDimitry Andric       m_writer_up(CreateWriter(
445bdd1243dSDimitry Andric           s, m_options, *m_cursor_sp->GetExecutionContextRef().GetThreadSP())) {
44681ad6265SDimitry Andric 
44781ad6265SDimitry Andric   if (m_options.id)
448bdd1243dSDimitry Andric     m_cursor_sp->GoToId(*m_options.id);
44981ad6265SDimitry Andric   else if (m_options.forwards)
450bdd1243dSDimitry Andric     m_cursor_sp->Seek(0, lldb::eTraceCursorSeekTypeBeginning);
45181ad6265SDimitry Andric   else
452bdd1243dSDimitry Andric     m_cursor_sp->Seek(0, lldb::eTraceCursorSeekTypeEnd);
45381ad6265SDimitry Andric 
454bdd1243dSDimitry Andric   m_cursor_sp->SetForwards(m_options.forwards);
45581ad6265SDimitry Andric   if (m_options.skip) {
456bdd1243dSDimitry Andric     m_cursor_sp->Seek((m_options.forwards ? 1 : -1) * *m_options.skip,
457bdd1243dSDimitry Andric                       lldb::eTraceCursorSeekTypeCurrent);
45881ad6265SDimitry Andric   }
45981ad6265SDimitry Andric }
46081ad6265SDimitry Andric 
46181ad6265SDimitry Andric TraceDumper::TraceItem TraceDumper::CreatRawTraceItem() {
462972a253aSDimitry Andric   TraceItem item = {};
463bdd1243dSDimitry Andric   item.id = m_cursor_sp->GetId();
46481ad6265SDimitry Andric 
465972a253aSDimitry Andric   if (m_options.show_timestamps)
466bdd1243dSDimitry Andric     item.timestamp = m_cursor_sp->GetWallClockTime();
46781ad6265SDimitry Andric   return item;
46881ad6265SDimitry Andric }
46981ad6265SDimitry Andric 
47081ad6265SDimitry Andric /// Find the symbol context for the given address reusing the previous
47181ad6265SDimitry Andric /// instruction's symbol context when possible.
47281ad6265SDimitry Andric static SymbolContext
47381ad6265SDimitry Andric CalculateSymbolContext(const Address &address,
474bdd1243dSDimitry Andric                        const SymbolContext &prev_symbol_context) {
4755f757f3fSDimitry Andric   lldb_private::AddressRange range;
476bdd1243dSDimitry Andric   if (prev_symbol_context.GetAddressRange(eSymbolContextEverything, 0,
477bdd1243dSDimitry Andric                                           /*inline_block_range*/ true, range) &&
47881ad6265SDimitry Andric       range.Contains(address))
479bdd1243dSDimitry Andric     return prev_symbol_context;
48081ad6265SDimitry Andric 
48181ad6265SDimitry Andric   SymbolContext sc;
48281ad6265SDimitry Andric   address.CalculateSymbolContext(&sc, eSymbolContextEverything);
48381ad6265SDimitry Andric   return sc;
48481ad6265SDimitry Andric }
48581ad6265SDimitry Andric 
48681ad6265SDimitry Andric /// Find the disassembler for the given address reusing the previous
48781ad6265SDimitry Andric /// instruction's disassembler when possible.
48881ad6265SDimitry Andric static std::tuple<DisassemblerSP, InstructionSP>
48981ad6265SDimitry Andric CalculateDisass(const TraceDumper::SymbolInfo &symbol_info,
49081ad6265SDimitry Andric                 const TraceDumper::SymbolInfo &prev_symbol_info,
49181ad6265SDimitry Andric                 const ExecutionContext &exe_ctx) {
49281ad6265SDimitry Andric   if (prev_symbol_info.disassembler) {
49381ad6265SDimitry Andric     if (InstructionSP instruction =
49481ad6265SDimitry Andric             prev_symbol_info.disassembler->GetInstructionList()
49581ad6265SDimitry Andric                 .GetInstructionAtAddress(symbol_info.address))
49681ad6265SDimitry Andric       return std::make_tuple(prev_symbol_info.disassembler, instruction);
49781ad6265SDimitry Andric   }
49881ad6265SDimitry Andric 
49981ad6265SDimitry Andric   if (symbol_info.sc.function) {
50081ad6265SDimitry Andric     if (DisassemblerSP disassembler =
50181ad6265SDimitry Andric             symbol_info.sc.function->GetInstructions(exe_ctx, nullptr)) {
50281ad6265SDimitry Andric       if (InstructionSP instruction =
50381ad6265SDimitry Andric               disassembler->GetInstructionList().GetInstructionAtAddress(
50481ad6265SDimitry Andric                   symbol_info.address))
50581ad6265SDimitry Andric         return std::make_tuple(disassembler, instruction);
50681ad6265SDimitry Andric     }
50781ad6265SDimitry Andric   }
50881ad6265SDimitry Andric   // We fallback to a single instruction disassembler
50981ad6265SDimitry Andric   Target &target = exe_ctx.GetTargetRef();
51081ad6265SDimitry Andric   const ArchSpec arch = target.GetArchitecture();
5115f757f3fSDimitry Andric   lldb_private::AddressRange range(symbol_info.address,
5125f757f3fSDimitry Andric                                    arch.GetMaximumOpcodeByteSize());
51381ad6265SDimitry Andric   DisassemblerSP disassembler =
51481ad6265SDimitry Andric       Disassembler::DisassembleRange(arch, /*plugin_name*/ nullptr,
51581ad6265SDimitry Andric                                      /*flavor*/ nullptr, target, range);
51681ad6265SDimitry Andric   return std::make_tuple(
51781ad6265SDimitry Andric       disassembler,
51881ad6265SDimitry Andric       disassembler ? disassembler->GetInstructionList().GetInstructionAtAddress(
51981ad6265SDimitry Andric                          symbol_info.address)
52081ad6265SDimitry Andric                    : InstructionSP());
52181ad6265SDimitry Andric }
52281ad6265SDimitry Andric 
523bdd1243dSDimitry Andric static TraceDumper::SymbolInfo
524bdd1243dSDimitry Andric CalculateSymbolInfo(const ExecutionContext &exe_ctx, lldb::addr_t load_address,
525bdd1243dSDimitry Andric                     const TraceDumper::SymbolInfo &prev_symbol_info) {
526bdd1243dSDimitry Andric   TraceDumper::SymbolInfo symbol_info;
527bdd1243dSDimitry Andric   symbol_info.exe_ctx = exe_ctx;
528bdd1243dSDimitry Andric   symbol_info.address.SetLoadAddress(load_address, exe_ctx.GetTargetPtr());
529bdd1243dSDimitry Andric   symbol_info.sc =
530bdd1243dSDimitry Andric       CalculateSymbolContext(symbol_info.address, prev_symbol_info.sc);
531bdd1243dSDimitry Andric   std::tie(symbol_info.disassembler, symbol_info.instruction) =
532bdd1243dSDimitry Andric       CalculateDisass(symbol_info, prev_symbol_info, exe_ctx);
533bdd1243dSDimitry Andric   return symbol_info;
534bdd1243dSDimitry Andric }
535bdd1243dSDimitry Andric 
536bdd1243dSDimitry Andric std::optional<lldb::user_id_t> TraceDumper::DumpInstructions(size_t count) {
537bdd1243dSDimitry Andric   ThreadSP thread_sp = m_cursor_sp->GetExecutionContextRef().GetThreadSP();
53881ad6265SDimitry Andric 
53981ad6265SDimitry Andric   SymbolInfo prev_symbol_info;
540bdd1243dSDimitry Andric   std::optional<lldb::user_id_t> last_id;
54181ad6265SDimitry Andric 
54281ad6265SDimitry Andric   ExecutionContext exe_ctx;
54381ad6265SDimitry Andric   thread_sp->GetProcess()->GetTarget().CalculateExecutionContext(exe_ctx);
54481ad6265SDimitry Andric 
545bdd1243dSDimitry Andric   for (size_t insn_seen = 0; insn_seen < count && m_cursor_sp->HasValue();
546bdd1243dSDimitry Andric        m_cursor_sp->Next()) {
54781ad6265SDimitry Andric 
548bdd1243dSDimitry Andric     last_id = m_cursor_sp->GetId();
54981ad6265SDimitry Andric     TraceItem item = CreatRawTraceItem();
55081ad6265SDimitry Andric 
551bdd1243dSDimitry Andric     if (m_cursor_sp->IsEvent() && m_options.show_events) {
552bdd1243dSDimitry Andric       item.event = m_cursor_sp->GetEventType();
553972a253aSDimitry Andric       switch (*item.event) {
554972a253aSDimitry Andric       case eTraceEventCPUChanged:
555bdd1243dSDimitry Andric         item.cpu_id = m_cursor_sp->GetCPU();
556972a253aSDimitry Andric         break;
557972a253aSDimitry Andric       case eTraceEventHWClockTick:
558bdd1243dSDimitry Andric         item.hw_clock = m_cursor_sp->GetHWClock();
559972a253aSDimitry Andric         break;
560972a253aSDimitry Andric       case eTraceEventDisabledHW:
561972a253aSDimitry Andric       case eTraceEventDisabledSW:
562972a253aSDimitry Andric         break;
563bdd1243dSDimitry Andric       case eTraceEventSyncPoint:
564bdd1243dSDimitry Andric         item.sync_point_metadata = m_cursor_sp->GetSyncPointMetadata();
565bdd1243dSDimitry Andric         break;
566972a253aSDimitry Andric       }
567bdd1243dSDimitry Andric       m_writer_up->TraceItem(item);
568bdd1243dSDimitry Andric     } else if (m_cursor_sp->IsError()) {
569bdd1243dSDimitry Andric       item.error = m_cursor_sp->GetError();
570bdd1243dSDimitry Andric       m_writer_up->TraceItem(item);
571bdd1243dSDimitry Andric     } else if (m_cursor_sp->IsInstruction() && !m_options.only_events) {
57281ad6265SDimitry Andric       insn_seen++;
573bdd1243dSDimitry Andric       item.load_address = m_cursor_sp->GetLoadAddress();
57481ad6265SDimitry Andric 
57581ad6265SDimitry Andric       if (!m_options.raw) {
576bdd1243dSDimitry Andric         SymbolInfo symbol_info =
577bdd1243dSDimitry Andric             CalculateSymbolInfo(exe_ctx, item.load_address, prev_symbol_info);
57881ad6265SDimitry Andric         item.prev_symbol_info = prev_symbol_info;
57981ad6265SDimitry Andric         item.symbol_info = symbol_info;
58081ad6265SDimitry Andric         prev_symbol_info = symbol_info;
58181ad6265SDimitry Andric       }
58281ad6265SDimitry Andric       m_writer_up->TraceItem(item);
58381ad6265SDimitry Andric     }
584bdd1243dSDimitry Andric   }
585bdd1243dSDimitry Andric   if (!m_cursor_sp->HasValue())
58681ad6265SDimitry Andric     m_writer_up->NoMoreData();
58781ad6265SDimitry Andric   return last_id;
58881ad6265SDimitry Andric }
589bdd1243dSDimitry Andric 
590bdd1243dSDimitry Andric void TraceDumper::FunctionCall::TracedSegment::AppendInsn(
591bdd1243dSDimitry Andric     const TraceCursorSP &cursor_sp,
592bdd1243dSDimitry Andric     const TraceDumper::SymbolInfo &symbol_info) {
593bdd1243dSDimitry Andric   m_last_insn_id = cursor_sp->GetId();
594bdd1243dSDimitry Andric   m_last_symbol_info = symbol_info;
595bdd1243dSDimitry Andric }
596bdd1243dSDimitry Andric 
597bdd1243dSDimitry Andric lldb::user_id_t
598bdd1243dSDimitry Andric TraceDumper::FunctionCall::TracedSegment::GetFirstInstructionID() const {
599bdd1243dSDimitry Andric   return m_first_insn_id;
600bdd1243dSDimitry Andric }
601bdd1243dSDimitry Andric 
602bdd1243dSDimitry Andric lldb::user_id_t
603bdd1243dSDimitry Andric TraceDumper::FunctionCall::TracedSegment::GetLastInstructionID() const {
604bdd1243dSDimitry Andric   return m_last_insn_id;
605bdd1243dSDimitry Andric }
606bdd1243dSDimitry Andric 
607bdd1243dSDimitry Andric void TraceDumper::FunctionCall::TracedSegment::IfNestedCall(
608bdd1243dSDimitry Andric     std::function<void(const FunctionCall &function_call)> callback) const {
609bdd1243dSDimitry Andric   if (m_nested_call)
610bdd1243dSDimitry Andric     callback(*m_nested_call);
611bdd1243dSDimitry Andric }
612bdd1243dSDimitry Andric 
613bdd1243dSDimitry Andric const TraceDumper::FunctionCall &
614bdd1243dSDimitry Andric TraceDumper::FunctionCall::TracedSegment::GetOwningCall() const {
615bdd1243dSDimitry Andric   return m_owning_call;
616bdd1243dSDimitry Andric }
617bdd1243dSDimitry Andric 
618bdd1243dSDimitry Andric TraceDumper::FunctionCall &
619bdd1243dSDimitry Andric TraceDumper::FunctionCall::TracedSegment::CreateNestedCall(
620bdd1243dSDimitry Andric     const TraceCursorSP &cursor_sp,
621bdd1243dSDimitry Andric     const TraceDumper::SymbolInfo &symbol_info) {
622bdd1243dSDimitry Andric   m_nested_call = std::make_unique<FunctionCall>(cursor_sp, symbol_info);
623bdd1243dSDimitry Andric   m_nested_call->SetParentCall(m_owning_call);
624bdd1243dSDimitry Andric   return *m_nested_call;
625bdd1243dSDimitry Andric }
626bdd1243dSDimitry Andric 
627bdd1243dSDimitry Andric const TraceDumper::SymbolInfo &
628bdd1243dSDimitry Andric TraceDumper::FunctionCall::TracedSegment::GetFirstInstructionSymbolInfo()
629bdd1243dSDimitry Andric     const {
630bdd1243dSDimitry Andric   return m_first_symbol_info;
631bdd1243dSDimitry Andric }
632bdd1243dSDimitry Andric 
633bdd1243dSDimitry Andric const TraceDumper::SymbolInfo &
634bdd1243dSDimitry Andric TraceDumper::FunctionCall::TracedSegment::GetLastInstructionSymbolInfo() const {
635bdd1243dSDimitry Andric   return m_last_symbol_info;
636bdd1243dSDimitry Andric }
637bdd1243dSDimitry Andric 
638bdd1243dSDimitry Andric const TraceDumper::FunctionCall &
639bdd1243dSDimitry Andric TraceDumper::FunctionCall::UntracedPrefixSegment::GetNestedCall() const {
640bdd1243dSDimitry Andric   return *m_nested_call;
641bdd1243dSDimitry Andric }
642bdd1243dSDimitry Andric 
643bdd1243dSDimitry Andric TraceDumper::FunctionCall::FunctionCall(
644bdd1243dSDimitry Andric     const TraceCursorSP &cursor_sp,
645bdd1243dSDimitry Andric     const TraceDumper::SymbolInfo &symbol_info) {
646bdd1243dSDimitry Andric   m_is_error = cursor_sp->IsError();
647bdd1243dSDimitry Andric   AppendSegment(cursor_sp, symbol_info);
648bdd1243dSDimitry Andric }
649bdd1243dSDimitry Andric 
650bdd1243dSDimitry Andric void TraceDumper::FunctionCall::AppendSegment(
651bdd1243dSDimitry Andric     const TraceCursorSP &cursor_sp,
652bdd1243dSDimitry Andric     const TraceDumper::SymbolInfo &symbol_info) {
653bdd1243dSDimitry Andric   m_traced_segments.emplace_back(cursor_sp, symbol_info, *this);
654bdd1243dSDimitry Andric }
655bdd1243dSDimitry Andric 
656bdd1243dSDimitry Andric const TraceDumper::SymbolInfo &
657bdd1243dSDimitry Andric TraceDumper::FunctionCall::GetSymbolInfo() const {
658bdd1243dSDimitry Andric   return m_traced_segments.back().GetLastInstructionSymbolInfo();
659bdd1243dSDimitry Andric }
660bdd1243dSDimitry Andric 
661bdd1243dSDimitry Andric bool TraceDumper::FunctionCall::IsError() const { return m_is_error; }
662bdd1243dSDimitry Andric 
663bdd1243dSDimitry Andric const std::deque<TraceDumper::FunctionCall::TracedSegment> &
664bdd1243dSDimitry Andric TraceDumper::FunctionCall::GetTracedSegments() const {
665bdd1243dSDimitry Andric   return m_traced_segments;
666bdd1243dSDimitry Andric }
667bdd1243dSDimitry Andric 
668bdd1243dSDimitry Andric TraceDumper::FunctionCall::TracedSegment &
669bdd1243dSDimitry Andric TraceDumper::FunctionCall::GetLastTracedSegment() {
670bdd1243dSDimitry Andric   return m_traced_segments.back();
671bdd1243dSDimitry Andric }
672bdd1243dSDimitry Andric 
673bdd1243dSDimitry Andric const std::optional<TraceDumper::FunctionCall::UntracedPrefixSegment> &
674bdd1243dSDimitry Andric TraceDumper::FunctionCall::GetUntracedPrefixSegment() const {
675bdd1243dSDimitry Andric   return m_untraced_prefix_segment;
676bdd1243dSDimitry Andric }
677bdd1243dSDimitry Andric 
678bdd1243dSDimitry Andric void TraceDumper::FunctionCall::SetUntracedPrefixSegment(
679bdd1243dSDimitry Andric     TraceDumper::FunctionCallUP &&nested_call) {
680bdd1243dSDimitry Andric   m_untraced_prefix_segment.emplace(std::move(nested_call));
681bdd1243dSDimitry Andric }
682bdd1243dSDimitry Andric 
683bdd1243dSDimitry Andric TraceDumper::FunctionCall *TraceDumper::FunctionCall::GetParentCall() const {
684bdd1243dSDimitry Andric   return m_parent_call;
685bdd1243dSDimitry Andric }
686bdd1243dSDimitry Andric 
687bdd1243dSDimitry Andric void TraceDumper::FunctionCall::SetParentCall(
688bdd1243dSDimitry Andric     TraceDumper::FunctionCall &parent_call) {
689bdd1243dSDimitry Andric   m_parent_call = &parent_call;
690bdd1243dSDimitry Andric }
691bdd1243dSDimitry Andric 
692bdd1243dSDimitry Andric /// Given an instruction that happens after a return, find the ancestor function
693bdd1243dSDimitry Andric /// call that owns it. If this ancestor doesn't exist, create a new ancestor and
694bdd1243dSDimitry Andric /// make it the root of the tree.
695bdd1243dSDimitry Andric ///
696bdd1243dSDimitry Andric /// \param[in] last_function_call
697bdd1243dSDimitry Andric ///   The function call that performs the return.
698bdd1243dSDimitry Andric ///
699bdd1243dSDimitry Andric /// \param[in] symbol_info
700bdd1243dSDimitry Andric ///   The symbol information of the instruction after the return.
701bdd1243dSDimitry Andric ///
702bdd1243dSDimitry Andric /// \param[in] cursor_sp
703bdd1243dSDimitry Andric ///   The cursor pointing to the instruction after the return.
704bdd1243dSDimitry Andric ///
705bdd1243dSDimitry Andric /// \param[in,out] roots
706bdd1243dSDimitry Andric ///   The object owning the roots. It might be modified if a new root needs to
707bdd1243dSDimitry Andric ///   be created.
708bdd1243dSDimitry Andric ///
709bdd1243dSDimitry Andric /// \return
710bdd1243dSDimitry Andric ///   A reference to the function call that owns the new instruction
711bdd1243dSDimitry Andric static TraceDumper::FunctionCall &AppendReturnedInstructionToFunctionCallForest(
712bdd1243dSDimitry Andric     TraceDumper::FunctionCall &last_function_call,
713bdd1243dSDimitry Andric     const TraceDumper::SymbolInfo &symbol_info, const TraceCursorSP &cursor_sp,
714bdd1243dSDimitry Andric     std::vector<TraceDumper::FunctionCallUP> &roots) {
715bdd1243dSDimitry Andric 
716bdd1243dSDimitry Andric   // We omit the current node because we can't return to itself.
717bdd1243dSDimitry Andric   TraceDumper::FunctionCall *ancestor = last_function_call.GetParentCall();
718bdd1243dSDimitry Andric 
719bdd1243dSDimitry Andric   for (; ancestor; ancestor = ancestor->GetParentCall()) {
720bdd1243dSDimitry Andric     // This loop traverses the tree until it finds a call that we can return to.
721bdd1243dSDimitry Andric     if (IsSameInstructionSymbolContext(ancestor->GetSymbolInfo(), symbol_info,
722bdd1243dSDimitry Andric                                        /*check_source_line_info=*/false)) {
723bdd1243dSDimitry Andric       // We returned to this symbol, so we are assuming we are returning there
724bdd1243dSDimitry Andric       // Note: If this is not robust enough, we should actually check if we
725bdd1243dSDimitry Andric       // returning to the instruction that follows the last instruction from
726bdd1243dSDimitry Andric       // that call, as that's the behavior of CALL instructions.
727bdd1243dSDimitry Andric       ancestor->AppendSegment(cursor_sp, symbol_info);
728bdd1243dSDimitry Andric       return *ancestor;
729bdd1243dSDimitry Andric     }
730bdd1243dSDimitry Andric   }
731bdd1243dSDimitry Andric 
732bdd1243dSDimitry Andric   // We didn't find the call we were looking for, so we now create a synthetic
733bdd1243dSDimitry Andric   // one that will contain the new instruction in its first traced segment.
734bdd1243dSDimitry Andric   TraceDumper::FunctionCallUP new_root =
735bdd1243dSDimitry Andric       std::make_unique<TraceDumper::FunctionCall>(cursor_sp, symbol_info);
736bdd1243dSDimitry Andric   // This new root will own the previous root through an untraced prefix segment.
737bdd1243dSDimitry Andric   new_root->SetUntracedPrefixSegment(std::move(roots.back()));
738bdd1243dSDimitry Andric   roots.pop_back();
739bdd1243dSDimitry Andric   // We update the roots container to point to the new root
740bdd1243dSDimitry Andric   roots.emplace_back(std::move(new_root));
741bdd1243dSDimitry Andric   return *roots.back();
742bdd1243dSDimitry Andric }
743bdd1243dSDimitry Andric 
744bdd1243dSDimitry Andric /// Append an instruction to a function call forest. The new instruction might
745bdd1243dSDimitry Andric /// be appended to the current segment, to a new nest call, or return to an
746bdd1243dSDimitry Andric /// ancestor call.
747bdd1243dSDimitry Andric ///
748bdd1243dSDimitry Andric /// \param[in] exe_ctx
749bdd1243dSDimitry Andric ///   The exeuction context of the traced thread.
750bdd1243dSDimitry Andric ///
751bdd1243dSDimitry Andric /// \param[in] last_function_call
752bdd1243dSDimitry Andric ///   The chronologically most recent function call before the new instruction.
753bdd1243dSDimitry Andric ///
754bdd1243dSDimitry Andric /// \param[in] prev_symbol_info
755bdd1243dSDimitry Andric ///   The symbol information of the previous instruction in the trace.
756bdd1243dSDimitry Andric ///
757bdd1243dSDimitry Andric /// \param[in] symbol_info
758bdd1243dSDimitry Andric ///   The symbol information of the new instruction.
759bdd1243dSDimitry Andric ///
760bdd1243dSDimitry Andric /// \param[in] cursor_sp
761bdd1243dSDimitry Andric ///   The cursor pointing to the new instruction.
762bdd1243dSDimitry Andric ///
763bdd1243dSDimitry Andric /// \param[in,out] roots
764bdd1243dSDimitry Andric ///   The object owning the roots. It might be modified if a new root needs to
765bdd1243dSDimitry Andric ///   be created.
766bdd1243dSDimitry Andric ///
767bdd1243dSDimitry Andric /// \return
768bdd1243dSDimitry Andric ///   A reference to the function call that owns the new instruction.
769bdd1243dSDimitry Andric static TraceDumper::FunctionCall &AppendInstructionToFunctionCallForest(
770bdd1243dSDimitry Andric     const ExecutionContext &exe_ctx,
771bdd1243dSDimitry Andric     TraceDumper::FunctionCall *last_function_call,
772bdd1243dSDimitry Andric     const TraceDumper::SymbolInfo &prev_symbol_info,
773bdd1243dSDimitry Andric     const TraceDumper::SymbolInfo &symbol_info, const TraceCursorSP &cursor_sp,
774bdd1243dSDimitry Andric     std::vector<TraceDumper::FunctionCallUP> &roots) {
775bdd1243dSDimitry Andric   if (!last_function_call || last_function_call->IsError()) {
776bdd1243dSDimitry Andric     // We create a brand new root
777bdd1243dSDimitry Andric     roots.emplace_back(
778bdd1243dSDimitry Andric         std::make_unique<TraceDumper::FunctionCall>(cursor_sp, symbol_info));
779bdd1243dSDimitry Andric     return *roots.back();
780bdd1243dSDimitry Andric   }
781bdd1243dSDimitry Andric 
7825f757f3fSDimitry Andric   lldb_private::AddressRange range;
783bdd1243dSDimitry Andric   if (symbol_info.sc.GetAddressRange(
784bdd1243dSDimitry Andric           eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol,
785bdd1243dSDimitry Andric           0, /*inline_block_range*/ true, range)) {
786bdd1243dSDimitry Andric     if (range.GetBaseAddress() == symbol_info.address) {
787bdd1243dSDimitry Andric       // Our instruction is the first instruction of a function. This has
788bdd1243dSDimitry Andric       // to be a call. This should also identify if a trampoline or the linker
789bdd1243dSDimitry Andric       // is making a call using a non-CALL instruction.
790bdd1243dSDimitry Andric       return last_function_call->GetLastTracedSegment().CreateNestedCall(
791bdd1243dSDimitry Andric           cursor_sp, symbol_info);
792bdd1243dSDimitry Andric     }
793bdd1243dSDimitry Andric   }
794bdd1243dSDimitry Andric   if (IsSameInstructionSymbolContext(prev_symbol_info, symbol_info,
795bdd1243dSDimitry Andric                                      /*check_source_line_info=*/false)) {
796bdd1243dSDimitry Andric     // We are still in the same function. This can't be a call because otherwise
797bdd1243dSDimitry Andric     // we would be in the first instruction of the symbol.
798bdd1243dSDimitry Andric     last_function_call->GetLastTracedSegment().AppendInsn(cursor_sp,
799bdd1243dSDimitry Andric                                                           symbol_info);
800bdd1243dSDimitry Andric     return *last_function_call;
801bdd1243dSDimitry Andric   }
802bdd1243dSDimitry Andric   // Now we are in a different symbol. Let's see if this is a return or a
803bdd1243dSDimitry Andric   // call
804bdd1243dSDimitry Andric   const InstructionSP &insn = last_function_call->GetLastTracedSegment()
805bdd1243dSDimitry Andric                                   .GetLastInstructionSymbolInfo()
806bdd1243dSDimitry Andric                                   .instruction;
807bdd1243dSDimitry Andric   InstructionControlFlowKind insn_kind =
808bdd1243dSDimitry Andric       insn ? insn->GetControlFlowKind(&exe_ctx)
809bdd1243dSDimitry Andric            : eInstructionControlFlowKindOther;
810bdd1243dSDimitry Andric 
811bdd1243dSDimitry Andric   switch (insn_kind) {
812bdd1243dSDimitry Andric   case lldb::eInstructionControlFlowKindCall:
813bdd1243dSDimitry Andric   case lldb::eInstructionControlFlowKindFarCall: {
814bdd1243dSDimitry Andric     // This is a regular call
815bdd1243dSDimitry Andric     return last_function_call->GetLastTracedSegment().CreateNestedCall(
816bdd1243dSDimitry Andric         cursor_sp, symbol_info);
817bdd1243dSDimitry Andric   }
818bdd1243dSDimitry Andric   case lldb::eInstructionControlFlowKindFarReturn:
819bdd1243dSDimitry Andric   case lldb::eInstructionControlFlowKindReturn: {
820bdd1243dSDimitry Andric     // We should have caught most trampolines and linker functions earlier, so
821bdd1243dSDimitry Andric     // let's assume this is a regular return.
822bdd1243dSDimitry Andric     return AppendReturnedInstructionToFunctionCallForest(
823bdd1243dSDimitry Andric         *last_function_call, symbol_info, cursor_sp, roots);
824bdd1243dSDimitry Andric   }
825bdd1243dSDimitry Andric   default:
826bdd1243dSDimitry Andric     // we changed symbols not using a call or return and we are not in the
827bdd1243dSDimitry Andric     // beginning of a symbol, so this should be something very artificial
828bdd1243dSDimitry Andric     // or maybe a jump to some label in the middle of it section.
829bdd1243dSDimitry Andric 
830bdd1243dSDimitry Andric     // We first check if it's a return from an inline method
831bdd1243dSDimitry Andric     if (prev_symbol_info.sc.block &&
832bdd1243dSDimitry Andric         prev_symbol_info.sc.block->GetContainingInlinedBlock()) {
833bdd1243dSDimitry Andric       return AppendReturnedInstructionToFunctionCallForest(
834bdd1243dSDimitry Andric           *last_function_call, symbol_info, cursor_sp, roots);
835bdd1243dSDimitry Andric     }
836bdd1243dSDimitry Andric     // Now We assume it's a call. We should revisit this in the future.
837bdd1243dSDimitry Andric     // Ideally we should be able to decide whether to create a new tree,
838bdd1243dSDimitry Andric     // or go deeper or higher in the stack.
839bdd1243dSDimitry Andric     return last_function_call->GetLastTracedSegment().CreateNestedCall(
840bdd1243dSDimitry Andric         cursor_sp, symbol_info);
841bdd1243dSDimitry Andric   }
842bdd1243dSDimitry Andric }
843bdd1243dSDimitry Andric 
844bdd1243dSDimitry Andric /// Append an error to a function call forest. The new error might be appended
845bdd1243dSDimitry Andric /// to the current segment if it contains errors or will create a new root.
846bdd1243dSDimitry Andric ///
847bdd1243dSDimitry Andric /// \param[in] last_function_call
848bdd1243dSDimitry Andric ///   The chronologically most recent function call before the new error.
849bdd1243dSDimitry Andric ///
850bdd1243dSDimitry Andric /// \param[in] cursor_sp
851bdd1243dSDimitry Andric ///   The cursor pointing to the new error.
852bdd1243dSDimitry Andric ///
853bdd1243dSDimitry Andric /// \param[in,out] roots
854bdd1243dSDimitry Andric ///   The object owning the roots. It might be modified if a new root needs to
855bdd1243dSDimitry Andric ///   be created.
856bdd1243dSDimitry Andric ///
857bdd1243dSDimitry Andric /// \return
858bdd1243dSDimitry Andric ///   A reference to the function call that owns the new error.
859bdd1243dSDimitry Andric TraceDumper::FunctionCall &AppendErrorToFunctionCallForest(
860bdd1243dSDimitry Andric     TraceDumper::FunctionCall *last_function_call, TraceCursorSP &cursor_sp,
861bdd1243dSDimitry Andric     std::vector<TraceDumper::FunctionCallUP> &roots) {
862bdd1243dSDimitry Andric   if (last_function_call && last_function_call->IsError()) {
863bdd1243dSDimitry Andric     last_function_call->GetLastTracedSegment().AppendInsn(
864bdd1243dSDimitry Andric         cursor_sp, TraceDumper::SymbolInfo{});
865bdd1243dSDimitry Andric     return *last_function_call;
866bdd1243dSDimitry Andric   } else {
867bdd1243dSDimitry Andric     roots.emplace_back(std::make_unique<TraceDumper::FunctionCall>(
868bdd1243dSDimitry Andric         cursor_sp, TraceDumper::SymbolInfo{}));
869bdd1243dSDimitry Andric     return *roots.back();
870bdd1243dSDimitry Andric   }
871bdd1243dSDimitry Andric }
872bdd1243dSDimitry Andric 
873bdd1243dSDimitry Andric static std::vector<TraceDumper::FunctionCallUP>
874bdd1243dSDimitry Andric CreateFunctionCallForest(TraceCursorSP &cursor_sp,
875bdd1243dSDimitry Andric                          const ExecutionContext &exe_ctx) {
876bdd1243dSDimitry Andric 
877bdd1243dSDimitry Andric   std::vector<TraceDumper::FunctionCallUP> roots;
878bdd1243dSDimitry Andric   TraceDumper::SymbolInfo prev_symbol_info;
879bdd1243dSDimitry Andric 
880bdd1243dSDimitry Andric   TraceDumper::FunctionCall *last_function_call = nullptr;
881bdd1243dSDimitry Andric 
882bdd1243dSDimitry Andric   for (; cursor_sp->HasValue(); cursor_sp->Next()) {
883bdd1243dSDimitry Andric     if (cursor_sp->IsError()) {
884bdd1243dSDimitry Andric       last_function_call = &AppendErrorToFunctionCallForest(last_function_call,
885bdd1243dSDimitry Andric                                                             cursor_sp, roots);
886bdd1243dSDimitry Andric       prev_symbol_info = {};
887bdd1243dSDimitry Andric     } else if (cursor_sp->IsInstruction()) {
888bdd1243dSDimitry Andric       TraceDumper::SymbolInfo symbol_info = CalculateSymbolInfo(
889bdd1243dSDimitry Andric           exe_ctx, cursor_sp->GetLoadAddress(), prev_symbol_info);
890bdd1243dSDimitry Andric 
891bdd1243dSDimitry Andric       last_function_call = &AppendInstructionToFunctionCallForest(
892bdd1243dSDimitry Andric           exe_ctx, last_function_call, prev_symbol_info, symbol_info, cursor_sp,
893bdd1243dSDimitry Andric           roots);
894bdd1243dSDimitry Andric       prev_symbol_info = symbol_info;
895bdd1243dSDimitry Andric     } else if (cursor_sp->GetEventType() == eTraceEventCPUChanged) {
896bdd1243dSDimitry Andric       // TODO: In case of a CPU change, we create a new root because we haven't
897bdd1243dSDimitry Andric       // investigated yet if a call tree can safely continue or if interrupts
898bdd1243dSDimitry Andric       // could have polluted the original call tree.
899bdd1243dSDimitry Andric       last_function_call = nullptr;
900bdd1243dSDimitry Andric       prev_symbol_info = {};
901bdd1243dSDimitry Andric     }
902bdd1243dSDimitry Andric   }
903bdd1243dSDimitry Andric 
904bdd1243dSDimitry Andric   return roots;
905bdd1243dSDimitry Andric }
906bdd1243dSDimitry Andric 
907bdd1243dSDimitry Andric void TraceDumper::DumpFunctionCalls() {
908bdd1243dSDimitry Andric   ThreadSP thread_sp = m_cursor_sp->GetExecutionContextRef().GetThreadSP();
909bdd1243dSDimitry Andric   ExecutionContext exe_ctx;
910bdd1243dSDimitry Andric   thread_sp->GetProcess()->GetTarget().CalculateExecutionContext(exe_ctx);
911bdd1243dSDimitry Andric 
912bdd1243dSDimitry Andric   m_writer_up->FunctionCallForest(
913bdd1243dSDimitry Andric       CreateFunctionCallForest(m_cursor_sp, exe_ctx));
914bdd1243dSDimitry Andric }
915