LLDB  mainline
TraceInstructionDumper.cpp
Go to the documentation of this file.
1 //===-- TraceInstructionDumper.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 
10 
11 #include "lldb/Core/Module.h"
12 #include "lldb/Symbol/Function.h"
14 #include "lldb/Target/Process.h"
16 
17 using namespace lldb;
18 using namespace lldb_private;
19 using namespace llvm;
20 
21 TraceInstructionDumper::TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up,
22  int initial_index, bool raw,
23  bool show_tsc)
24  : m_cursor_up(std::move(cursor_up)), m_index(initial_index), m_raw(raw),
25  m_show_tsc(show_tsc) {}
26 
27 /// \return
28 /// Return \b true if the cursor could move one step.
30  if (!m_cursor_up->Next()) {
31  SetNoMoreData();
32  return false;
33  }
34  m_index += m_cursor_up->IsForwards() ? 1 : -1;
35  return true;
36 }
37 
38 /// \return
39 /// The number of characters that would be needed to print the given
40 /// integer.
41 static int GetNumberOfChars(int num) {
42  if (num == 0)
43  return 1;
44  return (num < 0 ? 1 : 0) + static_cast<int>(log10(abs(num))) + 1;
45 }
46 
47 /// Helper struct that holds symbol, disassembly and address information of an
48 /// instruction.
53  lldb::DisassemblerSP disassembler;
54  lldb::InstructionSP instruction;
56 };
57 
58 // This custom LineEntry validator is neded because some line_entries have
59 // 0 as line, which is meaningless. Notice that LineEntry::IsValid only
60 // checks that line is not LLDB_INVALID_LINE_NUMBER, i.e. UINT32_MAX.
61 static bool IsLineEntryValid(const LineEntry &line_entry) {
62  return line_entry.IsValid() && line_entry.line > 0;
63 }
64 
65 /// \return
66 /// \b true if the provided line entries match line, column and source file.
67 /// This function assumes that the line entries are valid.
68 static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) {
69  if (a.line != b.line)
70  return false;
71  if (a.column != b.column)
72  return false;
73  return a.file == b.file;
74 }
75 
76 /// Compare the symbol contexts of the provided \a InstructionSymbolInfo
77 /// objects.
78 ///
79 /// \return
80 /// \a true if both instructions belong to the same scope level analized
81 /// in the following order:
82 /// - module
83 /// - symbol
84 /// - function
85 /// - line
86 static bool
88  const InstructionSymbolInfo &insn) {
89  // module checks
90  if (insn.sc.module_sp != prev_insn.sc.module_sp)
91  return false;
92 
93  // symbol checks
94  if (insn.sc.symbol != prev_insn.sc.symbol)
95  return false;
96 
97  // function checks
98  if (!insn.sc.function && !prev_insn.sc.function)
99  return true;
100  else if (insn.sc.function != prev_insn.sc.function)
101  return false;
102 
103  // line entry checks
104  const bool curr_line_valid = IsLineEntryValid(insn.sc.line_entry);
105  const bool prev_line_valid = IsLineEntryValid(prev_insn.sc.line_entry);
106  if (curr_line_valid && prev_line_valid)
108  prev_insn.sc.line_entry);
109  return curr_line_valid == prev_line_valid;
110 }
111 
112 /// Dump the symbol context of the given instruction address if it's different
113 /// from the symbol context of the previous instruction in the trace.
114 ///
115 /// \param[in] prev_sc
116 /// The symbol context of the previous instruction in the trace.
117 ///
118 /// \param[in] address
119 /// The address whose symbol information will be dumped.
120 ///
121 /// \return
122 /// The symbol context of the current address, which might differ from the
123 /// previous one.
124 static void
126  Optional<InstructionSymbolInfo> prev_insn,
127  InstructionSymbolInfo &insn) {
128  if (prev_insn && IsSameInstructionSymbolContext(*prev_insn, insn))
129  return;
130 
131  s.Printf(" ");
132 
133  if (!insn.sc.module_sp)
134  s.Printf("(none)");
135  else if (!insn.sc.function && !insn.sc.symbol)
136  s.Printf("%s`(none)",
137  insn.sc.module_sp->GetFileSpec().GetFilename().AsCString());
138  else
139  insn.sc.DumpStopContext(&s, insn.exe_ctx.GetTargetPtr(), insn.address,
140  /*show_fullpath=*/false,
141  /*show_module=*/true, /*show_inlined_frames=*/false,
142  /*show_function_arguments=*/true,
143  /*show_function_name=*/true);
144  s.Printf("\n");
145 }
146 
148  if (!insn.instruction)
149  return;
150  s.Printf(" ");
151  insn.instruction->Dump(&s, /*show_address=*/false, /*show_bytes=*/false,
152  /*max_opcode_byte_size=*/0, &insn.exe_ctx, &insn.sc,
153  /*prev_sym_ctx=*/nullptr,
154  /*disassembly_addr_format=*/nullptr,
155  /*max_address_text_size=*/0);
156 }
157 
159 
161 
163  ThreadSP thread_sp = m_cursor_up->GetExecutionContextRef().GetThreadSP();
164  if (!thread_sp) {
165  s.Printf("invalid thread");
166  return;
167  }
168 
169  s.Printf("thread #%u: tid = %" PRIu64 "\n", thread_sp->GetIndexID(),
170  thread_sp->GetID());
171 
172  int digits_count = GetNumberOfChars(
173  m_cursor_up->IsForwards() ? m_index + count - 1 : m_index - count + 1);
174  bool was_prev_instruction_an_error = false;
175 
176  auto printMissingInstructionsMessage = [&]() {
177  s.Printf(" ...missing instructions\n");
178  };
179 
180  auto printInstructionIndex = [&]() {
181  s.Printf(" [%*d] ", digits_count, m_index);
182 
183  if (m_show_tsc) {
184  s.Printf("[tsc=");
185 
186  if (Optional<uint64_t> timestamp = m_cursor_up->GetTimestampCounter())
187  s.Printf("0x%016" PRIx64, *timestamp);
188  else
189  s.Printf("unavailable");
190 
191  s.Printf("] ");
192  }
193  };
194 
195  InstructionSymbolInfo prev_insn_info;
196 
197  Target &target = thread_sp->GetProcess()->GetTarget();
198  ExecutionContext exe_ctx;
199  target.CalculateExecutionContext(exe_ctx);
200  const ArchSpec &arch = target.GetArchitecture();
201 
202  // Find the symbol context for the given address reusing the previous
203  // instruction's symbol context when possible.
204  auto calculateSymbolContext = [&](const Address &address) {
205  AddressRange range;
206  if (prev_insn_info.sc.GetAddressRange(eSymbolContextEverything, 0,
207  /*inline_block_range*/ false,
208  range) &&
209  range.Contains(address))
210  return prev_insn_info.sc;
211 
212  SymbolContext sc;
213  address.CalculateSymbolContext(&sc, eSymbolContextEverything);
214  return sc;
215  };
216 
217  // Find the disassembler for the given address reusing the previous
218  // instruction's disassembler when possible.
219  auto calculateDisass = [&](const Address &address, const SymbolContext &sc) {
220  if (prev_insn_info.disassembler) {
221  if (InstructionSP instruction =
222  prev_insn_info.disassembler->GetInstructionList()
223  .GetInstructionAtAddress(address))
224  return std::make_tuple(prev_insn_info.disassembler, instruction);
225  }
226 
227  if (sc.function) {
228  if (DisassemblerSP disassembler =
229  sc.function->GetInstructions(exe_ctx, nullptr)) {
230  if (InstructionSP instruction =
231  disassembler->GetInstructionList().GetInstructionAtAddress(
232  address))
233  return std::make_tuple(disassembler, instruction);
234  }
235  }
236  // We fallback to a single instruction disassembler
237  AddressRange range(address, arch.GetMaximumOpcodeByteSize());
238  DisassemblerSP disassembler =
239  Disassembler::DisassembleRange(arch, /*plugin_name*/ nullptr,
240  /*flavor*/ nullptr, target, range);
241  return std::make_tuple(disassembler,
242  disassembler ? disassembler->GetInstructionList()
243  .GetInstructionAtAddress(address)
244  : InstructionSP());
245  };
246 
247  for (size_t i = 0; i < count; i++) {
248  if (!HasMoreData()) {
249  s.Printf(" no more data\n");
250  break;
251  }
252 
253  if (Error err = m_cursor_up->GetError()) {
254  if (!m_cursor_up->IsForwards() && !was_prev_instruction_an_error)
255  printMissingInstructionsMessage();
256 
257  was_prev_instruction_an_error = true;
258 
259  printInstructionIndex();
260  s << toString(std::move(err));
261  } else {
262  if (m_cursor_up->IsForwards() && was_prev_instruction_an_error)
263  printMissingInstructionsMessage();
264 
265  was_prev_instruction_an_error = false;
266 
267  InstructionSymbolInfo insn_info;
268 
269  if (!m_raw) {
270  insn_info.load_address = m_cursor_up->GetLoadAddress();
271  insn_info.exe_ctx = exe_ctx;
272  insn_info.address.SetLoadAddress(insn_info.load_address, &target);
273  insn_info.sc = calculateSymbolContext(insn_info.address);
274  std::tie(insn_info.disassembler, insn_info.instruction) =
275  calculateDisass(insn_info.address, insn_info.sc);
276 
277  DumpInstructionSymbolContext(s, prev_insn_info, insn_info);
278  }
279 
280  printInstructionIndex();
281  s.Printf("0x%016" PRIx64, m_cursor_up->GetLoadAddress());
282 
283  if (!m_raw)
284  DumpInstructionDisassembly(s, insn_info);
285 
286  prev_insn_info = insn_info;
287  }
288 
289  s.Printf("\n");
290  TryMoveOneStep();
291  }
292 }
lldb_private::toString
const char * toString(AppleArm64ExceptionClass EC)
Definition: AppleArm64ExceptionClass.h:38
lldb_private::Address::SetLoadAddress
bool SetLoadAddress(lldb::addr_t load_addr, Target *target, bool allow_section_end=false)
Set the address to represent load_addr.
Definition: Address.cpp:1023
lldb_private::ExecutionContext
Definition: ExecutionContext.h:292
llvm
Definition: Debugger.h:49
lldb_private::LineEntry::file
FileSpec file
The source file, possibly mapped by the target.source-map setting.
Definition: LineEntry.h:140
lldb_private::ArchSpec
Definition: ArchSpec.h:33
DumpInstructionSymbolContext
static void DumpInstructionSymbolContext(Stream &s, Optional< InstructionSymbolInfo > prev_insn, InstructionSymbolInfo &insn)
Dump the symbol context of the given instruction address if it's different from the symbol context of...
Definition: TraceInstructionDumper.cpp:125
lldb_private::ArchSpec::GetMaximumOpcodeByteSize
uint32_t GetMaximumOpcodeByteSize() const
Definition: ArchSpec.cpp:923
lldb_private::TraceInstructionDumper::SetNoMoreData
void SetNoMoreData()
Indicate the dumper that no more data is available in the trace.
Definition: TraceInstructionDumper.cpp:158
lldb_private::LineEntry::IsValid
bool IsValid() const
Check if a line entry object is valid.
Definition: LineEntry.cpp:46
lldb_private::SymbolContext::GetAddressRange
bool GetAddressRange(uint32_t scope, uint32_t range_idx, bool use_inline_block_range, AddressRange &range) const
Get the address range contained within a symbol context.
Definition: SymbolContext.cpp:379
Module.h
InstructionSymbolInfo::exe_ctx
lldb_private::ExecutionContext exe_ctx
Definition: TraceInstructionDumper.cpp:55
DumpInstructionDisassembly
static void DumpInstructionDisassembly(Stream &s, InstructionSymbolInfo &insn)
Definition: TraceInstructionDumper.cpp:147
SectionLoadList.h
lldb_private::Stream
Definition: Stream.h:28
lldb::addr_t
uint64_t addr_t
Definition: lldb-types.h:83
TraceInstructionDumper.h
lldb_private::SymbolContext
Definition: SymbolContext.h:33
lldb_private::Target
Definition: Target.h:450
Process.h
lldb_private::TraceInstructionDumper::HasMoreData
bool HasMoreData()
Definition: TraceInstructionDumper.cpp:160
InstructionSymbolInfo
Helper struct that holds symbol, disassembly and address information of an instruction.
Definition: TraceInstructionDumper.cpp:49
lldb_private::TraceInstructionDumper::m_raw
bool m_raw
Definition: TraceInstructionDumper.h:69
lldb_private::SymbolContext::symbol
Symbol * symbol
The Symbol for a given query.
Definition: SymbolContext.h:323
lldb_private::SymbolContext::DumpStopContext
bool DumpStopContext(Stream *s, ExecutionContextScope *exe_scope, const Address &so_addr, bool show_fullpaths, bool show_module, bool show_inlined_frames, bool show_function_arguments, bool show_function_name) const
Dump the stop context in this object to a Stream.
Definition: SymbolContext.cpp:67
lldb_private::Disassembler::DisassembleRange
static lldb::DisassemblerSP DisassembleRange(const ArchSpec &arch, const char *plugin_name, const char *flavor, Target &target, const AddressRange &disasm_range, bool force_live_memory=false)
Definition: Disassembler.cpp:123
lldb_private::SymbolContext::function
Function * function
The Function for a given query.
Definition: SymbolContext.h:320
GetNumberOfChars
static int GetNumberOfChars(int num)
Definition: TraceInstructionDumper.cpp:41
lldb_private::AddressRange
Definition: AddressRange.h:25
lldb_private::LineEntry::column
uint16_t column
The column number of the source line, or zero if there is no column information.
Definition: LineEntry.h:146
lldb_private::LineEntry::line
uint32_t line
The source line number, or zero if there is no line number information.
Definition: LineEntry.h:143
InstructionSymbolInfo::load_address
lldb::addr_t load_address
Definition: TraceInstructionDumper.cpp:52
lldb_private::TraceInstructionDumper::m_index
int m_index
Definition: TraceInstructionDumper.h:68
IsLineEntryValid
static bool IsLineEntryValid(const LineEntry &line_entry)
Definition: TraceInstructionDumper.cpp:61
lldb_private::Target::GetArchitecture
const ArchSpec & GetArchitecture() const
Definition: Target.h:966
InstructionSymbolInfo::sc
SymbolContext sc
Definition: TraceInstructionDumper.cpp:50
lldb_private::Target::CalculateExecutionContext
void CalculateExecutionContext(ExecutionContext &exe_ctx) override
Reconstruct the object's execution context into sc.
Definition: Target.cpp:2185
lldb_private::Address
Definition: Address.h:59
InstructionSymbolInfo::address
Address address
Definition: TraceInstructionDumper.cpp:51
lldb_private::SymbolContext::module_sp
lldb::ModuleSP module_sp
The Module for a given query.
Definition: SymbolContext.h:318
Function.h
lldb_private::Stream::Printf
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:107
InstructionSymbolInfo::disassembler
lldb::DisassemblerSP disassembler
Definition: TraceInstructionDumper.cpp:53
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
lldb_private::TraceInstructionDumper::m_show_tsc
bool m_show_tsc
Definition: TraceInstructionDumper.h:70
Error
llvm::Error Error
Definition: UdtRecordCompleter.cpp:29
lldb_private::TraceInstructionDumper::DumpInstructions
void DumpInstructions(Stream &s, size_t count)
Dump count instructions of the thread trace starting at the current cursor position.
Definition: TraceInstructionDumper.cpp:162
lldb_private::TraceInstructionDumper::m_cursor_up
lldb::TraceCursorUP m_cursor_up
Definition: TraceInstructionDumper.h:67
IsSameInstructionSymbolContext
static bool IsSameInstructionSymbolContext(const InstructionSymbolInfo &prev_insn, const InstructionSymbolInfo &insn)
Compare the symbol contexts of the provided InstructionSymbolInfo objects.
Definition: TraceInstructionDumper.cpp:87
lldb_private::SymbolContext::line_entry
LineEntry line_entry
The LineEntry for a given query.
Definition: SymbolContext.h:322
lldb_private::AddressRange::Contains
bool Contains(const Address &so_addr) const
Check if a section offset address is contained in this range.
Definition: AddressRange.cpp:46
InstructionSymbolInfo::instruction
lldb::InstructionSP instruction
Definition: TraceInstructionDumper.cpp:54
lldb_private::TraceInstructionDumper::m_no_more_data
bool m_no_more_data
If true, all the instructions have been traversed.
Definition: TraceInstructionDumper.h:72
lldb
Definition: SBAddress.h:15
lldb_private::TraceInstructionDumper::TryMoveOneStep
bool TryMoveOneStep()
Move the cursor one step.
Definition: TraceInstructionDumper.cpp:29
lldb_private::LineEntry
Definition: LineEntry.h:20
lldb_private::ExecutionContext::GetTargetPtr
Target * GetTargetPtr() const
Returns a pointer to the target object.
Definition: ExecutionContext.cpp:200
ExecutionContext.h
FileLineAndColumnMatches
static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b)
Definition: TraceInstructionDumper.cpp:68