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