diff options
Diffstat (limited to 'lldb/source/Target/Trace.cpp')
| -rw-r--r-- | lldb/source/Target/Trace.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp new file mode 100644 index 000000000000..d19115b2d1f9 --- /dev/null +++ b/lldb/source/Target/Trace.cpp @@ -0,0 +1,268 @@ +//===-- Trace.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/Trace.h" + +#include "llvm/Support/Format.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +// Helper structs used to extract the type of a trace session json without +// having to parse the entire object. + +struct JSONSimplePluginSettings { + std::string type; +}; + +struct JSONSimpleTraceSession { + JSONSimplePluginSettings trace; +}; + +namespace llvm { +namespace json { + +bool fromJSON(const Value &value, JSONSimplePluginSettings &plugin_settings, + Path path) { + json::ObjectMapper o(value, path); + return o && o.map("type", plugin_settings.type); +} + +bool fromJSON(const Value &value, JSONSimpleTraceSession &session, Path path) { + json::ObjectMapper o(value, path); + return o && o.map("trace", session.trace); +} + +} // namespace json +} // namespace llvm + +static Error createInvalidPlugInError(StringRef plugin_name) { + return createStringError( + std::errc::invalid_argument, + "no trace plug-in matches the specified type: \"%s\"", + plugin_name.data()); +} + +Expected<lldb::TraceSP> Trace::FindPlugin(Debugger &debugger, + const json::Value &trace_session_file, + StringRef session_file_dir) { + JSONSimpleTraceSession json_session; + json::Path::Root root("traceSession"); + if (!json::fromJSON(trace_session_file, json_session, root)) + return root.getError(); + + ConstString plugin_name(json_session.trace.type); + if (auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name)) + return create_callback(trace_session_file, session_file_dir, debugger); + + return createInvalidPlugInError(json_session.trace.type); +} + +Expected<StringRef> Trace::FindPluginSchema(StringRef name) { + ConstString plugin_name(name); + StringRef schema = PluginManager::GetTraceSchema(plugin_name); + if (!schema.empty()) + return schema; + + return createInvalidPlugInError(name); +} + +static int GetNumberOfDigits(size_t num) { + return num == 0 ? 1 : static_cast<int>(log10(num)) + 1; +} + +/// Dump the symbol context of the given instruction address if it's different +/// from the symbol context of the previous instruction in the trace. +/// +/// \param[in] prev_sc +/// The symbol context of the previous instruction in the trace. +/// +/// \param[in] address +/// The address whose symbol information will be dumped. +/// +/// \return +/// The symbol context of the current address, which might differ from the +/// previous one. +static SymbolContext DumpSymbolContext(Stream &s, const SymbolContext &prev_sc, + Target &target, const Address &address) { + AddressRange range; + if (prev_sc.GetAddressRange(eSymbolContextEverything, 0, + /*inline_block_range*/ false, range) && + range.ContainsFileAddress(address)) + return prev_sc; + + SymbolContext sc; + address.CalculateSymbolContext(&sc, eSymbolContextEverything); + + if (!prev_sc.module_sp && !sc.module_sp) + return sc; + if (prev_sc.module_sp == sc.module_sp && !sc.function && !sc.symbol && + !prev_sc.function && !prev_sc.symbol) + return sc; + + s.Printf(" "); + + if (!sc.module_sp) + s.Printf("(none)"); + else if (!sc.function && !sc.symbol) + s.Printf("%s`(none)", + sc.module_sp->GetFileSpec().GetFilename().AsCString()); + else + sc.DumpStopContext(&s, &target, address, /*show_fullpath*/ false, + /*show_module*/ true, /*show_inlined_frames*/ false, + /*show_function_arguments*/ true, + /*show_function_name*/ true, + /*show_inline_callsite_line_info*/ false); + s.Printf("\n"); + return sc; +} + +/// Dump an instruction given by its address using a given disassembler, unless +/// the instruction is not present in the disassembler. +/// +/// \param[in] disassembler +/// A disassembler containing a certain instruction list. +/// +/// \param[in] address +/// The address of the instruction to dump. +/// +/// \return +/// \b true if the information could be dumped, \b false otherwise. +static bool TryDumpInstructionInfo(Stream &s, + const DisassemblerSP &disassembler, + const ExecutionContext &exe_ctx, + const Address &address) { + if (!disassembler) + return false; + + if (InstructionSP instruction = + disassembler->GetInstructionList().GetInstructionAtAddress(address)) { + instruction->Dump(&s, /*show_address*/ false, /*show_bytes*/ false, + /*max_opcode_byte_size*/ 0, &exe_ctx, + /*sym_ctx*/ nullptr, /*prev_sym_ctx*/ nullptr, + /*disassembly_addr_format*/ nullptr, + /*max_address_text_size*/ 0); + return true; + } + + return false; +} + +/// Dump an instruction instruction given by its address. +/// +/// \param[in] prev_disassembler +/// The disassembler that was used to dump the previous instruction in the +/// trace. It is useful to avoid recomputations. +/// +/// \param[in] address +/// The address of the instruction to dump. +/// +/// \return +/// A disassembler that contains the given instruction, which might differ +/// from the previous disassembler. +static DisassemblerSP +DumpInstructionInfo(Stream &s, const SymbolContext &sc, + const DisassemblerSP &prev_disassembler, + ExecutionContext &exe_ctx, const Address &address) { + // We first try to use the previous disassembler + if (TryDumpInstructionInfo(s, prev_disassembler, exe_ctx, address)) + return prev_disassembler; + + // Now we try using the current function's disassembler + if (sc.function) { + DisassemblerSP disassembler = + sc.function->GetInstructions(exe_ctx, nullptr, true); + if (TryDumpInstructionInfo(s, disassembler, exe_ctx, address)) + return disassembler; + } + + // We fallback to disassembly one instruction + Target &target = exe_ctx.GetTargetRef(); + const ArchSpec &arch = target.GetArchitecture(); + AddressRange range(address, arch.GetMaximumOpcodeByteSize() * 1); + DisassemblerSP disassembler = Disassembler::DisassembleRange( + arch, /*plugin_name*/ nullptr, + /*flavor*/ nullptr, target, range, /*prefer_file_cache*/ true); + if (TryDumpInstructionInfo(s, disassembler, exe_ctx, address)) + return disassembler; + return nullptr; +} + +void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count, + size_t end_position, bool raw) { + size_t instructions_count = GetInstructionCount(thread); + s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = %zu\n", + thread.GetIndexID(), thread.GetID(), instructions_count); + + if (count == 0 || end_position >= instructions_count) + return; + + size_t start_position = + end_position + 1 < count ? 0 : end_position + 1 - count; + + int digits_count = GetNumberOfDigits(end_position); + auto printInstructionIndex = [&](size_t index) { + s.Printf(" [%*zu] ", digits_count, index); + }; + + bool was_prev_instruction_an_error = false; + Target &target = thread.GetProcess()->GetTarget(); + + SymbolContext sc; + DisassemblerSP disassembler; + ExecutionContext exe_ctx; + target.CalculateExecutionContext(exe_ctx); + + TraverseInstructions( + thread, start_position, TraceDirection::Forwards, + [&](size_t index, Expected<lldb::addr_t> load_address) -> bool { + if (load_address) { + // We print an empty line after a sequence of errors to show more + // clearly that there's a gap in the trace + if (was_prev_instruction_an_error) + s.Printf(" ...missing instructions\n"); + + Address address; + if (!raw) { + target.GetSectionLoadList().ResolveLoadAddress(*load_address, + address); + + sc = DumpSymbolContext(s, sc, target, address); + } + + printInstructionIndex(index); + s.Printf("0x%016" PRIx64 " ", *load_address); + + if (!raw) { + disassembler = + DumpInstructionInfo(s, sc, disassembler, exe_ctx, address); + } + + was_prev_instruction_an_error = false; + } else { + printInstructionIndex(index); + s << toString(load_address.takeError()); + was_prev_instruction_an_error = true; + if (!raw) + sc = SymbolContext(); + } + + s.Printf("\n"); + + return index < end_position; + }); +} |
