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