xref: /openbsd-src/gnu/llvm/lldb/source/Target/TraceDumper.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1*f6aab3d8Srobert //===-- TraceDumper.cpp ---------------------------------------------------===//
2*f6aab3d8Srobert //
3*f6aab3d8Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*f6aab3d8Srobert // See https://llvm.org/LICENSE.txt for license information.
5*f6aab3d8Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*f6aab3d8Srobert //
7*f6aab3d8Srobert //===----------------------------------------------------------------------===//
8*f6aab3d8Srobert 
9*f6aab3d8Srobert #include "lldb/Target/TraceDumper.h"
10*f6aab3d8Srobert #include "lldb/Core/Module.h"
11*f6aab3d8Srobert #include "lldb/Symbol/CompileUnit.h"
12*f6aab3d8Srobert #include "lldb/Symbol/Function.h"
13*f6aab3d8Srobert #include "lldb/Target/ExecutionContext.h"
14*f6aab3d8Srobert #include "lldb/Target/Process.h"
15*f6aab3d8Srobert #include "lldb/Target/SectionLoadList.h"
16*f6aab3d8Srobert #include <optional>
17*f6aab3d8Srobert 
18*f6aab3d8Srobert using namespace lldb;
19*f6aab3d8Srobert using namespace lldb_private;
20*f6aab3d8Srobert using namespace llvm;
21*f6aab3d8Srobert 
22*f6aab3d8Srobert /// \return
23*f6aab3d8Srobert ///   The given string or \b std::nullopt if it's empty.
ToOptionalString(const char * s)24*f6aab3d8Srobert static std::optional<const char *> ToOptionalString(const char *s) {
25*f6aab3d8Srobert   if (!s)
26*f6aab3d8Srobert     return std::nullopt;
27*f6aab3d8Srobert   return s;
28*f6aab3d8Srobert }
29*f6aab3d8Srobert 
GetModuleName(const SymbolContext & sc)30*f6aab3d8Srobert static const char *GetModuleName(const SymbolContext &sc) {
31*f6aab3d8Srobert   if (!sc.module_sp)
32*f6aab3d8Srobert     return nullptr;
33*f6aab3d8Srobert   return sc.module_sp->GetFileSpec().GetFilename().AsCString();
34*f6aab3d8Srobert }
35*f6aab3d8Srobert 
36*f6aab3d8Srobert /// \return
37*f6aab3d8Srobert ///   The module name (basename if the module is a file, or the actual name if
38*f6aab3d8Srobert ///   it's a virtual module), or \b nullptr if no name nor module was found.
GetModuleName(const TraceDumper::TraceItem & item)39*f6aab3d8Srobert static const char *GetModuleName(const TraceDumper::TraceItem &item) {
40*f6aab3d8Srobert   if (!item.symbol_info)
41*f6aab3d8Srobert     return nullptr;
42*f6aab3d8Srobert   return GetModuleName(item.symbol_info->sc);
43*f6aab3d8Srobert }
44*f6aab3d8Srobert 
45*f6aab3d8Srobert // This custom LineEntry validator is neded because some line_entries have
46*f6aab3d8Srobert // 0 as line, which is meaningless. Notice that LineEntry::IsValid only
47*f6aab3d8Srobert // checks that line is not LLDB_INVALID_LINE_NUMBER, i.e. UINT32_MAX.
IsLineEntryValid(const LineEntry & line_entry)48*f6aab3d8Srobert static bool IsLineEntryValid(const LineEntry &line_entry) {
49*f6aab3d8Srobert   return line_entry.IsValid() && line_entry.line > 0;
50*f6aab3d8Srobert }
51*f6aab3d8Srobert 
52*f6aab3d8Srobert /// \return
53*f6aab3d8Srobert ///     \b true if the provided line entries match line, column and source file.
54*f6aab3d8Srobert ///     This function assumes that the line entries are valid.
FileLineAndColumnMatches(const LineEntry & a,const LineEntry & b)55*f6aab3d8Srobert static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) {
56*f6aab3d8Srobert   if (a.line != b.line)
57*f6aab3d8Srobert     return false;
58*f6aab3d8Srobert   if (a.column != b.column)
59*f6aab3d8Srobert     return false;
60*f6aab3d8Srobert   return a.file == b.file;
61*f6aab3d8Srobert }
62*f6aab3d8Srobert 
63*f6aab3d8Srobert /// Compare the symbol contexts of the provided \a SymbolInfo
64*f6aab3d8Srobert /// objects.
65*f6aab3d8Srobert ///
66*f6aab3d8Srobert /// \return
67*f6aab3d8Srobert ///     \a true if both instructions belong to the same scope level analized
68*f6aab3d8Srobert ///     in the following order:
69*f6aab3d8Srobert ///       - module
70*f6aab3d8Srobert ///       - symbol
71*f6aab3d8Srobert ///       - function
72*f6aab3d8Srobert ///       - inlined function
73*f6aab3d8Srobert ///       - source line info
74*f6aab3d8Srobert static bool
IsSameInstructionSymbolContext(const TraceDumper::SymbolInfo & prev_insn,const TraceDumper::SymbolInfo & insn,bool check_source_line_info=true)75*f6aab3d8Srobert IsSameInstructionSymbolContext(const TraceDumper::SymbolInfo &prev_insn,
76*f6aab3d8Srobert                                const TraceDumper::SymbolInfo &insn,
77*f6aab3d8Srobert                                bool check_source_line_info = true) {
78*f6aab3d8Srobert   // module checks
79*f6aab3d8Srobert   if (insn.sc.module_sp != prev_insn.sc.module_sp)
80*f6aab3d8Srobert     return false;
81*f6aab3d8Srobert 
82*f6aab3d8Srobert   // symbol checks
83*f6aab3d8Srobert   if (insn.sc.symbol != prev_insn.sc.symbol)
84*f6aab3d8Srobert     return false;
85*f6aab3d8Srobert 
86*f6aab3d8Srobert   // function checks
87*f6aab3d8Srobert   if (!insn.sc.function && !prev_insn.sc.function)
88*f6aab3d8Srobert     return true; // This means two dangling instruction in the same module. We
89*f6aab3d8Srobert                  // can assume they are part of the same unnamed symbol
90*f6aab3d8Srobert   else if (insn.sc.function != prev_insn.sc.function)
91*f6aab3d8Srobert     return false;
92*f6aab3d8Srobert 
93*f6aab3d8Srobert   Block *inline_block_a =
94*f6aab3d8Srobert       insn.sc.block ? insn.sc.block->GetContainingInlinedBlock() : nullptr;
95*f6aab3d8Srobert   Block *inline_block_b = prev_insn.sc.block
96*f6aab3d8Srobert                               ? prev_insn.sc.block->GetContainingInlinedBlock()
97*f6aab3d8Srobert                               : nullptr;
98*f6aab3d8Srobert   if (inline_block_a != inline_block_b)
99*f6aab3d8Srobert     return false;
100*f6aab3d8Srobert 
101*f6aab3d8Srobert   // line entry checks
102*f6aab3d8Srobert   if (!check_source_line_info)
103*f6aab3d8Srobert     return true;
104*f6aab3d8Srobert 
105*f6aab3d8Srobert   const bool curr_line_valid = IsLineEntryValid(insn.sc.line_entry);
106*f6aab3d8Srobert   const bool prev_line_valid = IsLineEntryValid(prev_insn.sc.line_entry);
107*f6aab3d8Srobert   if (curr_line_valid && prev_line_valid)
108*f6aab3d8Srobert     return FileLineAndColumnMatches(insn.sc.line_entry,
109*f6aab3d8Srobert                                     prev_insn.sc.line_entry);
110*f6aab3d8Srobert   return curr_line_valid == prev_line_valid;
111*f6aab3d8Srobert }
112*f6aab3d8Srobert 
113*f6aab3d8Srobert class OutputWriterCLI : public TraceDumper::OutputWriter {
114*f6aab3d8Srobert public:
OutputWriterCLI(Stream & s,const TraceDumperOptions & options,Thread & thread)115*f6aab3d8Srobert   OutputWriterCLI(Stream &s, const TraceDumperOptions &options, Thread &thread)
116*f6aab3d8Srobert       : m_s(s), m_options(options) {
117*f6aab3d8Srobert     m_s.Format("thread #{0}: tid = {1}\n", thread.GetIndexID(), thread.GetID());
118*f6aab3d8Srobert   };
119*f6aab3d8Srobert 
NoMoreData()120*f6aab3d8Srobert   void NoMoreData() override { m_s << "    no more data\n"; }
121*f6aab3d8Srobert 
FunctionCallForest(const std::vector<TraceDumper::FunctionCallUP> & forest)122*f6aab3d8Srobert   void FunctionCallForest(
123*f6aab3d8Srobert       const std::vector<TraceDumper::FunctionCallUP> &forest) override {
124*f6aab3d8Srobert     for (size_t i = 0; i < forest.size(); i++) {
125*f6aab3d8Srobert       m_s.Format("\n[call tree #{0}]\n", i);
126*f6aab3d8Srobert       DumpFunctionCallTree(*forest[i]);
127*f6aab3d8Srobert     }
128*f6aab3d8Srobert   }
129*f6aab3d8Srobert 
TraceItem(const TraceDumper::TraceItem & item)130*f6aab3d8Srobert   void TraceItem(const TraceDumper::TraceItem &item) override {
131*f6aab3d8Srobert     if (item.symbol_info) {
132*f6aab3d8Srobert       if (!item.prev_symbol_info ||
133*f6aab3d8Srobert           !IsSameInstructionSymbolContext(*item.prev_symbol_info,
134*f6aab3d8Srobert                                           *item.symbol_info)) {
135*f6aab3d8Srobert         m_s << "  ";
136*f6aab3d8Srobert         const char *module_name = GetModuleName(item);
137*f6aab3d8Srobert         if (!module_name)
138*f6aab3d8Srobert           m_s << "(none)";
139*f6aab3d8Srobert         else if (!item.symbol_info->sc.function && !item.symbol_info->sc.symbol)
140*f6aab3d8Srobert           m_s.Format("{0}`(none)", module_name);
141*f6aab3d8Srobert         else
142*f6aab3d8Srobert           item.symbol_info->sc.DumpStopContext(
143*f6aab3d8Srobert               &m_s, item.symbol_info->exe_ctx.GetTargetPtr(),
144*f6aab3d8Srobert               item.symbol_info->address,
145*f6aab3d8Srobert               /*show_fullpaths=*/false,
146*f6aab3d8Srobert               /*show_module=*/true, /*show_inlined_frames=*/false,
147*f6aab3d8Srobert               /*show_function_arguments=*/true,
148*f6aab3d8Srobert               /*show_function_name=*/true);
149*f6aab3d8Srobert         m_s << "\n";
150*f6aab3d8Srobert       }
151*f6aab3d8Srobert     }
152*f6aab3d8Srobert 
153*f6aab3d8Srobert     if (item.error && !m_was_prev_instruction_an_error)
154*f6aab3d8Srobert       m_s << "    ...missing instructions\n";
155*f6aab3d8Srobert 
156*f6aab3d8Srobert     m_s.Format("    {0}: ", item.id);
157*f6aab3d8Srobert 
158*f6aab3d8Srobert     if (m_options.show_timestamps) {
159*f6aab3d8Srobert       m_s.Format("[{0}] ", item.timestamp
160*f6aab3d8Srobert                                ? formatv("{0:3} ns", *item.timestamp).str()
161*f6aab3d8Srobert                                : "unavailable");
162*f6aab3d8Srobert     }
163*f6aab3d8Srobert 
164*f6aab3d8Srobert     if (item.event) {
165*f6aab3d8Srobert       m_s << "(event) " << TraceCursor::EventKindToString(*item.event);
166*f6aab3d8Srobert       switch (*item.event) {
167*f6aab3d8Srobert       case eTraceEventCPUChanged:
168*f6aab3d8Srobert         m_s.Format(" [new CPU={0}]",
169*f6aab3d8Srobert                    item.cpu_id ? std::to_string(*item.cpu_id) : "unavailable");
170*f6aab3d8Srobert         break;
171*f6aab3d8Srobert       case eTraceEventHWClockTick:
172*f6aab3d8Srobert         m_s.Format(" [{0}]", item.hw_clock ? std::to_string(*item.hw_clock)
173*f6aab3d8Srobert                                            : "unavailable");
174*f6aab3d8Srobert         break;
175*f6aab3d8Srobert       case eTraceEventDisabledHW:
176*f6aab3d8Srobert       case eTraceEventDisabledSW:
177*f6aab3d8Srobert         break;
178*f6aab3d8Srobert       case eTraceEventSyncPoint:
179*f6aab3d8Srobert         m_s.Format(" [{0}]", item.sync_point_metadata);
180*f6aab3d8Srobert         break;
181*f6aab3d8Srobert       }
182*f6aab3d8Srobert     } else if (item.error) {
183*f6aab3d8Srobert       m_s << "(error) " << *item.error;
184*f6aab3d8Srobert     } else {
185*f6aab3d8Srobert       m_s.Format("{0:x+16}", item.load_address);
186*f6aab3d8Srobert       if (item.symbol_info && item.symbol_info->instruction) {
187*f6aab3d8Srobert         m_s << "    ";
188*f6aab3d8Srobert         item.symbol_info->instruction->Dump(
189*f6aab3d8Srobert             &m_s, /*max_opcode_byte_size=*/0,
190*f6aab3d8Srobert             /*show_address=*/false,
191*f6aab3d8Srobert             /*show_bytes=*/false, m_options.show_control_flow_kind,
192*f6aab3d8Srobert             &item.symbol_info->exe_ctx, &item.symbol_info->sc,
193*f6aab3d8Srobert             /*prev_sym_ctx=*/nullptr,
194*f6aab3d8Srobert             /*disassembly_addr_format=*/nullptr,
195*f6aab3d8Srobert             /*max_address_text_size=*/0);
196*f6aab3d8Srobert       }
197*f6aab3d8Srobert     }
198*f6aab3d8Srobert 
199*f6aab3d8Srobert     m_was_prev_instruction_an_error = (bool)item.error;
200*f6aab3d8Srobert     m_s << "\n";
201*f6aab3d8Srobert   }
202*f6aab3d8Srobert 
203*f6aab3d8Srobert private:
204*f6aab3d8Srobert   void
DumpSegmentContext(const TraceDumper::FunctionCall::TracedSegment & segment)205*f6aab3d8Srobert   DumpSegmentContext(const TraceDumper::FunctionCall::TracedSegment &segment) {
206*f6aab3d8Srobert     if (segment.GetOwningCall().IsError()) {
207*f6aab3d8Srobert       m_s << "<tracing errors>";
208*f6aab3d8Srobert       return;
209*f6aab3d8Srobert     }
210*f6aab3d8Srobert 
211*f6aab3d8Srobert     const SymbolContext &first_sc = segment.GetFirstInstructionSymbolInfo().sc;
212*f6aab3d8Srobert     first_sc.DumpStopContext(
213*f6aab3d8Srobert         &m_s, segment.GetFirstInstructionSymbolInfo().exe_ctx.GetTargetPtr(),
214*f6aab3d8Srobert         segment.GetFirstInstructionSymbolInfo().address,
215*f6aab3d8Srobert         /*show_fullpaths=*/false,
216*f6aab3d8Srobert         /*show_module=*/true, /*show_inlined_frames=*/false,
217*f6aab3d8Srobert         /*show_function_arguments=*/true,
218*f6aab3d8Srobert         /*show_function_name=*/true);
219*f6aab3d8Srobert     m_s << " to ";
220*f6aab3d8Srobert     const SymbolContext &last_sc = segment.GetLastInstructionSymbolInfo().sc;
221*f6aab3d8Srobert     if (IsLineEntryValid(first_sc.line_entry) &&
222*f6aab3d8Srobert         IsLineEntryValid(last_sc.line_entry)) {
223*f6aab3d8Srobert       m_s.Format("{0}:{1}", last_sc.line_entry.line, last_sc.line_entry.column);
224*f6aab3d8Srobert     } else {
225*f6aab3d8Srobert       last_sc.DumpStopContext(
226*f6aab3d8Srobert           &m_s, segment.GetFirstInstructionSymbolInfo().exe_ctx.GetTargetPtr(),
227*f6aab3d8Srobert           segment.GetLastInstructionSymbolInfo().address,
228*f6aab3d8Srobert           /*show_fullpaths=*/false,
229*f6aab3d8Srobert           /*show_module=*/false, /*show_inlined_frames=*/false,
230*f6aab3d8Srobert           /*show_function_arguments=*/false,
231*f6aab3d8Srobert           /*show_function_name=*/false);
232*f6aab3d8Srobert     }
233*f6aab3d8Srobert   }
234*f6aab3d8Srobert 
DumpUntracedContext(const TraceDumper::FunctionCall & function_call)235*f6aab3d8Srobert   void DumpUntracedContext(const TraceDumper::FunctionCall &function_call) {
236*f6aab3d8Srobert     if (function_call.IsError()) {
237*f6aab3d8Srobert       m_s << "tracing error";
238*f6aab3d8Srobert     }
239*f6aab3d8Srobert     const SymbolContext &sc = function_call.GetSymbolInfo().sc;
240*f6aab3d8Srobert 
241*f6aab3d8Srobert     const char *module_name = GetModuleName(sc);
242*f6aab3d8Srobert     if (!module_name)
243*f6aab3d8Srobert       m_s << "(none)";
244*f6aab3d8Srobert     else if (!sc.function && !sc.symbol)
245*f6aab3d8Srobert       m_s << module_name << "`(none)";
246*f6aab3d8Srobert     else
247*f6aab3d8Srobert       m_s << module_name << "`" << sc.GetFunctionName().AsCString();
248*f6aab3d8Srobert   }
249*f6aab3d8Srobert 
DumpFunctionCallTree(const TraceDumper::FunctionCall & function_call)250*f6aab3d8Srobert   void DumpFunctionCallTree(const TraceDumper::FunctionCall &function_call) {
251*f6aab3d8Srobert     if (function_call.GetUntracedPrefixSegment()) {
252*f6aab3d8Srobert       m_s.Indent();
253*f6aab3d8Srobert       DumpUntracedContext(function_call);
254*f6aab3d8Srobert       m_s << "\n";
255*f6aab3d8Srobert 
256*f6aab3d8Srobert       m_s.IndentMore();
257*f6aab3d8Srobert       DumpFunctionCallTree(function_call.GetUntracedPrefixSegment()->GetNestedCall());
258*f6aab3d8Srobert       m_s.IndentLess();
259*f6aab3d8Srobert     }
260*f6aab3d8Srobert 
261*f6aab3d8Srobert     for (const TraceDumper::FunctionCall::TracedSegment &segment :
262*f6aab3d8Srobert          function_call.GetTracedSegments()) {
263*f6aab3d8Srobert       m_s.Indent();
264*f6aab3d8Srobert       DumpSegmentContext(segment);
265*f6aab3d8Srobert       m_s.Format("  [{0}, {1}]\n", segment.GetFirstInstructionID(),
266*f6aab3d8Srobert                  segment.GetLastInstructionID());
267*f6aab3d8Srobert 
268*f6aab3d8Srobert       segment.IfNestedCall([&](const TraceDumper::FunctionCall &nested_call) {
269*f6aab3d8Srobert         m_s.IndentMore();
270*f6aab3d8Srobert         DumpFunctionCallTree(nested_call);
271*f6aab3d8Srobert         m_s.IndentLess();
272*f6aab3d8Srobert       });
273*f6aab3d8Srobert     }
274*f6aab3d8Srobert   }
275*f6aab3d8Srobert 
276*f6aab3d8Srobert   Stream &m_s;
277*f6aab3d8Srobert   TraceDumperOptions m_options;
278*f6aab3d8Srobert   bool m_was_prev_instruction_an_error = false;
279*f6aab3d8Srobert };
280*f6aab3d8Srobert 
281*f6aab3d8Srobert class OutputWriterJSON : public TraceDumper::OutputWriter {
282*f6aab3d8Srobert   /* schema:
283*f6aab3d8Srobert     error_message: string
284*f6aab3d8Srobert     | {
285*f6aab3d8Srobert       "event": string,
286*f6aab3d8Srobert       "id": decimal,
287*f6aab3d8Srobert       "tsc"?: string decimal,
288*f6aab3d8Srobert       "cpuId"? decimal,
289*f6aab3d8Srobert     } | {
290*f6aab3d8Srobert       "error": string,
291*f6aab3d8Srobert       "id": decimal,
292*f6aab3d8Srobert       "tsc"?: string decimal,
293*f6aab3d8Srobert     | {
294*f6aab3d8Srobert       "loadAddress": string decimal,
295*f6aab3d8Srobert       "id": decimal,
296*f6aab3d8Srobert       "hwClock"?: string decimal,
297*f6aab3d8Srobert       "syncPointMetadata"?: string,
298*f6aab3d8Srobert       "timestamp_ns"?: string decimal,
299*f6aab3d8Srobert       "module"?: string,
300*f6aab3d8Srobert       "symbol"?: string,
301*f6aab3d8Srobert       "line"?: decimal,
302*f6aab3d8Srobert       "column"?: decimal,
303*f6aab3d8Srobert       "source"?: string,
304*f6aab3d8Srobert       "mnemonic"?: string,
305*f6aab3d8Srobert       "controlFlowKind"?: string,
306*f6aab3d8Srobert     }
307*f6aab3d8Srobert   */
308*f6aab3d8Srobert public:
OutputWriterJSON(Stream & s,const TraceDumperOptions & options)309*f6aab3d8Srobert   OutputWriterJSON(Stream &s, const TraceDumperOptions &options)
310*f6aab3d8Srobert       : m_s(s), m_options(options),
311*f6aab3d8Srobert         m_j(m_s.AsRawOstream(),
312*f6aab3d8Srobert             /*IndentSize=*/options.pretty_print_json ? 2 : 0) {
313*f6aab3d8Srobert     m_j.arrayBegin();
314*f6aab3d8Srobert   };
315*f6aab3d8Srobert 
~OutputWriterJSON()316*f6aab3d8Srobert   ~OutputWriterJSON() { m_j.arrayEnd(); }
317*f6aab3d8Srobert 
FunctionCallForest(const std::vector<TraceDumper::FunctionCallUP> & forest)318*f6aab3d8Srobert   void FunctionCallForest(
319*f6aab3d8Srobert       const std::vector<TraceDumper::FunctionCallUP> &forest) override {
320*f6aab3d8Srobert     for (size_t i = 0; i < forest.size(); i++) {
321*f6aab3d8Srobert       m_j.object([&] { DumpFunctionCallTree(*forest[i]); });
322*f6aab3d8Srobert     }
323*f6aab3d8Srobert   }
324*f6aab3d8Srobert 
DumpFunctionCallTree(const TraceDumper::FunctionCall & function_call)325*f6aab3d8Srobert   void DumpFunctionCallTree(const TraceDumper::FunctionCall &function_call) {
326*f6aab3d8Srobert     if (function_call.GetUntracedPrefixSegment()) {
327*f6aab3d8Srobert       m_j.attributeObject("untracedPrefixSegment", [&] {
328*f6aab3d8Srobert         m_j.attributeObject("nestedCall", [&] {
329*f6aab3d8Srobert           DumpFunctionCallTree(
330*f6aab3d8Srobert               function_call.GetUntracedPrefixSegment()->GetNestedCall());
331*f6aab3d8Srobert         });
332*f6aab3d8Srobert       });
333*f6aab3d8Srobert     }
334*f6aab3d8Srobert 
335*f6aab3d8Srobert     if (!function_call.GetTracedSegments().empty()) {
336*f6aab3d8Srobert       m_j.attributeArray("tracedSegments", [&] {
337*f6aab3d8Srobert         for (const TraceDumper::FunctionCall::TracedSegment &segment :
338*f6aab3d8Srobert              function_call.GetTracedSegments()) {
339*f6aab3d8Srobert           m_j.object([&] {
340*f6aab3d8Srobert             m_j.attribute("firstInstructionId",
341*f6aab3d8Srobert                           std::to_string(segment.GetFirstInstructionID()));
342*f6aab3d8Srobert             m_j.attribute("lastInstructionId",
343*f6aab3d8Srobert                           std::to_string(segment.GetLastInstructionID()));
344*f6aab3d8Srobert             segment.IfNestedCall(
345*f6aab3d8Srobert                 [&](const TraceDumper::FunctionCall &nested_call) {
346*f6aab3d8Srobert                   m_j.attributeObject(
347*f6aab3d8Srobert                       "nestedCall", [&] { DumpFunctionCallTree(nested_call); });
348*f6aab3d8Srobert                 });
349*f6aab3d8Srobert           });
350*f6aab3d8Srobert         }
351*f6aab3d8Srobert       });
352*f6aab3d8Srobert     }
353*f6aab3d8Srobert   }
354*f6aab3d8Srobert 
DumpEvent(const TraceDumper::TraceItem & item)355*f6aab3d8Srobert   void DumpEvent(const TraceDumper::TraceItem &item) {
356*f6aab3d8Srobert     m_j.attribute("event", TraceCursor::EventKindToString(*item.event));
357*f6aab3d8Srobert     switch (*item.event) {
358*f6aab3d8Srobert     case eTraceEventCPUChanged:
359*f6aab3d8Srobert       m_j.attribute("cpuId", item.cpu_id);
360*f6aab3d8Srobert       break;
361*f6aab3d8Srobert     case eTraceEventHWClockTick:
362*f6aab3d8Srobert       m_j.attribute("hwClock", item.hw_clock);
363*f6aab3d8Srobert       break;
364*f6aab3d8Srobert     case eTraceEventDisabledHW:
365*f6aab3d8Srobert     case eTraceEventDisabledSW:
366*f6aab3d8Srobert       break;
367*f6aab3d8Srobert     case eTraceEventSyncPoint:
368*f6aab3d8Srobert       m_j.attribute("syncPointMetadata", item.sync_point_metadata);
369*f6aab3d8Srobert       break;
370*f6aab3d8Srobert     }
371*f6aab3d8Srobert   }
372*f6aab3d8Srobert 
DumpInstruction(const TraceDumper::TraceItem & item)373*f6aab3d8Srobert   void DumpInstruction(const TraceDumper::TraceItem &item) {
374*f6aab3d8Srobert     m_j.attribute("loadAddress", formatv("{0:x}", item.load_address));
375*f6aab3d8Srobert     if (item.symbol_info) {
376*f6aab3d8Srobert       m_j.attribute("module", ToOptionalString(GetModuleName(item)));
377*f6aab3d8Srobert       m_j.attribute(
378*f6aab3d8Srobert           "symbol",
379*f6aab3d8Srobert           ToOptionalString(item.symbol_info->sc.GetFunctionName().AsCString()));
380*f6aab3d8Srobert 
381*f6aab3d8Srobert       if (lldb::InstructionSP instruction = item.symbol_info->instruction) {
382*f6aab3d8Srobert         ExecutionContext exe_ctx = item.symbol_info->exe_ctx;
383*f6aab3d8Srobert         m_j.attribute("mnemonic",
384*f6aab3d8Srobert                       ToOptionalString(instruction->GetMnemonic(&exe_ctx)));
385*f6aab3d8Srobert         if (m_options.show_control_flow_kind) {
386*f6aab3d8Srobert           lldb::InstructionControlFlowKind instruction_control_flow_kind =
387*f6aab3d8Srobert               instruction->GetControlFlowKind(&exe_ctx);
388*f6aab3d8Srobert           m_j.attribute("controlFlowKind",
389*f6aab3d8Srobert                         ToOptionalString(
390*f6aab3d8Srobert                             Instruction::GetNameForInstructionControlFlowKind(
391*f6aab3d8Srobert                                 instruction_control_flow_kind)));
392*f6aab3d8Srobert         }
393*f6aab3d8Srobert       }
394*f6aab3d8Srobert 
395*f6aab3d8Srobert       if (IsLineEntryValid(item.symbol_info->sc.line_entry)) {
396*f6aab3d8Srobert         m_j.attribute(
397*f6aab3d8Srobert             "source",
398*f6aab3d8Srobert             ToOptionalString(
399*f6aab3d8Srobert                 item.symbol_info->sc.line_entry.file.GetPath().c_str()));
400*f6aab3d8Srobert         m_j.attribute("line", item.symbol_info->sc.line_entry.line);
401*f6aab3d8Srobert         m_j.attribute("column", item.symbol_info->sc.line_entry.column);
402*f6aab3d8Srobert       }
403*f6aab3d8Srobert     }
404*f6aab3d8Srobert   }
405*f6aab3d8Srobert 
TraceItem(const TraceDumper::TraceItem & item)406*f6aab3d8Srobert   void TraceItem(const TraceDumper::TraceItem &item) override {
407*f6aab3d8Srobert     m_j.object([&] {
408*f6aab3d8Srobert       m_j.attribute("id", item.id);
409*f6aab3d8Srobert       if (m_options.show_timestamps)
410*f6aab3d8Srobert         m_j.attribute("timestamp_ns", item.timestamp
411*f6aab3d8Srobert                                           ? std::optional<std::string>(
412*f6aab3d8Srobert                                                 std::to_string(*item.timestamp))
413*f6aab3d8Srobert                                           : std::nullopt);
414*f6aab3d8Srobert 
415*f6aab3d8Srobert       if (item.event) {
416*f6aab3d8Srobert         DumpEvent(item);
417*f6aab3d8Srobert       } else if (item.error) {
418*f6aab3d8Srobert         m_j.attribute("error", *item.error);
419*f6aab3d8Srobert       } else {
420*f6aab3d8Srobert         DumpInstruction(item);
421*f6aab3d8Srobert       }
422*f6aab3d8Srobert     });
423*f6aab3d8Srobert   }
424*f6aab3d8Srobert 
425*f6aab3d8Srobert private:
426*f6aab3d8Srobert   Stream &m_s;
427*f6aab3d8Srobert   TraceDumperOptions m_options;
428*f6aab3d8Srobert   json::OStream m_j;
429*f6aab3d8Srobert };
430*f6aab3d8Srobert 
431*f6aab3d8Srobert static std::unique_ptr<TraceDumper::OutputWriter>
CreateWriter(Stream & s,const TraceDumperOptions & options,Thread & thread)432*f6aab3d8Srobert CreateWriter(Stream &s, const TraceDumperOptions &options, Thread &thread) {
433*f6aab3d8Srobert   if (options.json)
434*f6aab3d8Srobert     return std::unique_ptr<TraceDumper::OutputWriter>(
435*f6aab3d8Srobert         new OutputWriterJSON(s, options));
436*f6aab3d8Srobert   else
437*f6aab3d8Srobert     return std::unique_ptr<TraceDumper::OutputWriter>(
438*f6aab3d8Srobert         new OutputWriterCLI(s, options, thread));
439*f6aab3d8Srobert }
440*f6aab3d8Srobert 
TraceDumper(lldb::TraceCursorSP cursor_sp,Stream & s,const TraceDumperOptions & options)441*f6aab3d8Srobert TraceDumper::TraceDumper(lldb::TraceCursorSP cursor_sp, Stream &s,
442*f6aab3d8Srobert                          const TraceDumperOptions &options)
443*f6aab3d8Srobert     : m_cursor_sp(std::move(cursor_sp)), m_options(options),
444*f6aab3d8Srobert       m_writer_up(CreateWriter(
445*f6aab3d8Srobert           s, m_options, *m_cursor_sp->GetExecutionContextRef().GetThreadSP())) {
446*f6aab3d8Srobert 
447*f6aab3d8Srobert   if (m_options.id)
448*f6aab3d8Srobert     m_cursor_sp->GoToId(*m_options.id);
449*f6aab3d8Srobert   else if (m_options.forwards)
450*f6aab3d8Srobert     m_cursor_sp->Seek(0, lldb::eTraceCursorSeekTypeBeginning);
451*f6aab3d8Srobert   else
452*f6aab3d8Srobert     m_cursor_sp->Seek(0, lldb::eTraceCursorSeekTypeEnd);
453*f6aab3d8Srobert 
454*f6aab3d8Srobert   m_cursor_sp->SetForwards(m_options.forwards);
455*f6aab3d8Srobert   if (m_options.skip) {
456*f6aab3d8Srobert     m_cursor_sp->Seek((m_options.forwards ? 1 : -1) * *m_options.skip,
457*f6aab3d8Srobert                       lldb::eTraceCursorSeekTypeCurrent);
458*f6aab3d8Srobert   }
459*f6aab3d8Srobert }
460*f6aab3d8Srobert 
CreatRawTraceItem()461*f6aab3d8Srobert TraceDumper::TraceItem TraceDumper::CreatRawTraceItem() {
462*f6aab3d8Srobert   TraceItem item = {};
463*f6aab3d8Srobert   item.id = m_cursor_sp->GetId();
464*f6aab3d8Srobert 
465*f6aab3d8Srobert   if (m_options.show_timestamps)
466*f6aab3d8Srobert     item.timestamp = m_cursor_sp->GetWallClockTime();
467*f6aab3d8Srobert   return item;
468*f6aab3d8Srobert }
469*f6aab3d8Srobert 
470*f6aab3d8Srobert /// Find the symbol context for the given address reusing the previous
471*f6aab3d8Srobert /// instruction's symbol context when possible.
472*f6aab3d8Srobert static SymbolContext
CalculateSymbolContext(const Address & address,const SymbolContext & prev_symbol_context)473*f6aab3d8Srobert CalculateSymbolContext(const Address &address,
474*f6aab3d8Srobert                        const SymbolContext &prev_symbol_context) {
475*f6aab3d8Srobert   AddressRange range;
476*f6aab3d8Srobert   if (prev_symbol_context.GetAddressRange(eSymbolContextEverything, 0,
477*f6aab3d8Srobert                                           /*inline_block_range*/ true, range) &&
478*f6aab3d8Srobert       range.Contains(address))
479*f6aab3d8Srobert     return prev_symbol_context;
480*f6aab3d8Srobert 
481*f6aab3d8Srobert   SymbolContext sc;
482*f6aab3d8Srobert   address.CalculateSymbolContext(&sc, eSymbolContextEverything);
483*f6aab3d8Srobert   return sc;
484*f6aab3d8Srobert }
485*f6aab3d8Srobert 
486*f6aab3d8Srobert /// Find the disassembler for the given address reusing the previous
487*f6aab3d8Srobert /// instruction's disassembler when possible.
488*f6aab3d8Srobert static std::tuple<DisassemblerSP, InstructionSP>
CalculateDisass(const TraceDumper::SymbolInfo & symbol_info,const TraceDumper::SymbolInfo & prev_symbol_info,const ExecutionContext & exe_ctx)489*f6aab3d8Srobert CalculateDisass(const TraceDumper::SymbolInfo &symbol_info,
490*f6aab3d8Srobert                 const TraceDumper::SymbolInfo &prev_symbol_info,
491*f6aab3d8Srobert                 const ExecutionContext &exe_ctx) {
492*f6aab3d8Srobert   if (prev_symbol_info.disassembler) {
493*f6aab3d8Srobert     if (InstructionSP instruction =
494*f6aab3d8Srobert             prev_symbol_info.disassembler->GetInstructionList()
495*f6aab3d8Srobert                 .GetInstructionAtAddress(symbol_info.address))
496*f6aab3d8Srobert       return std::make_tuple(prev_symbol_info.disassembler, instruction);
497*f6aab3d8Srobert   }
498*f6aab3d8Srobert 
499*f6aab3d8Srobert   if (symbol_info.sc.function) {
500*f6aab3d8Srobert     if (DisassemblerSP disassembler =
501*f6aab3d8Srobert             symbol_info.sc.function->GetInstructions(exe_ctx, nullptr)) {
502*f6aab3d8Srobert       if (InstructionSP instruction =
503*f6aab3d8Srobert               disassembler->GetInstructionList().GetInstructionAtAddress(
504*f6aab3d8Srobert                   symbol_info.address))
505*f6aab3d8Srobert         return std::make_tuple(disassembler, instruction);
506*f6aab3d8Srobert     }
507*f6aab3d8Srobert   }
508*f6aab3d8Srobert   // We fallback to a single instruction disassembler
509*f6aab3d8Srobert   Target &target = exe_ctx.GetTargetRef();
510*f6aab3d8Srobert   const ArchSpec arch = target.GetArchitecture();
511*f6aab3d8Srobert   AddressRange range(symbol_info.address, arch.GetMaximumOpcodeByteSize());
512*f6aab3d8Srobert   DisassemblerSP disassembler =
513*f6aab3d8Srobert       Disassembler::DisassembleRange(arch, /*plugin_name*/ nullptr,
514*f6aab3d8Srobert                                      /*flavor*/ nullptr, target, range);
515*f6aab3d8Srobert   return std::make_tuple(
516*f6aab3d8Srobert       disassembler,
517*f6aab3d8Srobert       disassembler ? disassembler->GetInstructionList().GetInstructionAtAddress(
518*f6aab3d8Srobert                          symbol_info.address)
519*f6aab3d8Srobert                    : InstructionSP());
520*f6aab3d8Srobert }
521*f6aab3d8Srobert 
522*f6aab3d8Srobert static TraceDumper::SymbolInfo
CalculateSymbolInfo(const ExecutionContext & exe_ctx,lldb::addr_t load_address,const TraceDumper::SymbolInfo & prev_symbol_info)523*f6aab3d8Srobert CalculateSymbolInfo(const ExecutionContext &exe_ctx, lldb::addr_t load_address,
524*f6aab3d8Srobert                     const TraceDumper::SymbolInfo &prev_symbol_info) {
525*f6aab3d8Srobert   TraceDumper::SymbolInfo symbol_info;
526*f6aab3d8Srobert   symbol_info.exe_ctx = exe_ctx;
527*f6aab3d8Srobert   symbol_info.address.SetLoadAddress(load_address, exe_ctx.GetTargetPtr());
528*f6aab3d8Srobert   symbol_info.sc =
529*f6aab3d8Srobert       CalculateSymbolContext(symbol_info.address, prev_symbol_info.sc);
530*f6aab3d8Srobert   std::tie(symbol_info.disassembler, symbol_info.instruction) =
531*f6aab3d8Srobert       CalculateDisass(symbol_info, prev_symbol_info, exe_ctx);
532*f6aab3d8Srobert   return symbol_info;
533*f6aab3d8Srobert }
534*f6aab3d8Srobert 
DumpInstructions(size_t count)535*f6aab3d8Srobert std::optional<lldb::user_id_t> TraceDumper::DumpInstructions(size_t count) {
536*f6aab3d8Srobert   ThreadSP thread_sp = m_cursor_sp->GetExecutionContextRef().GetThreadSP();
537*f6aab3d8Srobert 
538*f6aab3d8Srobert   SymbolInfo prev_symbol_info;
539*f6aab3d8Srobert   std::optional<lldb::user_id_t> last_id;
540*f6aab3d8Srobert 
541*f6aab3d8Srobert   ExecutionContext exe_ctx;
542*f6aab3d8Srobert   thread_sp->GetProcess()->GetTarget().CalculateExecutionContext(exe_ctx);
543*f6aab3d8Srobert 
544*f6aab3d8Srobert   for (size_t insn_seen = 0; insn_seen < count && m_cursor_sp->HasValue();
545*f6aab3d8Srobert        m_cursor_sp->Next()) {
546*f6aab3d8Srobert 
547*f6aab3d8Srobert     last_id = m_cursor_sp->GetId();
548*f6aab3d8Srobert     TraceItem item = CreatRawTraceItem();
549*f6aab3d8Srobert 
550*f6aab3d8Srobert     if (m_cursor_sp->IsEvent() && m_options.show_events) {
551*f6aab3d8Srobert       item.event = m_cursor_sp->GetEventType();
552*f6aab3d8Srobert       switch (*item.event) {
553*f6aab3d8Srobert       case eTraceEventCPUChanged:
554*f6aab3d8Srobert         item.cpu_id = m_cursor_sp->GetCPU();
555*f6aab3d8Srobert         break;
556*f6aab3d8Srobert       case eTraceEventHWClockTick:
557*f6aab3d8Srobert         item.hw_clock = m_cursor_sp->GetHWClock();
558*f6aab3d8Srobert         break;
559*f6aab3d8Srobert       case eTraceEventDisabledHW:
560*f6aab3d8Srobert       case eTraceEventDisabledSW:
561*f6aab3d8Srobert         break;
562*f6aab3d8Srobert       case eTraceEventSyncPoint:
563*f6aab3d8Srobert         item.sync_point_metadata = m_cursor_sp->GetSyncPointMetadata();
564*f6aab3d8Srobert         break;
565*f6aab3d8Srobert       }
566*f6aab3d8Srobert       m_writer_up->TraceItem(item);
567*f6aab3d8Srobert     } else if (m_cursor_sp->IsError()) {
568*f6aab3d8Srobert       item.error = m_cursor_sp->GetError();
569*f6aab3d8Srobert       m_writer_up->TraceItem(item);
570*f6aab3d8Srobert     } else if (m_cursor_sp->IsInstruction() && !m_options.only_events) {
571*f6aab3d8Srobert       insn_seen++;
572*f6aab3d8Srobert       item.load_address = m_cursor_sp->GetLoadAddress();
573*f6aab3d8Srobert 
574*f6aab3d8Srobert       if (!m_options.raw) {
575*f6aab3d8Srobert         SymbolInfo symbol_info =
576*f6aab3d8Srobert             CalculateSymbolInfo(exe_ctx, item.load_address, prev_symbol_info);
577*f6aab3d8Srobert         item.prev_symbol_info = prev_symbol_info;
578*f6aab3d8Srobert         item.symbol_info = symbol_info;
579*f6aab3d8Srobert         prev_symbol_info = symbol_info;
580*f6aab3d8Srobert       }
581*f6aab3d8Srobert       m_writer_up->TraceItem(item);
582*f6aab3d8Srobert     }
583*f6aab3d8Srobert   }
584*f6aab3d8Srobert   if (!m_cursor_sp->HasValue())
585*f6aab3d8Srobert     m_writer_up->NoMoreData();
586*f6aab3d8Srobert   return last_id;
587*f6aab3d8Srobert }
588*f6aab3d8Srobert 
AppendInsn(const TraceCursorSP & cursor_sp,const TraceDumper::SymbolInfo & symbol_info)589*f6aab3d8Srobert void TraceDumper::FunctionCall::TracedSegment::AppendInsn(
590*f6aab3d8Srobert     const TraceCursorSP &cursor_sp,
591*f6aab3d8Srobert     const TraceDumper::SymbolInfo &symbol_info) {
592*f6aab3d8Srobert   m_last_insn_id = cursor_sp->GetId();
593*f6aab3d8Srobert   m_last_symbol_info = symbol_info;
594*f6aab3d8Srobert }
595*f6aab3d8Srobert 
596*f6aab3d8Srobert lldb::user_id_t
GetFirstInstructionID() const597*f6aab3d8Srobert TraceDumper::FunctionCall::TracedSegment::GetFirstInstructionID() const {
598*f6aab3d8Srobert   return m_first_insn_id;
599*f6aab3d8Srobert }
600*f6aab3d8Srobert 
601*f6aab3d8Srobert lldb::user_id_t
GetLastInstructionID() const602*f6aab3d8Srobert TraceDumper::FunctionCall::TracedSegment::GetLastInstructionID() const {
603*f6aab3d8Srobert   return m_last_insn_id;
604*f6aab3d8Srobert }
605*f6aab3d8Srobert 
IfNestedCall(std::function<void (const FunctionCall & function_call)> callback) const606*f6aab3d8Srobert void TraceDumper::FunctionCall::TracedSegment::IfNestedCall(
607*f6aab3d8Srobert     std::function<void(const FunctionCall &function_call)> callback) const {
608*f6aab3d8Srobert   if (m_nested_call)
609*f6aab3d8Srobert     callback(*m_nested_call);
610*f6aab3d8Srobert }
611*f6aab3d8Srobert 
612*f6aab3d8Srobert const TraceDumper::FunctionCall &
GetOwningCall() const613*f6aab3d8Srobert TraceDumper::FunctionCall::TracedSegment::GetOwningCall() const {
614*f6aab3d8Srobert   return m_owning_call;
615*f6aab3d8Srobert }
616*f6aab3d8Srobert 
617*f6aab3d8Srobert TraceDumper::FunctionCall &
CreateNestedCall(const TraceCursorSP & cursor_sp,const TraceDumper::SymbolInfo & symbol_info)618*f6aab3d8Srobert TraceDumper::FunctionCall::TracedSegment::CreateNestedCall(
619*f6aab3d8Srobert     const TraceCursorSP &cursor_sp,
620*f6aab3d8Srobert     const TraceDumper::SymbolInfo &symbol_info) {
621*f6aab3d8Srobert   m_nested_call = std::make_unique<FunctionCall>(cursor_sp, symbol_info);
622*f6aab3d8Srobert   m_nested_call->SetParentCall(m_owning_call);
623*f6aab3d8Srobert   return *m_nested_call;
624*f6aab3d8Srobert }
625*f6aab3d8Srobert 
626*f6aab3d8Srobert const TraceDumper::SymbolInfo &
GetFirstInstructionSymbolInfo() const627*f6aab3d8Srobert TraceDumper::FunctionCall::TracedSegment::GetFirstInstructionSymbolInfo()
628*f6aab3d8Srobert     const {
629*f6aab3d8Srobert   return m_first_symbol_info;
630*f6aab3d8Srobert }
631*f6aab3d8Srobert 
632*f6aab3d8Srobert const TraceDumper::SymbolInfo &
GetLastInstructionSymbolInfo() const633*f6aab3d8Srobert TraceDumper::FunctionCall::TracedSegment::GetLastInstructionSymbolInfo() const {
634*f6aab3d8Srobert   return m_last_symbol_info;
635*f6aab3d8Srobert }
636*f6aab3d8Srobert 
637*f6aab3d8Srobert const TraceDumper::FunctionCall &
GetNestedCall() const638*f6aab3d8Srobert TraceDumper::FunctionCall::UntracedPrefixSegment::GetNestedCall() const {
639*f6aab3d8Srobert   return *m_nested_call;
640*f6aab3d8Srobert }
641*f6aab3d8Srobert 
FunctionCall(const TraceCursorSP & cursor_sp,const TraceDumper::SymbolInfo & symbol_info)642*f6aab3d8Srobert TraceDumper::FunctionCall::FunctionCall(
643*f6aab3d8Srobert     const TraceCursorSP &cursor_sp,
644*f6aab3d8Srobert     const TraceDumper::SymbolInfo &symbol_info) {
645*f6aab3d8Srobert   m_is_error = cursor_sp->IsError();
646*f6aab3d8Srobert   AppendSegment(cursor_sp, symbol_info);
647*f6aab3d8Srobert }
648*f6aab3d8Srobert 
AppendSegment(const TraceCursorSP & cursor_sp,const TraceDumper::SymbolInfo & symbol_info)649*f6aab3d8Srobert void TraceDumper::FunctionCall::AppendSegment(
650*f6aab3d8Srobert     const TraceCursorSP &cursor_sp,
651*f6aab3d8Srobert     const TraceDumper::SymbolInfo &symbol_info) {
652*f6aab3d8Srobert   m_traced_segments.emplace_back(cursor_sp, symbol_info, *this);
653*f6aab3d8Srobert }
654*f6aab3d8Srobert 
655*f6aab3d8Srobert const TraceDumper::SymbolInfo &
GetSymbolInfo() const656*f6aab3d8Srobert TraceDumper::FunctionCall::GetSymbolInfo() const {
657*f6aab3d8Srobert   return m_traced_segments.back().GetLastInstructionSymbolInfo();
658*f6aab3d8Srobert }
659*f6aab3d8Srobert 
IsError() const660*f6aab3d8Srobert bool TraceDumper::FunctionCall::IsError() const { return m_is_error; }
661*f6aab3d8Srobert 
662*f6aab3d8Srobert const std::deque<TraceDumper::FunctionCall::TracedSegment> &
GetTracedSegments() const663*f6aab3d8Srobert TraceDumper::FunctionCall::GetTracedSegments() const {
664*f6aab3d8Srobert   return m_traced_segments;
665*f6aab3d8Srobert }
666*f6aab3d8Srobert 
667*f6aab3d8Srobert TraceDumper::FunctionCall::TracedSegment &
GetLastTracedSegment()668*f6aab3d8Srobert TraceDumper::FunctionCall::GetLastTracedSegment() {
669*f6aab3d8Srobert   return m_traced_segments.back();
670*f6aab3d8Srobert }
671*f6aab3d8Srobert 
672*f6aab3d8Srobert const std::optional<TraceDumper::FunctionCall::UntracedPrefixSegment> &
GetUntracedPrefixSegment() const673*f6aab3d8Srobert TraceDumper::FunctionCall::GetUntracedPrefixSegment() const {
674*f6aab3d8Srobert   return m_untraced_prefix_segment;
675*f6aab3d8Srobert }
676*f6aab3d8Srobert 
SetUntracedPrefixSegment(TraceDumper::FunctionCallUP && nested_call)677*f6aab3d8Srobert void TraceDumper::FunctionCall::SetUntracedPrefixSegment(
678*f6aab3d8Srobert     TraceDumper::FunctionCallUP &&nested_call) {
679*f6aab3d8Srobert   m_untraced_prefix_segment.emplace(std::move(nested_call));
680*f6aab3d8Srobert }
681*f6aab3d8Srobert 
GetParentCall() const682*f6aab3d8Srobert TraceDumper::FunctionCall *TraceDumper::FunctionCall::GetParentCall() const {
683*f6aab3d8Srobert   return m_parent_call;
684*f6aab3d8Srobert }
685*f6aab3d8Srobert 
SetParentCall(TraceDumper::FunctionCall & parent_call)686*f6aab3d8Srobert void TraceDumper::FunctionCall::SetParentCall(
687*f6aab3d8Srobert     TraceDumper::FunctionCall &parent_call) {
688*f6aab3d8Srobert   m_parent_call = &parent_call;
689*f6aab3d8Srobert }
690*f6aab3d8Srobert 
691*f6aab3d8Srobert /// Given an instruction that happens after a return, find the ancestor function
692*f6aab3d8Srobert /// call that owns it. If this ancestor doesn't exist, create a new ancestor and
693*f6aab3d8Srobert /// make it the root of the tree.
694*f6aab3d8Srobert ///
695*f6aab3d8Srobert /// \param[in] last_function_call
696*f6aab3d8Srobert ///   The function call that performs the return.
697*f6aab3d8Srobert ///
698*f6aab3d8Srobert /// \param[in] symbol_info
699*f6aab3d8Srobert ///   The symbol information of the instruction after the return.
700*f6aab3d8Srobert ///
701*f6aab3d8Srobert /// \param[in] cursor_sp
702*f6aab3d8Srobert ///   The cursor pointing to the instruction after the return.
703*f6aab3d8Srobert ///
704*f6aab3d8Srobert /// \param[in,out] roots
705*f6aab3d8Srobert ///   The object owning the roots. It might be modified if a new root needs to
706*f6aab3d8Srobert ///   be created.
707*f6aab3d8Srobert ///
708*f6aab3d8Srobert /// \return
709*f6aab3d8Srobert ///   A reference to the function call that owns the new instruction
AppendReturnedInstructionToFunctionCallForest(TraceDumper::FunctionCall & last_function_call,const TraceDumper::SymbolInfo & symbol_info,const TraceCursorSP & cursor_sp,std::vector<TraceDumper::FunctionCallUP> & roots)710*f6aab3d8Srobert static TraceDumper::FunctionCall &AppendReturnedInstructionToFunctionCallForest(
711*f6aab3d8Srobert     TraceDumper::FunctionCall &last_function_call,
712*f6aab3d8Srobert     const TraceDumper::SymbolInfo &symbol_info, const TraceCursorSP &cursor_sp,
713*f6aab3d8Srobert     std::vector<TraceDumper::FunctionCallUP> &roots) {
714*f6aab3d8Srobert 
715*f6aab3d8Srobert   // We omit the current node because we can't return to itself.
716*f6aab3d8Srobert   TraceDumper::FunctionCall *ancestor = last_function_call.GetParentCall();
717*f6aab3d8Srobert 
718*f6aab3d8Srobert   for (; ancestor; ancestor = ancestor->GetParentCall()) {
719*f6aab3d8Srobert     // This loop traverses the tree until it finds a call that we can return to.
720*f6aab3d8Srobert     if (IsSameInstructionSymbolContext(ancestor->GetSymbolInfo(), symbol_info,
721*f6aab3d8Srobert                                        /*check_source_line_info=*/false)) {
722*f6aab3d8Srobert       // We returned to this symbol, so we are assuming we are returning there
723*f6aab3d8Srobert       // Note: If this is not robust enough, we should actually check if we
724*f6aab3d8Srobert       // returning to the instruction that follows the last instruction from
725*f6aab3d8Srobert       // that call, as that's the behavior of CALL instructions.
726*f6aab3d8Srobert       ancestor->AppendSegment(cursor_sp, symbol_info);
727*f6aab3d8Srobert       return *ancestor;
728*f6aab3d8Srobert     }
729*f6aab3d8Srobert   }
730*f6aab3d8Srobert 
731*f6aab3d8Srobert   // We didn't find the call we were looking for, so we now create a synthetic
732*f6aab3d8Srobert   // one that will contain the new instruction in its first traced segment.
733*f6aab3d8Srobert   TraceDumper::FunctionCallUP new_root =
734*f6aab3d8Srobert       std::make_unique<TraceDumper::FunctionCall>(cursor_sp, symbol_info);
735*f6aab3d8Srobert   // This new root will own the previous root through an untraced prefix segment.
736*f6aab3d8Srobert   new_root->SetUntracedPrefixSegment(std::move(roots.back()));
737*f6aab3d8Srobert   roots.pop_back();
738*f6aab3d8Srobert   // We update the roots container to point to the new root
739*f6aab3d8Srobert   roots.emplace_back(std::move(new_root));
740*f6aab3d8Srobert   return *roots.back();
741*f6aab3d8Srobert }
742*f6aab3d8Srobert 
743*f6aab3d8Srobert /// Append an instruction to a function call forest. The new instruction might
744*f6aab3d8Srobert /// be appended to the current segment, to a new nest call, or return to an
745*f6aab3d8Srobert /// ancestor call.
746*f6aab3d8Srobert ///
747*f6aab3d8Srobert /// \param[in] exe_ctx
748*f6aab3d8Srobert ///   The exeuction context of the traced thread.
749*f6aab3d8Srobert ///
750*f6aab3d8Srobert /// \param[in] last_function_call
751*f6aab3d8Srobert ///   The chronologically most recent function call before the new instruction.
752*f6aab3d8Srobert ///
753*f6aab3d8Srobert /// \param[in] prev_symbol_info
754*f6aab3d8Srobert ///   The symbol information of the previous instruction in the trace.
755*f6aab3d8Srobert ///
756*f6aab3d8Srobert /// \param[in] symbol_info
757*f6aab3d8Srobert ///   The symbol information of the new instruction.
758*f6aab3d8Srobert ///
759*f6aab3d8Srobert /// \param[in] cursor_sp
760*f6aab3d8Srobert ///   The cursor pointing to the new instruction.
761*f6aab3d8Srobert ///
762*f6aab3d8Srobert /// \param[in,out] roots
763*f6aab3d8Srobert ///   The object owning the roots. It might be modified if a new root needs to
764*f6aab3d8Srobert ///   be created.
765*f6aab3d8Srobert ///
766*f6aab3d8Srobert /// \return
767*f6aab3d8Srobert ///   A reference to the function call that owns the new instruction.
AppendInstructionToFunctionCallForest(const ExecutionContext & exe_ctx,TraceDumper::FunctionCall * last_function_call,const TraceDumper::SymbolInfo & prev_symbol_info,const TraceDumper::SymbolInfo & symbol_info,const TraceCursorSP & cursor_sp,std::vector<TraceDumper::FunctionCallUP> & roots)768*f6aab3d8Srobert static TraceDumper::FunctionCall &AppendInstructionToFunctionCallForest(
769*f6aab3d8Srobert     const ExecutionContext &exe_ctx,
770*f6aab3d8Srobert     TraceDumper::FunctionCall *last_function_call,
771*f6aab3d8Srobert     const TraceDumper::SymbolInfo &prev_symbol_info,
772*f6aab3d8Srobert     const TraceDumper::SymbolInfo &symbol_info, const TraceCursorSP &cursor_sp,
773*f6aab3d8Srobert     std::vector<TraceDumper::FunctionCallUP> &roots) {
774*f6aab3d8Srobert   if (!last_function_call || last_function_call->IsError()) {
775*f6aab3d8Srobert     // We create a brand new root
776*f6aab3d8Srobert     roots.emplace_back(
777*f6aab3d8Srobert         std::make_unique<TraceDumper::FunctionCall>(cursor_sp, symbol_info));
778*f6aab3d8Srobert     return *roots.back();
779*f6aab3d8Srobert   }
780*f6aab3d8Srobert 
781*f6aab3d8Srobert   AddressRange range;
782*f6aab3d8Srobert   if (symbol_info.sc.GetAddressRange(
783*f6aab3d8Srobert           eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol,
784*f6aab3d8Srobert           0, /*inline_block_range*/ true, range)) {
785*f6aab3d8Srobert     if (range.GetBaseAddress() == symbol_info.address) {
786*f6aab3d8Srobert       // Our instruction is the first instruction of a function. This has
787*f6aab3d8Srobert       // to be a call. This should also identify if a trampoline or the linker
788*f6aab3d8Srobert       // is making a call using a non-CALL instruction.
789*f6aab3d8Srobert       return last_function_call->GetLastTracedSegment().CreateNestedCall(
790*f6aab3d8Srobert           cursor_sp, symbol_info);
791*f6aab3d8Srobert     }
792*f6aab3d8Srobert   }
793*f6aab3d8Srobert   if (IsSameInstructionSymbolContext(prev_symbol_info, symbol_info,
794*f6aab3d8Srobert                                      /*check_source_line_info=*/false)) {
795*f6aab3d8Srobert     // We are still in the same function. This can't be a call because otherwise
796*f6aab3d8Srobert     // we would be in the first instruction of the symbol.
797*f6aab3d8Srobert     last_function_call->GetLastTracedSegment().AppendInsn(cursor_sp,
798*f6aab3d8Srobert                                                           symbol_info);
799*f6aab3d8Srobert     return *last_function_call;
800*f6aab3d8Srobert   }
801*f6aab3d8Srobert   // Now we are in a different symbol. Let's see if this is a return or a
802*f6aab3d8Srobert   // call
803*f6aab3d8Srobert   const InstructionSP &insn = last_function_call->GetLastTracedSegment()
804*f6aab3d8Srobert                                   .GetLastInstructionSymbolInfo()
805*f6aab3d8Srobert                                   .instruction;
806*f6aab3d8Srobert   InstructionControlFlowKind insn_kind =
807*f6aab3d8Srobert       insn ? insn->GetControlFlowKind(&exe_ctx)
808*f6aab3d8Srobert            : eInstructionControlFlowKindOther;
809*f6aab3d8Srobert 
810*f6aab3d8Srobert   switch (insn_kind) {
811*f6aab3d8Srobert   case lldb::eInstructionControlFlowKindCall:
812*f6aab3d8Srobert   case lldb::eInstructionControlFlowKindFarCall: {
813*f6aab3d8Srobert     // This is a regular call
814*f6aab3d8Srobert     return last_function_call->GetLastTracedSegment().CreateNestedCall(
815*f6aab3d8Srobert         cursor_sp, symbol_info);
816*f6aab3d8Srobert   }
817*f6aab3d8Srobert   case lldb::eInstructionControlFlowKindFarReturn:
818*f6aab3d8Srobert   case lldb::eInstructionControlFlowKindReturn: {
819*f6aab3d8Srobert     // We should have caught most trampolines and linker functions earlier, so
820*f6aab3d8Srobert     // let's assume this is a regular return.
821*f6aab3d8Srobert     return AppendReturnedInstructionToFunctionCallForest(
822*f6aab3d8Srobert         *last_function_call, symbol_info, cursor_sp, roots);
823*f6aab3d8Srobert   }
824*f6aab3d8Srobert   default:
825*f6aab3d8Srobert     // we changed symbols not using a call or return and we are not in the
826*f6aab3d8Srobert     // beginning of a symbol, so this should be something very artificial
827*f6aab3d8Srobert     // or maybe a jump to some label in the middle of it section.
828*f6aab3d8Srobert 
829*f6aab3d8Srobert     // We first check if it's a return from an inline method
830*f6aab3d8Srobert     if (prev_symbol_info.sc.block &&
831*f6aab3d8Srobert         prev_symbol_info.sc.block->GetContainingInlinedBlock()) {
832*f6aab3d8Srobert       return AppendReturnedInstructionToFunctionCallForest(
833*f6aab3d8Srobert           *last_function_call, symbol_info, cursor_sp, roots);
834*f6aab3d8Srobert     }
835*f6aab3d8Srobert     // Now We assume it's a call. We should revisit this in the future.
836*f6aab3d8Srobert     // Ideally we should be able to decide whether to create a new tree,
837*f6aab3d8Srobert     // or go deeper or higher in the stack.
838*f6aab3d8Srobert     return last_function_call->GetLastTracedSegment().CreateNestedCall(
839*f6aab3d8Srobert         cursor_sp, symbol_info);
840*f6aab3d8Srobert   }
841*f6aab3d8Srobert }
842*f6aab3d8Srobert 
843*f6aab3d8Srobert /// Append an error to a function call forest. The new error might be appended
844*f6aab3d8Srobert /// to the current segment if it contains errors or will create a new root.
845*f6aab3d8Srobert ///
846*f6aab3d8Srobert /// \param[in] last_function_call
847*f6aab3d8Srobert ///   The chronologically most recent function call before the new error.
848*f6aab3d8Srobert ///
849*f6aab3d8Srobert /// \param[in] cursor_sp
850*f6aab3d8Srobert ///   The cursor pointing to the new error.
851*f6aab3d8Srobert ///
852*f6aab3d8Srobert /// \param[in,out] roots
853*f6aab3d8Srobert ///   The object owning the roots. It might be modified if a new root needs to
854*f6aab3d8Srobert ///   be created.
855*f6aab3d8Srobert ///
856*f6aab3d8Srobert /// \return
857*f6aab3d8Srobert ///   A reference to the function call that owns the new error.
AppendErrorToFunctionCallForest(TraceDumper::FunctionCall * last_function_call,TraceCursorSP & cursor_sp,std::vector<TraceDumper::FunctionCallUP> & roots)858*f6aab3d8Srobert TraceDumper::FunctionCall &AppendErrorToFunctionCallForest(
859*f6aab3d8Srobert     TraceDumper::FunctionCall *last_function_call, TraceCursorSP &cursor_sp,
860*f6aab3d8Srobert     std::vector<TraceDumper::FunctionCallUP> &roots) {
861*f6aab3d8Srobert   if (last_function_call && last_function_call->IsError()) {
862*f6aab3d8Srobert     last_function_call->GetLastTracedSegment().AppendInsn(
863*f6aab3d8Srobert         cursor_sp, TraceDumper::SymbolInfo{});
864*f6aab3d8Srobert     return *last_function_call;
865*f6aab3d8Srobert   } else {
866*f6aab3d8Srobert     roots.emplace_back(std::make_unique<TraceDumper::FunctionCall>(
867*f6aab3d8Srobert         cursor_sp, TraceDumper::SymbolInfo{}));
868*f6aab3d8Srobert     return *roots.back();
869*f6aab3d8Srobert   }
870*f6aab3d8Srobert }
871*f6aab3d8Srobert 
872*f6aab3d8Srobert static std::vector<TraceDumper::FunctionCallUP>
CreateFunctionCallForest(TraceCursorSP & cursor_sp,const ExecutionContext & exe_ctx)873*f6aab3d8Srobert CreateFunctionCallForest(TraceCursorSP &cursor_sp,
874*f6aab3d8Srobert                          const ExecutionContext &exe_ctx) {
875*f6aab3d8Srobert 
876*f6aab3d8Srobert   std::vector<TraceDumper::FunctionCallUP> roots;
877*f6aab3d8Srobert   TraceDumper::SymbolInfo prev_symbol_info;
878*f6aab3d8Srobert 
879*f6aab3d8Srobert   TraceDumper::FunctionCall *last_function_call = nullptr;
880*f6aab3d8Srobert 
881*f6aab3d8Srobert   for (; cursor_sp->HasValue(); cursor_sp->Next()) {
882*f6aab3d8Srobert     if (cursor_sp->IsError()) {
883*f6aab3d8Srobert       last_function_call = &AppendErrorToFunctionCallForest(last_function_call,
884*f6aab3d8Srobert                                                             cursor_sp, roots);
885*f6aab3d8Srobert       prev_symbol_info = {};
886*f6aab3d8Srobert     } else if (cursor_sp->IsInstruction()) {
887*f6aab3d8Srobert       TraceDumper::SymbolInfo symbol_info = CalculateSymbolInfo(
888*f6aab3d8Srobert           exe_ctx, cursor_sp->GetLoadAddress(), prev_symbol_info);
889*f6aab3d8Srobert 
890*f6aab3d8Srobert       last_function_call = &AppendInstructionToFunctionCallForest(
891*f6aab3d8Srobert           exe_ctx, last_function_call, prev_symbol_info, symbol_info, cursor_sp,
892*f6aab3d8Srobert           roots);
893*f6aab3d8Srobert       prev_symbol_info = symbol_info;
894*f6aab3d8Srobert     } else if (cursor_sp->GetEventType() == eTraceEventCPUChanged) {
895*f6aab3d8Srobert       // TODO: In case of a CPU change, we create a new root because we haven't
896*f6aab3d8Srobert       // investigated yet if a call tree can safely continue or if interrupts
897*f6aab3d8Srobert       // could have polluted the original call tree.
898*f6aab3d8Srobert       last_function_call = nullptr;
899*f6aab3d8Srobert       prev_symbol_info = {};
900*f6aab3d8Srobert     }
901*f6aab3d8Srobert   }
902*f6aab3d8Srobert 
903*f6aab3d8Srobert   return roots;
904*f6aab3d8Srobert }
905*f6aab3d8Srobert 
DumpFunctionCalls()906*f6aab3d8Srobert void TraceDumper::DumpFunctionCalls() {
907*f6aab3d8Srobert   ThreadSP thread_sp = m_cursor_sp->GetExecutionContextRef().GetThreadSP();
908*f6aab3d8Srobert   ExecutionContext exe_ctx;
909*f6aab3d8Srobert   thread_sp->GetProcess()->GetTarget().CalculateExecutionContext(exe_ctx);
910*f6aab3d8Srobert 
911*f6aab3d8Srobert   m_writer_up->FunctionCallForest(
912*f6aab3d8Srobert       CreateFunctionCallForest(m_cursor_sp, exe_ctx));
913*f6aab3d8Srobert }
914