xref: /freebsd-src/contrib/llvm-project/lldb/source/Target/Trace.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
1*e8d8bef9SDimitry Andric //===-- Trace.cpp ---------------------------------------------------------===//
2*e8d8bef9SDimitry Andric //
3*e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*e8d8bef9SDimitry Andric //
7*e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8*e8d8bef9SDimitry Andric 
9*e8d8bef9SDimitry Andric #include "lldb/Target/Trace.h"
10*e8d8bef9SDimitry Andric 
11*e8d8bef9SDimitry Andric #include "llvm/Support/Format.h"
12*e8d8bef9SDimitry Andric 
13*e8d8bef9SDimitry Andric #include "lldb/Core/Module.h"
14*e8d8bef9SDimitry Andric #include "lldb/Core/PluginManager.h"
15*e8d8bef9SDimitry Andric #include "lldb/Symbol/Function.h"
16*e8d8bef9SDimitry Andric #include "lldb/Target/Process.h"
17*e8d8bef9SDimitry Andric #include "lldb/Target/SectionLoadList.h"
18*e8d8bef9SDimitry Andric #include "lldb/Target/Thread.h"
19*e8d8bef9SDimitry Andric #include "lldb/Utility/Stream.h"
20*e8d8bef9SDimitry Andric 
21*e8d8bef9SDimitry Andric using namespace lldb;
22*e8d8bef9SDimitry Andric using namespace lldb_private;
23*e8d8bef9SDimitry Andric using namespace llvm;
24*e8d8bef9SDimitry Andric 
25*e8d8bef9SDimitry Andric // Helper structs used to extract the type of a trace session json without
26*e8d8bef9SDimitry Andric // having to parse the entire object.
27*e8d8bef9SDimitry Andric 
28*e8d8bef9SDimitry Andric struct JSONSimplePluginSettings {
29*e8d8bef9SDimitry Andric   std::string type;
30*e8d8bef9SDimitry Andric };
31*e8d8bef9SDimitry Andric 
32*e8d8bef9SDimitry Andric struct JSONSimpleTraceSession {
33*e8d8bef9SDimitry Andric   JSONSimplePluginSettings trace;
34*e8d8bef9SDimitry Andric };
35*e8d8bef9SDimitry Andric 
36*e8d8bef9SDimitry Andric namespace llvm {
37*e8d8bef9SDimitry Andric namespace json {
38*e8d8bef9SDimitry Andric 
39*e8d8bef9SDimitry Andric bool fromJSON(const Value &value, JSONSimplePluginSettings &plugin_settings,
40*e8d8bef9SDimitry Andric               Path path) {
41*e8d8bef9SDimitry Andric   json::ObjectMapper o(value, path);
42*e8d8bef9SDimitry Andric   return o && o.map("type", plugin_settings.type);
43*e8d8bef9SDimitry Andric }
44*e8d8bef9SDimitry Andric 
45*e8d8bef9SDimitry Andric bool fromJSON(const Value &value, JSONSimpleTraceSession &session, Path path) {
46*e8d8bef9SDimitry Andric   json::ObjectMapper o(value, path);
47*e8d8bef9SDimitry Andric   return o && o.map("trace", session.trace);
48*e8d8bef9SDimitry Andric }
49*e8d8bef9SDimitry Andric 
50*e8d8bef9SDimitry Andric } // namespace json
51*e8d8bef9SDimitry Andric } // namespace llvm
52*e8d8bef9SDimitry Andric 
53*e8d8bef9SDimitry Andric static Error createInvalidPlugInError(StringRef plugin_name) {
54*e8d8bef9SDimitry Andric   return createStringError(
55*e8d8bef9SDimitry Andric       std::errc::invalid_argument,
56*e8d8bef9SDimitry Andric       "no trace plug-in matches the specified type: \"%s\"",
57*e8d8bef9SDimitry Andric       plugin_name.data());
58*e8d8bef9SDimitry Andric }
59*e8d8bef9SDimitry Andric 
60*e8d8bef9SDimitry Andric Expected<lldb::TraceSP> Trace::FindPlugin(Debugger &debugger,
61*e8d8bef9SDimitry Andric                                           const json::Value &trace_session_file,
62*e8d8bef9SDimitry Andric                                           StringRef session_file_dir) {
63*e8d8bef9SDimitry Andric   JSONSimpleTraceSession json_session;
64*e8d8bef9SDimitry Andric   json::Path::Root root("traceSession");
65*e8d8bef9SDimitry Andric   if (!json::fromJSON(trace_session_file, json_session, root))
66*e8d8bef9SDimitry Andric     return root.getError();
67*e8d8bef9SDimitry Andric 
68*e8d8bef9SDimitry Andric   ConstString plugin_name(json_session.trace.type);
69*e8d8bef9SDimitry Andric   if (auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name))
70*e8d8bef9SDimitry Andric     return create_callback(trace_session_file, session_file_dir, debugger);
71*e8d8bef9SDimitry Andric 
72*e8d8bef9SDimitry Andric   return createInvalidPlugInError(json_session.trace.type);
73*e8d8bef9SDimitry Andric }
74*e8d8bef9SDimitry Andric 
75*e8d8bef9SDimitry Andric Expected<StringRef> Trace::FindPluginSchema(StringRef name) {
76*e8d8bef9SDimitry Andric   ConstString plugin_name(name);
77*e8d8bef9SDimitry Andric   StringRef schema = PluginManager::GetTraceSchema(plugin_name);
78*e8d8bef9SDimitry Andric   if (!schema.empty())
79*e8d8bef9SDimitry Andric     return schema;
80*e8d8bef9SDimitry Andric 
81*e8d8bef9SDimitry Andric   return createInvalidPlugInError(name);
82*e8d8bef9SDimitry Andric }
83*e8d8bef9SDimitry Andric 
84*e8d8bef9SDimitry Andric static int GetNumberOfDigits(size_t num) {
85*e8d8bef9SDimitry Andric   return num == 0 ? 1 : static_cast<int>(log10(num)) + 1;
86*e8d8bef9SDimitry Andric }
87*e8d8bef9SDimitry Andric 
88*e8d8bef9SDimitry Andric /// Dump the symbol context of the given instruction address if it's different
89*e8d8bef9SDimitry Andric /// from the symbol context of the previous instruction in the trace.
90*e8d8bef9SDimitry Andric ///
91*e8d8bef9SDimitry Andric /// \param[in] prev_sc
92*e8d8bef9SDimitry Andric ///     The symbol context of the previous instruction in the trace.
93*e8d8bef9SDimitry Andric ///
94*e8d8bef9SDimitry Andric /// \param[in] address
95*e8d8bef9SDimitry Andric ///     The address whose symbol information will be dumped.
96*e8d8bef9SDimitry Andric ///
97*e8d8bef9SDimitry Andric /// \return
98*e8d8bef9SDimitry Andric ///     The symbol context of the current address, which might differ from the
99*e8d8bef9SDimitry Andric ///     previous one.
100*e8d8bef9SDimitry Andric static SymbolContext DumpSymbolContext(Stream &s, const SymbolContext &prev_sc,
101*e8d8bef9SDimitry Andric                                        Target &target, const Address &address) {
102*e8d8bef9SDimitry Andric   AddressRange range;
103*e8d8bef9SDimitry Andric   if (prev_sc.GetAddressRange(eSymbolContextEverything, 0,
104*e8d8bef9SDimitry Andric                               /*inline_block_range*/ false, range) &&
105*e8d8bef9SDimitry Andric       range.ContainsFileAddress(address))
106*e8d8bef9SDimitry Andric     return prev_sc;
107*e8d8bef9SDimitry Andric 
108*e8d8bef9SDimitry Andric   SymbolContext sc;
109*e8d8bef9SDimitry Andric   address.CalculateSymbolContext(&sc, eSymbolContextEverything);
110*e8d8bef9SDimitry Andric 
111*e8d8bef9SDimitry Andric   if (!prev_sc.module_sp && !sc.module_sp)
112*e8d8bef9SDimitry Andric     return sc;
113*e8d8bef9SDimitry Andric   if (prev_sc.module_sp == sc.module_sp && !sc.function && !sc.symbol &&
114*e8d8bef9SDimitry Andric       !prev_sc.function && !prev_sc.symbol)
115*e8d8bef9SDimitry Andric     return sc;
116*e8d8bef9SDimitry Andric 
117*e8d8bef9SDimitry Andric   s.Printf("  ");
118*e8d8bef9SDimitry Andric 
119*e8d8bef9SDimitry Andric   if (!sc.module_sp)
120*e8d8bef9SDimitry Andric     s.Printf("(none)");
121*e8d8bef9SDimitry Andric   else if (!sc.function && !sc.symbol)
122*e8d8bef9SDimitry Andric     s.Printf("%s`(none)",
123*e8d8bef9SDimitry Andric              sc.module_sp->GetFileSpec().GetFilename().AsCString());
124*e8d8bef9SDimitry Andric   else
125*e8d8bef9SDimitry Andric     sc.DumpStopContext(&s, &target, address, /*show_fullpath*/ false,
126*e8d8bef9SDimitry Andric                        /*show_module*/ true, /*show_inlined_frames*/ false,
127*e8d8bef9SDimitry Andric                        /*show_function_arguments*/ true,
128*e8d8bef9SDimitry Andric                        /*show_function_name*/ true,
129*e8d8bef9SDimitry Andric                        /*show_inline_callsite_line_info*/ false);
130*e8d8bef9SDimitry Andric   s.Printf("\n");
131*e8d8bef9SDimitry Andric   return sc;
132*e8d8bef9SDimitry Andric }
133*e8d8bef9SDimitry Andric 
134*e8d8bef9SDimitry Andric /// Dump an instruction given by its address using a given disassembler, unless
135*e8d8bef9SDimitry Andric /// the instruction is not present in the disassembler.
136*e8d8bef9SDimitry Andric ///
137*e8d8bef9SDimitry Andric /// \param[in] disassembler
138*e8d8bef9SDimitry Andric ///     A disassembler containing a certain instruction list.
139*e8d8bef9SDimitry Andric ///
140*e8d8bef9SDimitry Andric /// \param[in] address
141*e8d8bef9SDimitry Andric ///     The address of the instruction to dump.
142*e8d8bef9SDimitry Andric ///
143*e8d8bef9SDimitry Andric /// \return
144*e8d8bef9SDimitry Andric ///     \b true if the information could be dumped, \b false otherwise.
145*e8d8bef9SDimitry Andric static bool TryDumpInstructionInfo(Stream &s,
146*e8d8bef9SDimitry Andric                                    const DisassemblerSP &disassembler,
147*e8d8bef9SDimitry Andric                                    const ExecutionContext &exe_ctx,
148*e8d8bef9SDimitry Andric                                    const Address &address) {
149*e8d8bef9SDimitry Andric   if (!disassembler)
150*e8d8bef9SDimitry Andric     return false;
151*e8d8bef9SDimitry Andric 
152*e8d8bef9SDimitry Andric   if (InstructionSP instruction =
153*e8d8bef9SDimitry Andric           disassembler->GetInstructionList().GetInstructionAtAddress(address)) {
154*e8d8bef9SDimitry Andric     instruction->Dump(&s, /*show_address*/ false, /*show_bytes*/ false,
155*e8d8bef9SDimitry Andric                       /*max_opcode_byte_size*/ 0, &exe_ctx,
156*e8d8bef9SDimitry Andric                       /*sym_ctx*/ nullptr, /*prev_sym_ctx*/ nullptr,
157*e8d8bef9SDimitry Andric                       /*disassembly_addr_format*/ nullptr,
158*e8d8bef9SDimitry Andric                       /*max_address_text_size*/ 0);
159*e8d8bef9SDimitry Andric     return true;
160*e8d8bef9SDimitry Andric   }
161*e8d8bef9SDimitry Andric 
162*e8d8bef9SDimitry Andric   return false;
163*e8d8bef9SDimitry Andric }
164*e8d8bef9SDimitry Andric 
165*e8d8bef9SDimitry Andric /// Dump an instruction instruction given by its address.
166*e8d8bef9SDimitry Andric ///
167*e8d8bef9SDimitry Andric /// \param[in] prev_disassembler
168*e8d8bef9SDimitry Andric ///     The disassembler that was used to dump the previous instruction in the
169*e8d8bef9SDimitry Andric ///     trace. It is useful to avoid recomputations.
170*e8d8bef9SDimitry Andric ///
171*e8d8bef9SDimitry Andric /// \param[in] address
172*e8d8bef9SDimitry Andric ///     The address of the instruction to dump.
173*e8d8bef9SDimitry Andric ///
174*e8d8bef9SDimitry Andric /// \return
175*e8d8bef9SDimitry Andric ///     A disassembler that contains the given instruction, which might differ
176*e8d8bef9SDimitry Andric ///     from the previous disassembler.
177*e8d8bef9SDimitry Andric static DisassemblerSP
178*e8d8bef9SDimitry Andric DumpInstructionInfo(Stream &s, const SymbolContext &sc,
179*e8d8bef9SDimitry Andric                     const DisassemblerSP &prev_disassembler,
180*e8d8bef9SDimitry Andric                     ExecutionContext &exe_ctx, const Address &address) {
181*e8d8bef9SDimitry Andric   // We first try to use the previous disassembler
182*e8d8bef9SDimitry Andric   if (TryDumpInstructionInfo(s, prev_disassembler, exe_ctx, address))
183*e8d8bef9SDimitry Andric     return prev_disassembler;
184*e8d8bef9SDimitry Andric 
185*e8d8bef9SDimitry Andric   // Now we try using the current function's disassembler
186*e8d8bef9SDimitry Andric   if (sc.function) {
187*e8d8bef9SDimitry Andric     DisassemblerSP disassembler =
188*e8d8bef9SDimitry Andric         sc.function->GetInstructions(exe_ctx, nullptr, true);
189*e8d8bef9SDimitry Andric     if (TryDumpInstructionInfo(s, disassembler, exe_ctx, address))
190*e8d8bef9SDimitry Andric       return disassembler;
191*e8d8bef9SDimitry Andric   }
192*e8d8bef9SDimitry Andric 
193*e8d8bef9SDimitry Andric   // We fallback to disassembly one instruction
194*e8d8bef9SDimitry Andric   Target &target = exe_ctx.GetTargetRef();
195*e8d8bef9SDimitry Andric   const ArchSpec &arch = target.GetArchitecture();
196*e8d8bef9SDimitry Andric   AddressRange range(address, arch.GetMaximumOpcodeByteSize() * 1);
197*e8d8bef9SDimitry Andric   DisassemblerSP disassembler = Disassembler::DisassembleRange(
198*e8d8bef9SDimitry Andric       arch, /*plugin_name*/ nullptr,
199*e8d8bef9SDimitry Andric       /*flavor*/ nullptr, target, range, /*prefer_file_cache*/ true);
200*e8d8bef9SDimitry Andric   if (TryDumpInstructionInfo(s, disassembler, exe_ctx, address))
201*e8d8bef9SDimitry Andric     return disassembler;
202*e8d8bef9SDimitry Andric   return nullptr;
203*e8d8bef9SDimitry Andric }
204*e8d8bef9SDimitry Andric 
205*e8d8bef9SDimitry Andric void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
206*e8d8bef9SDimitry Andric                                   size_t end_position, bool raw) {
207*e8d8bef9SDimitry Andric   size_t instructions_count = GetInstructionCount(thread);
208*e8d8bef9SDimitry Andric   s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = %zu\n",
209*e8d8bef9SDimitry Andric            thread.GetIndexID(), thread.GetID(), instructions_count);
210*e8d8bef9SDimitry Andric 
211*e8d8bef9SDimitry Andric   if (count == 0 || end_position >= instructions_count)
212*e8d8bef9SDimitry Andric     return;
213*e8d8bef9SDimitry Andric 
214*e8d8bef9SDimitry Andric   size_t start_position =
215*e8d8bef9SDimitry Andric       end_position + 1 < count ? 0 : end_position + 1 - count;
216*e8d8bef9SDimitry Andric 
217*e8d8bef9SDimitry Andric   int digits_count = GetNumberOfDigits(end_position);
218*e8d8bef9SDimitry Andric   auto printInstructionIndex = [&](size_t index) {
219*e8d8bef9SDimitry Andric     s.Printf("    [%*zu] ", digits_count, index);
220*e8d8bef9SDimitry Andric   };
221*e8d8bef9SDimitry Andric 
222*e8d8bef9SDimitry Andric   bool was_prev_instruction_an_error = false;
223*e8d8bef9SDimitry Andric   Target &target = thread.GetProcess()->GetTarget();
224*e8d8bef9SDimitry Andric 
225*e8d8bef9SDimitry Andric   SymbolContext sc;
226*e8d8bef9SDimitry Andric   DisassemblerSP disassembler;
227*e8d8bef9SDimitry Andric   ExecutionContext exe_ctx;
228*e8d8bef9SDimitry Andric   target.CalculateExecutionContext(exe_ctx);
229*e8d8bef9SDimitry Andric 
230*e8d8bef9SDimitry Andric   TraverseInstructions(
231*e8d8bef9SDimitry Andric       thread, start_position, TraceDirection::Forwards,
232*e8d8bef9SDimitry Andric       [&](size_t index, Expected<lldb::addr_t> load_address) -> bool {
233*e8d8bef9SDimitry Andric         if (load_address) {
234*e8d8bef9SDimitry Andric           // We print an empty line after a sequence of errors to show more
235*e8d8bef9SDimitry Andric           // clearly that there's a gap in the trace
236*e8d8bef9SDimitry Andric           if (was_prev_instruction_an_error)
237*e8d8bef9SDimitry Andric             s.Printf("    ...missing instructions\n");
238*e8d8bef9SDimitry Andric 
239*e8d8bef9SDimitry Andric           Address address;
240*e8d8bef9SDimitry Andric           if (!raw) {
241*e8d8bef9SDimitry Andric             target.GetSectionLoadList().ResolveLoadAddress(*load_address,
242*e8d8bef9SDimitry Andric                                                            address);
243*e8d8bef9SDimitry Andric 
244*e8d8bef9SDimitry Andric             sc = DumpSymbolContext(s, sc, target, address);
245*e8d8bef9SDimitry Andric           }
246*e8d8bef9SDimitry Andric 
247*e8d8bef9SDimitry Andric           printInstructionIndex(index);
248*e8d8bef9SDimitry Andric           s.Printf("0x%016" PRIx64 "    ", *load_address);
249*e8d8bef9SDimitry Andric 
250*e8d8bef9SDimitry Andric           if (!raw) {
251*e8d8bef9SDimitry Andric             disassembler =
252*e8d8bef9SDimitry Andric                 DumpInstructionInfo(s, sc, disassembler, exe_ctx, address);
253*e8d8bef9SDimitry Andric           }
254*e8d8bef9SDimitry Andric 
255*e8d8bef9SDimitry Andric           was_prev_instruction_an_error = false;
256*e8d8bef9SDimitry Andric         } else {
257*e8d8bef9SDimitry Andric           printInstructionIndex(index);
258*e8d8bef9SDimitry Andric           s << toString(load_address.takeError());
259*e8d8bef9SDimitry Andric           was_prev_instruction_an_error = true;
260*e8d8bef9SDimitry Andric           if (!raw)
261*e8d8bef9SDimitry Andric             sc = SymbolContext();
262*e8d8bef9SDimitry Andric         }
263*e8d8bef9SDimitry Andric 
264*e8d8bef9SDimitry Andric         s.Printf("\n");
265*e8d8bef9SDimitry Andric 
266*e8d8bef9SDimitry Andric         return index < end_position;
267*e8d8bef9SDimitry Andric       });
268*e8d8bef9SDimitry Andric }
269