//===- lldb-test.cpp ------------------------------------------ *- C++ --*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "FormatUtil.h" #include "SystemInitializerTest.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/Section.h" #include "lldb/Initialization/SystemLifetimeManager.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangASTImporter.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/StreamString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include using namespace lldb; using namespace lldb_private; using namespace llvm; namespace opts { static cl::SubCommand BreakpointSubcommand("breakpoints", "Test breakpoint resolution"); cl::SubCommand ModuleSubcommand("module-sections", "Display LLDB Module Information"); cl::SubCommand SymbolsSubcommand("symbols", "Dump symbols for an object file"); namespace breakpoint { static cl::opt Target(cl::Positional, cl::desc(""), cl::Required, cl::sub(BreakpointSubcommand)); static cl::opt CommandFile(cl::Positional, cl::desc(""), cl::init("-"), cl::sub(BreakpointSubcommand)); static cl::opt Persistent( "persistent", cl::desc("Don't automatically remove all breakpoints before each command"), cl::sub(BreakpointSubcommand)); static llvm::StringRef plural(uintmax_t value) { return value == 1 ? "" : "s"; } static void dumpState(const BreakpointList &List, LinePrinter &P); static std::string substitute(StringRef Cmd); static void evaluateBreakpoints(Debugger &Dbg); } // namespace breakpoint namespace module { cl::opt SectionContents("contents", cl::desc("Dump each section's contents"), cl::sub(ModuleSubcommand)); cl::list InputFilenames(cl::Positional, cl::desc(""), cl::OneOrMore, cl::sub(ModuleSubcommand)); } // namespace module namespace symbols { cl::list InputFilenames(cl::Positional, cl::desc(""), cl::OneOrMore, cl::sub(SymbolsSubcommand)); } } // namespace opts static llvm::ManagedStatic DebuggerLifetime; void opts::breakpoint::dumpState(const BreakpointList &List, LinePrinter &P) { P.formatLine("{0} breakpoint{1}", List.GetSize(), plural(List.GetSize())); if (List.GetSize() > 0) P.formatLine("At least one breakpoint."); for (size_t i = 0, e = List.GetSize(); i < e; ++i) { BreakpointSP BP = List.GetBreakpointAtIndex(i); P.formatLine("Breakpoint ID {0}:", BP->GetID()); AutoIndent Indent(P, 2); P.formatLine("{0} location{1}.", BP->GetNumLocations(), plural(BP->GetNumLocations())); if (BP->GetNumLocations() > 0) P.formatLine("At least one location."); P.formatLine("{0} resolved location{1}.", BP->GetNumResolvedLocations(), plural(BP->GetNumResolvedLocations())); if (BP->GetNumResolvedLocations() > 0) P.formatLine("At least one resolved location."); for (size_t l = 0, le = BP->GetNumLocations(); l < le; ++l) { BreakpointLocationSP Loc = BP->GetLocationAtIndex(l); P.formatLine("Location ID {0}:", Loc->GetID()); AutoIndent Indent(P, 2); P.formatLine("Enabled: {0}", Loc->IsEnabled()); P.formatLine("Resolved: {0}", Loc->IsResolved()); P.formatLine("Address: {0}+{1:x}", Loc->GetAddress().GetSection()->GetName(), Loc->GetAddress().GetOffset()); } } P.NewLine(); } std::string opts::breakpoint::substitute(StringRef Cmd) { std::string Result; raw_string_ostream OS(Result); while (!Cmd.empty()) { switch (Cmd[0]) { case '%': if (Cmd.consume_front("%p") && (Cmd.empty() || !isalnum(Cmd[0]))) { OS << sys::path::parent_path(CommandFile); break; } // fall through default: size_t pos = Cmd.find('%'); OS << Cmd.substr(0, pos); Cmd = Cmd.substr(pos); break; } } return std::move(OS.str()); } void opts::breakpoint::evaluateBreakpoints(Debugger &Dbg) { TargetSP Target; Status ST = Dbg.GetTargetList().CreateTarget(Dbg, breakpoint::Target, /*triple*/ "", /*get_dependent_modules*/ false, /*platform_options*/ nullptr, Target); if (ST.Fail()) { errs() << formatv("Failed to create target '{0}: {1}\n", breakpoint::Target, ST); exit(1); } auto MB = MemoryBuffer::getFileOrSTDIN(CommandFile); if (!MB) { errs() << formatv("Could not open file '{0}: {1}\n", CommandFile, MB.getError().message()); exit(1); } LinePrinter P(4, outs()); StringRef Rest = (*MB)->getBuffer(); while (!Rest.empty()) { StringRef Line; std::tie(Line, Rest) = Rest.split('\n'); Line = Line.ltrim(); if (Line.empty() || Line[0] == '#') continue; if (!Persistent) Target->RemoveAllBreakpoints(/*internal_also*/ true); std::string Command = substitute(Line); P.formatLine("Command: {0}", Command); CommandReturnObject Result; if (!Dbg.GetCommandInterpreter().HandleCommand( Command.c_str(), /*add_to_history*/ eLazyBoolNo, Result)) { P.formatLine("Failed: {0}", Result.GetErrorData()); continue; } dumpState(Target->GetBreakpointList(/*internal*/ false), P); } } static void dumpSymbols(Debugger &Dbg) { for (const auto &File : opts::symbols::InputFilenames) { ModuleSpec Spec{FileSpec(File, false)}; Spec.GetSymbolFileSpec().SetFile(File, false); auto ModulePtr = std::make_shared(Spec); StreamString Stream; ModulePtr->ParseAllDebugSymbols(); ModulePtr->Dump(&Stream); llvm::outs() << Stream.GetData() << "\n"; llvm::outs().flush(); } } static void dumpModules(Debugger &Dbg) { LinePrinter Printer(4, llvm::outs()); for (const auto &File : opts::module::InputFilenames) { ModuleSpec Spec{FileSpec(File, false)}; Spec.GetSymbolFileSpec().SetFile(File, false); auto ModulePtr = std::make_shared(Spec); SectionList *Sections = ModulePtr->GetSectionList(); if (!Sections) { llvm::errs() << "Could not load sections for module " << File << "\n"; continue; } size_t Count = Sections->GetNumSections(0); Printer.formatLine("Showing {0} sections", Count); for (size_t I = 0; I < Count; ++I) { AutoIndent Indent(Printer, 2); auto S = Sections->GetSectionAtIndex(I); assert(S); Printer.formatLine("Index: {0}", I); Printer.formatLine("Name: {0}", S->GetName().GetStringRef()); Printer.formatLine("VM size: {0}", S->GetByteSize()); Printer.formatLine("File size: {0}", S->GetFileSize()); if (opts::module::SectionContents) { DataExtractor Data; S->GetSectionData(Data); ArrayRef Bytes = {Data.GetDataStart(), Data.GetDataEnd()}; Printer.formatBinary("Data: ", Bytes, 0); } Printer.NewLine(); } } } int main(int argc, const char *argv[]) { StringRef ToolName = argv[0]; sys::PrintStackTraceOnErrorSignal(ToolName); PrettyStackTraceProgram X(argc, argv); llvm_shutdown_obj Y; cl::ParseCommandLineOptions(argc, argv, "LLDB Testing Utility\n"); DebuggerLifetime->Initialize(llvm::make_unique(), nullptr); auto Dbg = lldb_private::Debugger::CreateInstance(); if (opts::BreakpointSubcommand) opts::breakpoint::evaluateBreakpoints(*Dbg); if (opts::ModuleSubcommand) dumpModules(*Dbg); else if (opts::SymbolsSubcommand) dumpSymbols(*Dbg); DebuggerLifetime->Terminate(); return 0; }