1 | //===- lldb-test.cpp ------------------------------------------ *- C++ --*-===// |
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 | |
9 | #include "FormatUtil.h" |
10 | #include "SystemInitializerTest.h" |
11 | |
12 | #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" |
13 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
14 | #include "lldb/Breakpoint/BreakpointLocation.h" |
15 | #include "lldb/Core/Debugger.h" |
16 | #include "lldb/Core/Mangled.h" |
17 | #include "lldb/Core/Module.h" |
18 | #include "lldb/Core/Section.h" |
19 | #include "lldb/Expression/IRMemoryMap.h" |
20 | #include "lldb/Initialization/SystemLifetimeManager.h" |
21 | #include "lldb/Interpreter/CommandInterpreter.h" |
22 | #include "lldb/Interpreter/CommandReturnObject.h" |
23 | #include "lldb/Symbol/CompileUnit.h" |
24 | #include "lldb/Symbol/LineTable.h" |
25 | #include "lldb/Symbol/SymbolFile.h" |
26 | #include "lldb/Symbol/Symtab.h" |
27 | #include "lldb/Symbol/Type.h" |
28 | #include "lldb/Symbol/TypeList.h" |
29 | #include "lldb/Symbol/TypeMap.h" |
30 | #include "lldb/Symbol/VariableList.h" |
31 | #include "lldb/Target/Language.h" |
32 | #include "lldb/Target/Process.h" |
33 | #include "lldb/Target/Target.h" |
34 | #include "lldb/Utility/DataExtractor.h" |
35 | #include "lldb/Utility/LLDBAssert.h" |
36 | #include "lldb/Utility/State.h" |
37 | #include "lldb/Utility/StreamString.h" |
38 | |
39 | #include "llvm/ADT/IntervalMap.h" |
40 | #include "llvm/ADT/ScopeExit.h" |
41 | #include "llvm/ADT/StringRef.h" |
42 | #include "llvm/Support/CommandLine.h" |
43 | #include "llvm/Support/ManagedStatic.h" |
44 | #include "llvm/Support/MathExtras.h" |
45 | #include "llvm/Support/Path.h" |
46 | #include "llvm/Support/PrettyStackTrace.h" |
47 | #include "llvm/Support/Signals.h" |
48 | #include "llvm/Support/WithColor.h" |
49 | |
50 | #include <cstdio> |
51 | #include <optional> |
52 | #include <thread> |
53 | |
54 | using namespace lldb; |
55 | using namespace lldb_private; |
56 | using namespace llvm; |
57 | |
58 | namespace opts { |
59 | static cl::SubCommand BreakpointSubcommand("breakpoints" , |
60 | "Test breakpoint resolution" ); |
61 | cl::SubCommand ObjectFileSubcommand("object-file" , |
62 | "Display LLDB object file information" ); |
63 | cl::SubCommand SymbolsSubcommand("symbols" , "Dump symbols for an object file" ); |
64 | cl::SubCommand SymTabSubcommand("symtab" , |
65 | "Test symbol table functionality" ); |
66 | cl::SubCommand IRMemoryMapSubcommand("ir-memory-map" , "Test IRMemoryMap" ); |
67 | cl::SubCommand AssertSubcommand("assert" , "Test assert handling" ); |
68 | |
69 | cl::opt<std::string> Log("log" , cl::desc("Path to a log file" ), cl::init(Val: "" ), |
70 | cl::sub(BreakpointSubcommand), |
71 | cl::sub(ObjectFileSubcommand), |
72 | cl::sub(SymbolsSubcommand), |
73 | cl::sub(SymTabSubcommand), |
74 | cl::sub(IRMemoryMapSubcommand)); |
75 | |
76 | /// Create a target using the file pointed to by \p Filename, or abort. |
77 | TargetSP createTarget(Debugger &Dbg, const std::string &Filename); |
78 | |
79 | /// Read \p Filename into a null-terminated buffer, or abort. |
80 | std::unique_ptr<MemoryBuffer> openFile(const std::string &Filename); |
81 | |
82 | namespace breakpoint { |
83 | static cl::opt<std::string> Target(cl::Positional, cl::desc("<target>" ), |
84 | cl::Required, cl::sub(BreakpointSubcommand)); |
85 | static cl::opt<std::string> CommandFile(cl::Positional, |
86 | cl::desc("<command-file>" ), |
87 | cl::init(Val: "-" ), |
88 | cl::sub(BreakpointSubcommand)); |
89 | static cl::opt<bool> Persistent( |
90 | "persistent" , |
91 | cl::desc("Don't automatically remove all breakpoints before each command" ), |
92 | cl::sub(BreakpointSubcommand)); |
93 | |
94 | static llvm::StringRef plural(uintmax_t value) { return value == 1 ? "" : "s" ; } |
95 | static void dumpState(const BreakpointList &List, LinePrinter &P); |
96 | static std::string substitute(StringRef Cmd); |
97 | static int evaluateBreakpoints(Debugger &Dbg); |
98 | } // namespace breakpoint |
99 | |
100 | namespace object { |
101 | cl::opt<bool> SectionContents("contents" , |
102 | cl::desc("Dump each section's contents" ), |
103 | cl::sub(ObjectFileSubcommand)); |
104 | cl::opt<bool> SectionDependentModules("dep-modules" , |
105 | cl::desc("Dump each dependent module" ), |
106 | cl::sub(ObjectFileSubcommand)); |
107 | cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>" ), |
108 | cl::OneOrMore, |
109 | cl::sub(ObjectFileSubcommand)); |
110 | } // namespace object |
111 | |
112 | namespace symtab { |
113 | |
114 | /// The same enum as Mangled::NamePreference but with a default |
115 | /// 'None' case. This is needed to disambiguate wheter "ManglingPreference" was |
116 | /// explicitly set or not. |
117 | enum class ManglingPreference { |
118 | None, |
119 | Mangled, |
120 | Demangled, |
121 | MangledWithoutArguments, |
122 | }; |
123 | |
124 | static cl::opt<std::string> FindSymbolsByRegex( |
125 | "find-symbols-by-regex" , |
126 | cl::desc( |
127 | "Dump symbols found in the symbol table matching the specified regex." ), |
128 | cl::sub(SymTabSubcommand)); |
129 | |
130 | static cl::opt<ManglingPreference> ManglingPreference( |
131 | "mangling-preference" , |
132 | cl::desc("Preference on mangling scheme the regex should match against and " |
133 | "dumped." ), |
134 | cl::values( |
135 | clEnumValN(ManglingPreference::Mangled, "mangled" , "Prefer mangled" ), |
136 | clEnumValN(ManglingPreference::Demangled, "demangled" , |
137 | "Prefer demangled" ), |
138 | clEnumValN(ManglingPreference::MangledWithoutArguments, |
139 | "demangled-without-args" , "Prefer mangled without args" )), |
140 | cl::sub(SymTabSubcommand)); |
141 | |
142 | static cl::opt<std::string> InputFile(cl::Positional, cl::desc("<input file>" ), |
143 | cl::Required, cl::sub(SymTabSubcommand)); |
144 | |
145 | /// Validate that the options passed make sense. |
146 | static std::optional<llvm::Error> validate(); |
147 | |
148 | /// Transforms the selected mangling preference into a Mangled::NamePreference |
149 | static Mangled::NamePreference getNamePreference(); |
150 | |
151 | static int handleSymtabCommand(Debugger &Dbg); |
152 | } // namespace symtab |
153 | |
154 | namespace symbols { |
155 | static cl::opt<std::string> InputFile(cl::Positional, cl::desc("<input file>" ), |
156 | cl::Required, cl::sub(SymbolsSubcommand)); |
157 | |
158 | static cl::opt<std::string> |
159 | SymbolPath("symbol-file" , |
160 | cl::desc("The file from which to fetch symbol information." ), |
161 | cl::value_desc("file" ), cl::sub(SymbolsSubcommand)); |
162 | |
163 | enum class FindType { |
164 | None, |
165 | Function, |
166 | Block, |
167 | Namespace, |
168 | Type, |
169 | Variable, |
170 | }; |
171 | static cl::opt<FindType> Find( |
172 | "find" , cl::desc("Choose search type:" ), |
173 | cl::values( |
174 | clEnumValN(FindType::None, "none" , "No search, just dump the module." ), |
175 | clEnumValN(FindType::Function, "function" , "Find functions." ), |
176 | clEnumValN(FindType::Block, "block" , "Find blocks." ), |
177 | clEnumValN(FindType::Namespace, "namespace" , "Find namespaces." ), |
178 | clEnumValN(FindType::Type, "type" , "Find types." ), |
179 | clEnumValN(FindType::Variable, "variable" , "Find global variables." )), |
180 | cl::sub(SymbolsSubcommand)); |
181 | |
182 | static cl::opt<std::string> Name("name" , cl::desc("Name to find." ), |
183 | cl::sub(SymbolsSubcommand)); |
184 | static cl::opt<std::string> MangledName( |
185 | "mangled-name" , |
186 | cl::desc("Mangled name to find. Only compatible when searching types" ), |
187 | cl::sub(SymbolsSubcommand)); |
188 | static cl::opt<bool> |
189 | Regex("regex" , |
190 | cl::desc("Search using regular expressions (available for variables " |
191 | "and functions only)." ), |
192 | cl::sub(SymbolsSubcommand)); |
193 | static cl::opt<std::string> |
194 | Context("context" , |
195 | cl::desc("Restrict search to the context of the given variable." ), |
196 | cl::value_desc("variable" ), cl::sub(SymbolsSubcommand)); |
197 | |
198 | static cl::opt<std::string> CompilerContext( |
199 | "compiler-context" , |
200 | cl::desc("Specify a compiler context as \"kind:name,...\"." ), |
201 | cl::value_desc("context" ), cl::sub(SymbolsSubcommand)); |
202 | |
203 | static cl::opt<bool> FindInAnyModule( |
204 | "find-in-any-module" , |
205 | cl::desc("If true, the type will be searched for in all modules. Otherwise " |
206 | "the modules must be provided in -compiler-context" ), |
207 | cl::sub(SymbolsSubcommand)); |
208 | |
209 | static cl::opt<std::string> |
210 | Language("language" , cl::desc("Specify a language type, like C99." ), |
211 | cl::value_desc("language" ), cl::sub(SymbolsSubcommand)); |
212 | |
213 | static cl::list<FunctionNameType> FunctionNameFlags( |
214 | "function-flags" , cl::desc("Function search flags:" ), |
215 | cl::values(clEnumValN(eFunctionNameTypeAuto, "auto" , |
216 | "Automatically deduce flags based on name." ), |
217 | clEnumValN(eFunctionNameTypeFull, "full" , "Full function name." ), |
218 | clEnumValN(eFunctionNameTypeBase, "base" , "Base name." ), |
219 | clEnumValN(eFunctionNameTypeMethod, "method" , "Method name." ), |
220 | clEnumValN(eFunctionNameTypeSelector, "selector" , |
221 | "Selector name." )), |
222 | cl::sub(SymbolsSubcommand)); |
223 | static FunctionNameType getFunctionNameFlags() { |
224 | FunctionNameType Result = FunctionNameType(0); |
225 | for (FunctionNameType Flag : FunctionNameFlags) |
226 | Result = FunctionNameType(Result | Flag); |
227 | return Result; |
228 | } |
229 | |
230 | static cl::opt<bool> DumpAST("dump-ast" , |
231 | cl::desc("Dump AST restored from symbols." ), |
232 | cl::sub(SymbolsSubcommand)); |
233 | static cl::opt<bool> DumpClangAST( |
234 | "dump-clang-ast" , |
235 | cl::desc("Dump clang AST restored from symbols. When used on its own this " |
236 | "will dump the entire AST of all loaded symbols. When combined " |
237 | "with -find, it changes the presentation of the search results " |
238 | "from pretty-printing the types to an AST dump." ), |
239 | cl::sub(SymbolsSubcommand)); |
240 | |
241 | static cl::opt<bool> Verify("verify" , cl::desc("Verify symbol information." ), |
242 | cl::sub(SymbolsSubcommand)); |
243 | |
244 | static cl::opt<std::string> File("file" , |
245 | cl::desc("File (compile unit) to search." ), |
246 | cl::sub(SymbolsSubcommand)); |
247 | static cl::opt<int> Line("line" , cl::desc("Line to search." ), |
248 | cl::sub(SymbolsSubcommand)); |
249 | |
250 | static Expected<CompilerDeclContext> getDeclContext(SymbolFile &Symfile); |
251 | |
252 | static Error findFunctions(lldb_private::Module &Module); |
253 | static Error findBlocks(lldb_private::Module &Module); |
254 | static Error findNamespaces(lldb_private::Module &Module); |
255 | static Error findTypes(lldb_private::Module &Module); |
256 | static Error findVariables(lldb_private::Module &Module); |
257 | static Error dumpModule(lldb_private::Module &Module); |
258 | static Error dumpAST(lldb_private::Module &Module); |
259 | static Error dumpEntireClangAST(lldb_private::Module &Module); |
260 | static Error verify(lldb_private::Module &Module); |
261 | |
262 | static Expected<Error (*)(lldb_private::Module &)> getAction(); |
263 | static int dumpSymbols(Debugger &Dbg); |
264 | } // namespace symbols |
265 | |
266 | namespace irmemorymap { |
267 | static cl::opt<std::string> Target(cl::Positional, cl::desc("<target>" ), |
268 | cl::Required, |
269 | cl::sub(IRMemoryMapSubcommand)); |
270 | static cl::opt<std::string> CommandFile(cl::Positional, |
271 | cl::desc("<command-file>" ), |
272 | cl::init(Val: "-" ), |
273 | cl::sub(IRMemoryMapSubcommand)); |
274 | static cl::opt<bool> UseHostOnlyAllocationPolicy( |
275 | "host-only" , cl::desc("Use the host-only allocation policy" ), |
276 | cl::init(Val: false), cl::sub(IRMemoryMapSubcommand)); |
277 | |
278 | using AllocationT = std::pair<addr_t, addr_t>; |
279 | using AddrIntervalMap = |
280 | IntervalMap<addr_t, unsigned, 8, IntervalMapHalfOpenInfo<addr_t>>; |
281 | |
282 | struct IRMemoryMapTestState { |
283 | TargetSP Target; |
284 | IRMemoryMap Map; |
285 | |
286 | AddrIntervalMap::Allocator IntervalMapAllocator; |
287 | AddrIntervalMap Allocations; |
288 | |
289 | StringMap<addr_t> Label2AddrMap; |
290 | |
291 | IRMemoryMapTestState(TargetSP Target) |
292 | : Target(Target), Map(Target), Allocations(IntervalMapAllocator) {} |
293 | }; |
294 | |
295 | bool evalMalloc(StringRef Line, IRMemoryMapTestState &State); |
296 | bool evalFree(StringRef Line, IRMemoryMapTestState &State); |
297 | int evaluateMemoryMapCommands(Debugger &Dbg); |
298 | } // namespace irmemorymap |
299 | |
300 | namespace assert { |
301 | int lldb_assert(Debugger &Dbg); |
302 | } // namespace assert |
303 | } // namespace opts |
304 | |
305 | llvm::SmallVector<CompilerContext, 4> parseCompilerContext() { |
306 | llvm::SmallVector<CompilerContext, 4> result; |
307 | if (opts::symbols::CompilerContext.empty()) |
308 | return result; |
309 | |
310 | StringRef str{opts::symbols::CompilerContext}; |
311 | SmallVector<StringRef, 8> entries_str; |
312 | str.split(A&: entries_str, Separator: ',', /*maxSplit*/MaxSplit: -1, /*keepEmpty=*/KeepEmpty: false); |
313 | for (auto entry_str : entries_str) { |
314 | StringRef key, value; |
315 | std::tie(args&: key, args&: value) = entry_str.split(Separator: ':'); |
316 | auto kind = |
317 | StringSwitch<CompilerContextKind>(key) |
318 | .Case(S: "TranslationUnit" , Value: CompilerContextKind::TranslationUnit) |
319 | .Case(S: "Module" , Value: CompilerContextKind::Module) |
320 | .Case(S: "Namespace" , Value: CompilerContextKind::Namespace) |
321 | .Case(S: "ClassOrStruct" , Value: CompilerContextKind::ClassOrStruct) |
322 | .Case(S: "Union" , Value: CompilerContextKind::Union) |
323 | .Case(S: "Function" , Value: CompilerContextKind::Function) |
324 | .Case(S: "Variable" , Value: CompilerContextKind::Variable) |
325 | .Case(S: "Enum" , Value: CompilerContextKind::Enum) |
326 | .Case(S: "Typedef" , Value: CompilerContextKind::Typedef) |
327 | .Case(S: "AnyType" , Value: CompilerContextKind::AnyType) |
328 | .Default(Value: CompilerContextKind::Invalid); |
329 | if (value.empty()) { |
330 | WithColor::error() << "compiler context entry has no \"name\"\n" ; |
331 | exit(status: 1); |
332 | } |
333 | result.push_back(Elt: {kind, ConstString{value}}); |
334 | } |
335 | outs() << "Search context: {" ; |
336 | lldb_private::StreamString s; |
337 | llvm::interleaveComma(c: result, os&: s, each_fn: [&](auto &ctx) { ctx.Dump(s); }); |
338 | outs() << s.GetString().str() << "}\n" ; |
339 | |
340 | return result; |
341 | } |
342 | |
343 | template <typename... Args> |
344 | static Error make_string_error(const char *Format, Args &&... args) { |
345 | return llvm::make_error<llvm::StringError>( |
346 | llvm::formatv(Format, std::forward<Args>(args)...).str(), |
347 | llvm::inconvertibleErrorCode()); |
348 | } |
349 | |
350 | TargetSP opts::createTarget(Debugger &Dbg, const std::string &Filename) { |
351 | TargetSP Target; |
352 | Status ST = Dbg.GetTargetList().CreateTarget( |
353 | debugger&: Dbg, user_exe_path: Filename, /*triple*/ triple_str: "" , get_dependent_modules: eLoadDependentsNo, |
354 | /*platform_options*/ nullptr, target_sp&: Target); |
355 | if (ST.Fail()) { |
356 | errs() << formatv(Fmt: "Failed to create target '{0}: {1}\n" , Vals: Filename, Vals&: ST); |
357 | exit(status: 1); |
358 | } |
359 | return Target; |
360 | } |
361 | |
362 | std::unique_ptr<MemoryBuffer> opts::openFile(const std::string &Filename) { |
363 | auto MB = MemoryBuffer::getFileOrSTDIN(Filename); |
364 | if (!MB) { |
365 | errs() << formatv(Fmt: "Could not open file '{0}: {1}\n" , Vals: Filename, |
366 | Vals: MB.getError().message()); |
367 | exit(status: 1); |
368 | } |
369 | return std::move(*MB); |
370 | } |
371 | |
372 | void opts::breakpoint::dumpState(const BreakpointList &List, LinePrinter &P) { |
373 | P.formatLine(Fmt: "{0} breakpoint{1}" , Items: List.GetSize(), Items: plural(value: List.GetSize())); |
374 | if (List.GetSize() > 0) |
375 | P.formatLine(Fmt: "At least one breakpoint." ); |
376 | for (size_t i = 0, e = List.GetSize(); i < e; ++i) { |
377 | BreakpointSP BP = List.GetBreakpointAtIndex(i); |
378 | P.formatLine(Fmt: "Breakpoint ID {0}:" , Items: BP->GetID()); |
379 | AutoIndent Indent(P, 2); |
380 | P.formatLine(Fmt: "{0} location{1}." , Items: BP->GetNumLocations(), |
381 | Items: plural(value: BP->GetNumLocations())); |
382 | if (BP->GetNumLocations() > 0) |
383 | P.formatLine(Fmt: "At least one location." ); |
384 | P.formatLine(Fmt: "{0} resolved location{1}." , Items: BP->GetNumResolvedLocations(), |
385 | Items: plural(value: BP->GetNumResolvedLocations())); |
386 | if (BP->GetNumResolvedLocations() > 0) |
387 | P.formatLine(Fmt: "At least one resolved location." ); |
388 | for (size_t l = 0, le = BP->GetNumLocations(); l < le; ++l) { |
389 | BreakpointLocationSP Loc = BP->GetLocationAtIndex(index: l); |
390 | P.formatLine(Fmt: "Location ID {0}:" , Items: Loc->GetID()); |
391 | AutoIndent Indent(P, 2); |
392 | P.formatLine(Fmt: "Enabled: {0}" , Items: Loc->IsEnabled()); |
393 | P.formatLine(Fmt: "Resolved: {0}" , Items: Loc->IsResolved()); |
394 | SymbolContext sc; |
395 | Loc->GetAddress().CalculateSymbolContext(sc: &sc); |
396 | lldb_private::StreamString S; |
397 | sc.DumpStopContext(s: &S, exe_scope: BP->GetTarget().GetProcessSP().get(), |
398 | so_addr: Loc->GetAddress(), show_fullpaths: false, show_module: true, show_inlined_frames: false, show_function_arguments: true, show_function_name: true); |
399 | P.formatLine(Fmt: "Address: {0}" , Items: S.GetString()); |
400 | } |
401 | } |
402 | P.NewLine(); |
403 | } |
404 | |
405 | std::string opts::breakpoint::substitute(StringRef Cmd) { |
406 | std::string Result; |
407 | raw_string_ostream OS(Result); |
408 | while (!Cmd.empty()) { |
409 | switch (Cmd[0]) { |
410 | case '%': |
411 | if (Cmd.consume_front(Prefix: "%p" ) && (Cmd.empty() || !isalnum(Cmd[0]))) { |
412 | OS << sys::path::parent_path(path: breakpoint::CommandFile); |
413 | break; |
414 | } |
415 | [[fallthrough]]; |
416 | default: |
417 | size_t pos = Cmd.find(C: '%'); |
418 | OS << Cmd.substr(Start: 0, N: pos); |
419 | Cmd = Cmd.substr(Start: pos); |
420 | break; |
421 | } |
422 | } |
423 | return Result; |
424 | } |
425 | |
426 | int opts::breakpoint::evaluateBreakpoints(Debugger &Dbg) { |
427 | TargetSP Target = opts::createTarget(Dbg, Filename: breakpoint::Target); |
428 | std::unique_ptr<MemoryBuffer> MB = opts::openFile(Filename: breakpoint::CommandFile); |
429 | |
430 | LinePrinter P(4, outs()); |
431 | StringRef Rest = MB->getBuffer(); |
432 | int HadErrors = 0; |
433 | while (!Rest.empty()) { |
434 | StringRef Line; |
435 | std::tie(args&: Line, args&: Rest) = Rest.split(Separator: '\n'); |
436 | Line = Line.ltrim().rtrim(); |
437 | if (Line.empty() || Line[0] == '#') |
438 | continue; |
439 | |
440 | if (!Persistent) |
441 | Target->RemoveAllBreakpoints(/*internal_also*/ true); |
442 | |
443 | std::string Command = substitute(Cmd: Line); |
444 | P.formatLine(Fmt: "Command: {0}" , Items&: Command); |
445 | CommandReturnObject Result(/*colors*/ false); |
446 | if (!Dbg.GetCommandInterpreter().HandleCommand( |
447 | command_line: Command.c_str(), /*add_to_history*/ eLazyBoolNo, result&: Result)) { |
448 | P.formatLine(Fmt: "Failed: {0}" , Items: Result.GetErrorString()); |
449 | HadErrors = 1; |
450 | continue; |
451 | } |
452 | |
453 | dumpState(List: Target->GetBreakpointList(/*internal*/ false), P); |
454 | } |
455 | return HadErrors; |
456 | } |
457 | |
458 | Expected<CompilerDeclContext> |
459 | opts::symbols::getDeclContext(SymbolFile &Symfile) { |
460 | if (Context.empty()) |
461 | return CompilerDeclContext(); |
462 | VariableList List; |
463 | Symfile.FindGlobalVariables(name: ConstString(Context), parent_decl_ctx: CompilerDeclContext(), |
464 | UINT32_MAX, variables&: List); |
465 | if (List.Empty()) |
466 | return make_string_error(Format: "Context search didn't find a match." ); |
467 | if (List.GetSize() > 1) |
468 | return make_string_error(Format: "Context search found multiple matches." ); |
469 | return List.GetVariableAtIndex(idx: 0)->GetDeclContext(); |
470 | } |
471 | |
472 | static lldb::DescriptionLevel GetDescriptionLevel() { |
473 | return opts::symbols::DumpClangAST ? eDescriptionLevelVerbose : eDescriptionLevelFull; |
474 | } |
475 | |
476 | Error opts::symbols::findFunctions(lldb_private::Module &Module) { |
477 | if (!MangledName.empty()) |
478 | return make_string_error(Format: "Cannot search functions by mangled name." ); |
479 | |
480 | SymbolFile &Symfile = *Module.GetSymbolFile(); |
481 | SymbolContextList List; |
482 | auto compiler_context = parseCompilerContext(); |
483 | if (!File.empty()) { |
484 | assert(Line != 0); |
485 | |
486 | FileSpec src_file(File); |
487 | size_t cu_count = Module.GetNumCompileUnits(); |
488 | for (size_t i = 0; i < cu_count; i++) { |
489 | lldb::CompUnitSP cu_sp = Module.GetCompileUnitAtIndex(idx: i); |
490 | if (!cu_sp) |
491 | continue; |
492 | |
493 | LineEntry le; |
494 | cu_sp->FindLineEntry(start_idx: 0, line: Line, file_spec_ptr: &src_file, exact: false, line_entry: &le); |
495 | if (!le.IsValid()) |
496 | continue; |
497 | const bool include_inlined_functions = false; |
498 | auto addr = |
499 | le.GetSameLineContiguousAddressRange(include_inlined_functions) |
500 | .GetBaseAddress(); |
501 | if (!addr.IsValid()) |
502 | continue; |
503 | |
504 | SymbolContext sc; |
505 | uint32_t resolved = |
506 | addr.CalculateSymbolContext(sc: &sc, resolve_scope: eSymbolContextFunction); |
507 | if (resolved & eSymbolContextFunction) |
508 | List.Append(sc); |
509 | } |
510 | } else if (Regex) { |
511 | RegularExpression RE(Name); |
512 | assert(RE.IsValid()); |
513 | List.Clear(); |
514 | Symfile.FindFunctions(regex: RE, include_inlines: true, sc_list&: List); |
515 | } else if (!compiler_context.empty()) { |
516 | List.Clear(); |
517 | Module.FindFunctions(compiler_ctx: compiler_context, name_type_mask: getFunctionNameFlags(), options: {}, sc_list&: List); |
518 | } else { |
519 | Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); |
520 | if (!ContextOr) |
521 | return ContextOr.takeError(); |
522 | const CompilerDeclContext &ContextPtr = |
523 | ContextOr->IsValid() ? *ContextOr : CompilerDeclContext(); |
524 | |
525 | List.Clear(); |
526 | lldb_private::Module::LookupInfo lookup_info( |
527 | ConstString(Name), getFunctionNameFlags(), eLanguageTypeUnknown); |
528 | Symfile.FindFunctions(lookup_info, parent_decl_ctx: ContextPtr, include_inlines: true, sc_list&: List); |
529 | } |
530 | outs() << formatv(Fmt: "Found {0} functions:\n" , Vals: List.GetSize()); |
531 | StreamString Stream; |
532 | List.Dump(s: &Stream, target: nullptr); |
533 | outs() << Stream.GetData() << "\n" ; |
534 | return Error::success(); |
535 | } |
536 | |
537 | Error opts::symbols::findBlocks(lldb_private::Module &Module) { |
538 | assert(!Regex); |
539 | assert(!File.empty()); |
540 | assert(Line != 0); |
541 | if (!MangledName.empty()) |
542 | return make_string_error(Format: "Cannot search blocks by mangled name." ); |
543 | |
544 | SymbolContextList List; |
545 | |
546 | FileSpec src_file(File); |
547 | size_t cu_count = Module.GetNumCompileUnits(); |
548 | for (size_t i = 0; i < cu_count; i++) { |
549 | lldb::CompUnitSP cu_sp = Module.GetCompileUnitAtIndex(idx: i); |
550 | if (!cu_sp) |
551 | continue; |
552 | |
553 | LineEntry le; |
554 | cu_sp->FindLineEntry(start_idx: 0, line: Line, file_spec_ptr: &src_file, exact: false, line_entry: &le); |
555 | if (!le.IsValid()) |
556 | continue; |
557 | const bool include_inlined_functions = false; |
558 | auto addr = le.GetSameLineContiguousAddressRange(include_inlined_functions) |
559 | .GetBaseAddress(); |
560 | if (!addr.IsValid()) |
561 | continue; |
562 | |
563 | SymbolContext sc; |
564 | uint32_t resolved = addr.CalculateSymbolContext(sc: &sc, resolve_scope: eSymbolContextBlock); |
565 | if (resolved & eSymbolContextBlock) |
566 | List.Append(sc); |
567 | } |
568 | |
569 | outs() << formatv(Fmt: "Found {0} blocks:\n" , Vals: List.GetSize()); |
570 | StreamString Stream; |
571 | List.Dump(s: &Stream, target: nullptr); |
572 | outs() << Stream.GetData() << "\n" ; |
573 | return Error::success(); |
574 | } |
575 | |
576 | Error opts::symbols::findNamespaces(lldb_private::Module &Module) { |
577 | if (!MangledName.empty()) |
578 | return make_string_error(Format: "Cannot search namespaces by mangled name." ); |
579 | |
580 | SymbolFile &Symfile = *Module.GetSymbolFile(); |
581 | Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); |
582 | if (!ContextOr) |
583 | return ContextOr.takeError(); |
584 | const CompilerDeclContext &ContextPtr = |
585 | ContextOr->IsValid() ? *ContextOr : CompilerDeclContext(); |
586 | |
587 | CompilerDeclContext Result = |
588 | Symfile.FindNamespace(name: ConstString(Name), parent_decl_ctx: ContextPtr); |
589 | if (Result) |
590 | outs() << "Found namespace: " |
591 | << Result.GetScopeQualifiedName().GetStringRef() << "\n" ; |
592 | else |
593 | outs() << "Namespace not found.\n" ; |
594 | return Error::success(); |
595 | } |
596 | |
597 | Error opts::symbols::findTypes(lldb_private::Module &Module) { |
598 | SymbolFile &Symfile = *Module.GetSymbolFile(); |
599 | Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); |
600 | if (!ContextOr) |
601 | return ContextOr.takeError(); |
602 | ; |
603 | |
604 | TypeQueryOptions Opts = TypeQueryOptions::e_module_search; |
605 | if (FindInAnyModule) |
606 | Opts |= TypeQueryOptions::e_ignore_modules; |
607 | TypeResults results; |
608 | if (!Name.empty() && !MangledName.empty()) |
609 | return make_string_error(Format: "Cannot search by both name and mangled name." ); |
610 | |
611 | if (!Name.empty()) { |
612 | if (ContextOr->IsValid()) { |
613 | TypeQuery query(*ContextOr, ConstString(Name), Opts); |
614 | if (!Language.empty()) |
615 | query.AddLanguage(language: Language::GetLanguageTypeFromString(string: Language)); |
616 | Symfile.FindTypes(query, results); |
617 | } else { |
618 | TypeQuery query(Name); |
619 | if (!Language.empty()) |
620 | query.AddLanguage(language: Language::GetLanguageTypeFromString(string: Language)); |
621 | Symfile.FindTypes(query, results); |
622 | } |
623 | } else if (!MangledName.empty()) { |
624 | Opts = TypeQueryOptions::e_search_by_mangled_name; |
625 | if (ContextOr->IsValid()) { |
626 | TypeQuery query(*ContextOr, ConstString(MangledName), Opts); |
627 | if (!Language.empty()) |
628 | query.AddLanguage(language: Language::GetLanguageTypeFromString(string: Language)); |
629 | Symfile.FindTypes(query, results); |
630 | } else { |
631 | TypeQuery query(MangledName, Opts); |
632 | if (!Language.empty()) |
633 | query.AddLanguage(language: Language::GetLanguageTypeFromString(string: Language)); |
634 | Symfile.FindTypes(query, results); |
635 | } |
636 | |
637 | } else { |
638 | TypeQuery query(parseCompilerContext(), Opts); |
639 | if (!Language.empty()) |
640 | query.AddLanguage(language: Language::GetLanguageTypeFromString(string: Language)); |
641 | Symfile.FindTypes(query, results); |
642 | } |
643 | outs() << formatv(Fmt: "Found {0} types:\n" , Vals: results.GetTypeMap().GetSize()); |
644 | StreamString Stream; |
645 | // Resolve types to force-materialize typedef types. |
646 | for (const auto &type_sp : results.GetTypeMap().Types()) |
647 | type_sp->GetFullCompilerType(); |
648 | results.GetTypeMap().Dump(s: &Stream, show_context: false, level: GetDescriptionLevel()); |
649 | outs() << Stream.GetData() << "\n" ; |
650 | return Error::success(); |
651 | } |
652 | |
653 | Error opts::symbols::findVariables(lldb_private::Module &Module) { |
654 | if (!MangledName.empty()) |
655 | return make_string_error(Format: "Cannot search variables by mangled name." ); |
656 | |
657 | SymbolFile &Symfile = *Module.GetSymbolFile(); |
658 | VariableList List; |
659 | if (Regex) { |
660 | RegularExpression RE(Name); |
661 | assert(RE.IsValid()); |
662 | Symfile.FindGlobalVariables(regex: RE, UINT32_MAX, variables&: List); |
663 | } else if (!File.empty()) { |
664 | CompUnitSP CU; |
665 | for (size_t Ind = 0; !CU && Ind < Module.GetNumCompileUnits(); ++Ind) { |
666 | CompUnitSP Candidate = Module.GetCompileUnitAtIndex(idx: Ind); |
667 | if (!Candidate || |
668 | Candidate->GetPrimaryFile().GetFilename().GetStringRef() != File) |
669 | continue; |
670 | if (CU) |
671 | return make_string_error(Format: "Multiple compile units for file `{0}` found." , |
672 | args&: File); |
673 | CU = std::move(Candidate); |
674 | } |
675 | |
676 | if (!CU) |
677 | return make_string_error(Format: "Compile unit `{0}` not found." , args&: File); |
678 | |
679 | List.AddVariables(variable_list: CU->GetVariableList(can_create: true).get()); |
680 | } else { |
681 | Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); |
682 | if (!ContextOr) |
683 | return ContextOr.takeError(); |
684 | const CompilerDeclContext &ContextPtr = |
685 | ContextOr->IsValid() ? *ContextOr : CompilerDeclContext(); |
686 | |
687 | Symfile.FindGlobalVariables(name: ConstString(Name), parent_decl_ctx: ContextPtr, UINT32_MAX, variables&: List); |
688 | } |
689 | outs() << formatv(Fmt: "Found {0} variables:\n" , Vals: List.GetSize()); |
690 | StreamString Stream; |
691 | List.Dump(s: &Stream, show_context: false); |
692 | outs() << Stream.GetData() << "\n" ; |
693 | return Error::success(); |
694 | } |
695 | |
696 | Error opts::symbols::dumpModule(lldb_private::Module &Module) { |
697 | StreamString Stream; |
698 | Module.ParseAllDebugSymbols(); |
699 | Module.Dump(s: &Stream); |
700 | outs() << Stream.GetData() << "\n" ; |
701 | return Error::success(); |
702 | } |
703 | |
704 | Error opts::symbols::dumpAST(lldb_private::Module &Module) { |
705 | Module.ParseAllDebugSymbols(); |
706 | |
707 | SymbolFile *symfile = Module.GetSymbolFile(); |
708 | if (!symfile) |
709 | return make_string_error(Format: "Module has no symbol file." ); |
710 | |
711 | auto type_system_or_err = |
712 | symfile->GetTypeSystemForLanguage(language: eLanguageTypeC_plus_plus); |
713 | if (!type_system_or_err) |
714 | return make_string_error(Format: "Can't retrieve TypeSystemClang" ); |
715 | |
716 | auto ts = *type_system_or_err; |
717 | auto *clang_ast_ctx = llvm::dyn_cast_or_null<TypeSystemClang>(Val: ts.get()); |
718 | if (!clang_ast_ctx) |
719 | return make_string_error(Format: "Retrieved TypeSystem was not a TypeSystemClang" ); |
720 | |
721 | clang::ASTContext &ast_ctx = clang_ast_ctx->getASTContext(); |
722 | |
723 | clang::TranslationUnitDecl *tu = ast_ctx.getTranslationUnitDecl(); |
724 | if (!tu) |
725 | return make_string_error(Format: "Can't retrieve translation unit declaration." ); |
726 | |
727 | tu->print(outs()); |
728 | |
729 | return Error::success(); |
730 | } |
731 | |
732 | Error opts::symbols::dumpEntireClangAST(lldb_private::Module &Module) { |
733 | Module.ParseAllDebugSymbols(); |
734 | |
735 | SymbolFile *symfile = Module.GetSymbolFile(); |
736 | if (!symfile) |
737 | return make_string_error(Format: "Module has no symbol file." ); |
738 | |
739 | auto type_system_or_err = |
740 | symfile->GetTypeSystemForLanguage(language: eLanguageTypeObjC_plus_plus); |
741 | if (!type_system_or_err) |
742 | return make_string_error(Format: "Can't retrieve TypeSystemClang" ); |
743 | auto ts = *type_system_or_err; |
744 | auto *clang_ast_ctx = llvm::dyn_cast_or_null<TypeSystemClang>(Val: ts.get()); |
745 | if (!clang_ast_ctx) |
746 | return make_string_error(Format: "Retrieved TypeSystem was not a TypeSystemClang" ); |
747 | |
748 | StreamString Stream; |
749 | clang_ast_ctx->DumpFromSymbolFile(s&: Stream, symbol_name: Name); |
750 | outs() << Stream.GetData() << "\n" ; |
751 | |
752 | return Error::success(); |
753 | } |
754 | |
755 | Error opts::symbols::verify(lldb_private::Module &Module) { |
756 | SymbolFile *symfile = Module.GetSymbolFile(); |
757 | if (!symfile) |
758 | return make_string_error(Format: "Module has no symbol file." ); |
759 | |
760 | uint32_t comp_units_count = symfile->GetNumCompileUnits(); |
761 | |
762 | outs() << "Found " << comp_units_count << " compile units.\n" ; |
763 | |
764 | for (uint32_t i = 0; i < comp_units_count; i++) { |
765 | lldb::CompUnitSP comp_unit = symfile->GetCompileUnitAtIndex(idx: i); |
766 | if (!comp_unit) |
767 | return make_string_error(Format: "Cannot parse compile unit {0}." , args&: i); |
768 | |
769 | outs() << "Processing '" |
770 | << comp_unit->GetPrimaryFile().GetFilename().AsCString() |
771 | << "' compile unit.\n" ; |
772 | |
773 | LineTable *lt = comp_unit->GetLineTable(); |
774 | if (!lt) |
775 | return make_string_error(Format: "Can't get a line table of a compile unit." ); |
776 | |
777 | uint32_t count = lt->GetSize(); |
778 | |
779 | outs() << "The line table contains " << count << " entries.\n" ; |
780 | |
781 | if (count == 0) |
782 | continue; |
783 | |
784 | LineEntry le; |
785 | if (!lt->GetLineEntryAtIndex(idx: 0, line_entry&: le)) |
786 | return make_string_error(Format: "Can't get a line entry of a compile unit." ); |
787 | |
788 | for (uint32_t i = 1; i < count; i++) { |
789 | lldb::addr_t curr_end = |
790 | le.range.GetBaseAddress().GetFileAddress() + le.range.GetByteSize(); |
791 | |
792 | if (!lt->GetLineEntryAtIndex(idx: i, line_entry&: le)) |
793 | return make_string_error(Format: "Can't get a line entry of a compile unit" ); |
794 | |
795 | if (curr_end > le.range.GetBaseAddress().GetFileAddress()) |
796 | return make_string_error( |
797 | Format: "Line table of a compile unit is inconsistent." ); |
798 | } |
799 | } |
800 | |
801 | outs() << "The symbol information is verified.\n" ; |
802 | |
803 | return Error::success(); |
804 | } |
805 | |
806 | Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { |
807 | if (Verify && DumpAST) |
808 | return make_string_error( |
809 | Format: "Cannot both verify symbol information and dump AST." ); |
810 | |
811 | if (Verify) { |
812 | if (Find != FindType::None) |
813 | return make_string_error( |
814 | Format: "Cannot both search and verify symbol information." ); |
815 | if (Regex || !Context.empty() || !Name.empty() || !File.empty() || |
816 | Line != 0) |
817 | return make_string_error( |
818 | Format: "-regex, -context, -name, -file and -line options are not " |
819 | "applicable for symbol verification." ); |
820 | return verify; |
821 | } |
822 | |
823 | if (DumpAST) { |
824 | if (Find != FindType::None) |
825 | return make_string_error(Format: "Cannot both search and dump AST." ); |
826 | if (Regex || !Context.empty() || !Name.empty() || !File.empty() || |
827 | Line != 0) |
828 | return make_string_error( |
829 | Format: "-regex, -context, -name, -file and -line options are not " |
830 | "applicable for dumping AST." ); |
831 | return dumpAST; |
832 | } |
833 | |
834 | if (DumpClangAST) { |
835 | if (Find == FindType::None) { |
836 | if (Regex || !Context.empty() || !File.empty() || Line != 0) |
837 | return make_string_error( |
838 | Format: "-regex, -context, -name, -file and -line options are not " |
839 | "applicable for dumping the entire clang AST. Either combine with " |
840 | "-find, or use -dump-clang-ast as a standalone option." ); |
841 | return dumpEntireClangAST; |
842 | } |
843 | if (Find != FindType::Type) |
844 | return make_string_error(Format: "This combination of -dump-clang-ast and -find " |
845 | "<kind> is not yet implemented." ); |
846 | } |
847 | |
848 | if (Regex && !Context.empty()) |
849 | return make_string_error( |
850 | Format: "Cannot search using both regular expressions and context." ); |
851 | |
852 | if (Regex && !RegularExpression(Name).IsValid()) |
853 | return make_string_error(Format: "`{0}` is not a valid regular expression." , args&: Name); |
854 | |
855 | if (Regex + !Context.empty() + !File.empty() >= 2) |
856 | return make_string_error( |
857 | Format: "Only one of -regex, -context and -file may be used simultaneously." ); |
858 | if (Regex && Name.empty()) |
859 | return make_string_error(Format: "-regex used without a -name" ); |
860 | |
861 | if (FindInAnyModule && (Find != FindType::Type)) |
862 | return make_string_error(Format: "-find-in-any-module only works with -find=type" ); |
863 | |
864 | switch (Find) { |
865 | case FindType::None: |
866 | if (!Context.empty() || !Name.empty() || !File.empty() || Line != 0) |
867 | return make_string_error( |
868 | Format: "Specify search type (-find) to use search options." ); |
869 | return dumpModule; |
870 | |
871 | case FindType::Function: |
872 | if (!File.empty() + (Line != 0) == 1) |
873 | return make_string_error(Format: "Both file name and line number must be " |
874 | "specified when searching a function " |
875 | "by file position." ); |
876 | if (Regex + (getFunctionNameFlags() != 0) + !File.empty() >= 2) |
877 | return make_string_error(Format: "Only one of regular expression, function-flags " |
878 | "and file position may be used simultaneously " |
879 | "when searching a function." ); |
880 | return findFunctions; |
881 | |
882 | case FindType::Block: |
883 | if (File.empty() || Line == 0) |
884 | return make_string_error(Format: "Both file name and line number must be " |
885 | "specified when searching a block." ); |
886 | if (Regex || getFunctionNameFlags() != 0) |
887 | return make_string_error(Format: "Cannot use regular expression or " |
888 | "function-flags for searching a block." ); |
889 | return findBlocks; |
890 | |
891 | case FindType::Namespace: |
892 | if (Regex || !File.empty() || Line != 0) |
893 | return make_string_error(Format: "Cannot search for namespaces using regular " |
894 | "expressions, file names or line numbers." ); |
895 | return findNamespaces; |
896 | |
897 | case FindType::Type: |
898 | if (Regex || !File.empty() || Line != 0) |
899 | return make_string_error(Format: "Cannot search for types using regular " |
900 | "expressions, file names or line numbers." ); |
901 | if (!Name.empty() && !CompilerContext.empty()) |
902 | return make_string_error(Format: "Name is ignored if compiler context present." ); |
903 | |
904 | return findTypes; |
905 | |
906 | case FindType::Variable: |
907 | if (Line != 0) |
908 | return make_string_error(Format: "Cannot search for variables " |
909 | "using line numbers." ); |
910 | return findVariables; |
911 | } |
912 | |
913 | llvm_unreachable("Unsupported symbol action." ); |
914 | } |
915 | |
916 | std::optional<llvm::Error> opts::symtab::validate() { |
917 | if (ManglingPreference != ManglingPreference::None && |
918 | FindSymbolsByRegex.empty()) |
919 | return make_string_error(Format: "Mangling preference set but no regex specified." ); |
920 | |
921 | return {}; |
922 | } |
923 | |
924 | static Mangled::NamePreference opts::symtab::getNamePreference() { |
925 | switch (ManglingPreference) { |
926 | case ManglingPreference::None: |
927 | case ManglingPreference::Mangled: |
928 | return Mangled::ePreferMangled; |
929 | case ManglingPreference::Demangled: |
930 | return Mangled::ePreferDemangled; |
931 | case ManglingPreference::MangledWithoutArguments: |
932 | return Mangled::ePreferDemangledWithoutArguments; |
933 | } |
934 | llvm_unreachable("Fully covered switch above!" ); |
935 | } |
936 | |
937 | int opts::symtab::handleSymtabCommand(Debugger &Dbg) { |
938 | if (auto error = validate()) { |
939 | logAllUnhandledErrors(E: std::move(*error), OS&: WithColor::error(), ErrorBanner: "" ); |
940 | return 1; |
941 | } |
942 | |
943 | if (!FindSymbolsByRegex.empty()) { |
944 | ModuleSpec Spec{FileSpec(InputFile)}; |
945 | |
946 | auto ModulePtr = std::make_shared<lldb_private::Module>(args&: Spec); |
947 | auto *Symtab = ModulePtr->GetSymtab(); |
948 | auto NamePreference = getNamePreference(); |
949 | std::vector<uint32_t> Indexes; |
950 | |
951 | Symtab->FindAllSymbolsMatchingRexExAndType( |
952 | regex: RegularExpression(FindSymbolsByRegex), symbol_type: lldb::eSymbolTypeAny, |
953 | symbol_debug_type: Symtab::eDebugAny, symbol_visibility: Symtab::eVisibilityAny, symbol_indexes&: Indexes, name_preference: NamePreference); |
954 | for (auto i : Indexes) { |
955 | auto *symbol = Symtab->SymbolAtIndex(idx: i); |
956 | if (symbol) { |
957 | StreamString stream; |
958 | symbol->Dump(s: &stream, target: nullptr, index: i, name_preference: NamePreference); |
959 | outs() << stream.GetString(); |
960 | } |
961 | } |
962 | } |
963 | |
964 | return 0; |
965 | } |
966 | |
967 | int opts::symbols::dumpSymbols(Debugger &Dbg) { |
968 | auto ActionOr = getAction(); |
969 | if (!ActionOr) { |
970 | logAllUnhandledErrors(E: ActionOr.takeError(), OS&: WithColor::error(), ErrorBanner: "" ); |
971 | return 1; |
972 | } |
973 | auto Action = *ActionOr; |
974 | |
975 | outs() << "Module: " << InputFile << "\n" ; |
976 | ModuleSpec Spec{FileSpec(InputFile)}; |
977 | StringRef Symbols = SymbolPath.empty() ? InputFile : SymbolPath; |
978 | Spec.GetSymbolFileSpec().SetFile(path: Symbols, style: FileSpec::Style::native); |
979 | |
980 | auto ModulePtr = std::make_shared<lldb_private::Module>(args&: Spec); |
981 | SymbolFile *Symfile = ModulePtr->GetSymbolFile(); |
982 | if (!Symfile) { |
983 | WithColor::error() << "Module has no symbol vendor.\n" ; |
984 | return 1; |
985 | } |
986 | |
987 | if (Error E = Action(*ModulePtr)) { |
988 | WithColor::error() << toString(E: std::move(E)) << "\n" ; |
989 | return 1; |
990 | } |
991 | |
992 | return 0; |
993 | } |
994 | |
995 | static void dumpSectionList(LinePrinter &Printer, const SectionList &List, bool is_subsection) { |
996 | size_t Count = List.GetNumSections(depth: 0); |
997 | if (Count == 0) { |
998 | Printer.formatLine(Fmt: "There are no {0}sections" , Items: is_subsection ? "sub" : "" ); |
999 | return; |
1000 | } |
1001 | Printer.formatLine(Fmt: "Showing {0} {1}sections" , Items&: Count, |
1002 | Items: is_subsection ? "sub" : "" ); |
1003 | for (size_t I = 0; I < Count; ++I) { |
1004 | auto S = List.GetSectionAtIndex(idx: I); |
1005 | assert(S); |
1006 | AutoIndent Indent(Printer, 2); |
1007 | Printer.formatLine(Fmt: "Index: {0}" , Items&: I); |
1008 | Printer.formatLine(Fmt: "ID: {0:x}" , Items: S->GetID()); |
1009 | Printer.formatLine(Fmt: "Name: {0}" , Items: S->GetName().GetStringRef()); |
1010 | Printer.formatLine(Fmt: "Type: {0}" , Items: S->GetTypeAsCString()); |
1011 | Printer.formatLine(Fmt: "Permissions: {0}" , Items: GetPermissionsAsCString(permissions: S->GetPermissions())); |
1012 | Printer.formatLine(Fmt: "Thread specific: {0:y}" , Items: S->IsThreadSpecific()); |
1013 | Printer.formatLine(Fmt: "VM address: {0:x}" , Items: S->GetFileAddress()); |
1014 | Printer.formatLine(Fmt: "VM size: {0}" , Items: S->GetByteSize()); |
1015 | Printer.formatLine(Fmt: "File size: {0}" , Items: S->GetFileSize()); |
1016 | |
1017 | if (opts::object::SectionContents) { |
1018 | lldb_private::DataExtractor Data; |
1019 | S->GetSectionData(data&: Data); |
1020 | ArrayRef<uint8_t> Bytes(Data.GetDataStart(), Data.GetDataEnd()); |
1021 | Printer.formatBinary(Label: "Data: " , Data: Bytes, StartOffset: 0); |
1022 | } |
1023 | |
1024 | if (S->GetType() == eSectionTypeContainer) |
1025 | dumpSectionList(Printer, List: S->GetChildren(), is_subsection: true); |
1026 | Printer.NewLine(); |
1027 | } |
1028 | } |
1029 | |
1030 | static int dumpObjectFiles(Debugger &Dbg) { |
1031 | LinePrinter Printer(4, llvm::outs()); |
1032 | |
1033 | int HadErrors = 0; |
1034 | for (const auto &File : opts::object::InputFilenames) { |
1035 | ModuleSpec Spec{FileSpec(File)}; |
1036 | |
1037 | auto ModulePtr = std::make_shared<lldb_private::Module>(args&: Spec); |
1038 | |
1039 | ObjectFile *ObjectPtr = ModulePtr->GetObjectFile(); |
1040 | if (!ObjectPtr) { |
1041 | WithColor::error() << File << " not recognised as an object file\n" ; |
1042 | HadErrors = 1; |
1043 | continue; |
1044 | } |
1045 | |
1046 | // Fetch symbol vendor before we get the section list to give the symbol |
1047 | // vendor a chance to populate it. |
1048 | ModulePtr->GetSymbolFile(); |
1049 | SectionList *Sections = ModulePtr->GetSectionList(); |
1050 | if (!Sections) { |
1051 | llvm::errs() << "Could not load sections for module " << File << "\n" ; |
1052 | HadErrors = 1; |
1053 | continue; |
1054 | } |
1055 | |
1056 | Printer.formatLine(Fmt: "Plugin name: {0}" , Items: ObjectPtr->GetPluginName()); |
1057 | Printer.formatLine(Fmt: "Architecture: {0}" , |
1058 | Items: ModulePtr->GetArchitecture().GetTriple().getTriple()); |
1059 | Printer.formatLine(Fmt: "UUID: {0}" , Items: ModulePtr->GetUUID().GetAsString()); |
1060 | Printer.formatLine(Fmt: "Executable: {0}" , Items: ObjectPtr->IsExecutable()); |
1061 | Printer.formatLine(Fmt: "Stripped: {0}" , Items: ObjectPtr->IsStripped()); |
1062 | Printer.formatLine(Fmt: "Type: {0}" , Items: ObjectPtr->GetType()); |
1063 | Printer.formatLine(Fmt: "Strata: {0}" , Items: ObjectPtr->GetStrata()); |
1064 | Printer.formatLine(Fmt: "Base VM address: {0:x}" , |
1065 | Items: ObjectPtr->GetBaseAddress().GetFileAddress()); |
1066 | |
1067 | dumpSectionList(Printer, List: *Sections, /*is_subsection*/ false); |
1068 | |
1069 | if (opts::object::SectionDependentModules) { |
1070 | // A non-empty section list ensures a valid object file. |
1071 | auto Obj = ModulePtr->GetObjectFile(); |
1072 | FileSpecList Files; |
1073 | auto Count = Obj->GetDependentModules(file_list&: Files); |
1074 | Printer.formatLine(Fmt: "Showing {0} dependent module(s)" , Items&: Count); |
1075 | for (size_t I = 0; I < Files.GetSize(); ++I) { |
1076 | AutoIndent Indent(Printer, 2); |
1077 | Printer.formatLine(Fmt: "Name: {0}" , |
1078 | Items: Files.GetFileSpecAtIndex(idx: I).GetPath()); |
1079 | } |
1080 | Printer.NewLine(); |
1081 | } |
1082 | } |
1083 | return HadErrors; |
1084 | } |
1085 | |
1086 | bool opts::irmemorymap::evalMalloc(StringRef Line, |
1087 | IRMemoryMapTestState &State) { |
1088 | // ::= <label> = malloc <size> <alignment> |
1089 | StringRef Label; |
1090 | std::tie(args&: Label, args&: Line) = Line.split(Separator: '='); |
1091 | if (Line.empty()) |
1092 | return false; |
1093 | Label = Label.trim(); |
1094 | Line = Line.trim(); |
1095 | size_t Size; |
1096 | uint8_t Alignment; |
1097 | int Matches = sscanf(s: Line.data(), format: "malloc %zu %hhu" , &Size, &Alignment); |
1098 | if (Matches != 2) |
1099 | return false; |
1100 | |
1101 | outs() << formatv(Fmt: "Command: {0} = malloc(size={1}, alignment={2})\n" , Vals&: Label, |
1102 | Vals&: Size, Vals&: Alignment); |
1103 | if (!isPowerOf2_32(Value: Alignment)) { |
1104 | outs() << "Malloc error: alignment is not a power of 2\n" ; |
1105 | exit(status: 1); |
1106 | } |
1107 | |
1108 | IRMemoryMap::AllocationPolicy AP = |
1109 | UseHostOnlyAllocationPolicy ? IRMemoryMap::eAllocationPolicyHostOnly |
1110 | : IRMemoryMap::eAllocationPolicyProcessOnly; |
1111 | |
1112 | // Issue the malloc in the target process with "-rw" permissions. |
1113 | const uint32_t Permissions = 0x3; |
1114 | const bool ZeroMemory = false; |
1115 | Status ST; |
1116 | addr_t Addr = |
1117 | State.Map.Malloc(size: Size, alignment: Alignment, permissions: Permissions, policy: AP, zero_memory: ZeroMemory, error&: ST); |
1118 | if (ST.Fail()) { |
1119 | outs() << formatv(Fmt: "Malloc error: {0}\n" , Vals&: ST); |
1120 | return true; |
1121 | } |
1122 | |
1123 | // Print the result of the allocation before checking its validity. |
1124 | outs() << formatv(Fmt: "Malloc: address = {0:x}\n" , Vals&: Addr); |
1125 | |
1126 | // Check that the allocation is aligned. |
1127 | if (!Addr || Addr % Alignment != 0) { |
1128 | outs() << "Malloc error: zero or unaligned allocation detected\n" ; |
1129 | exit(status: 1); |
1130 | } |
1131 | |
1132 | // In case of Size == 0, we still expect the returned address to be unique and |
1133 | // non-overlapping. |
1134 | addr_t EndOfRegion = Addr + std::max<size_t>(a: Size, b: 1); |
1135 | if (State.Allocations.overlaps(a: Addr, b: EndOfRegion)) { |
1136 | auto I = State.Allocations.find(x: Addr); |
1137 | outs() << "Malloc error: overlapping allocation detected" |
1138 | << formatv(Fmt: ", previous allocation at [{0:x}, {1:x})\n" , Vals: I.start(), |
1139 | Vals: I.stop()); |
1140 | exit(status: 1); |
1141 | } |
1142 | |
1143 | // Insert the new allocation into the interval map. Use unique allocation |
1144 | // IDs to inhibit interval coalescing. |
1145 | static unsigned AllocationID = 0; |
1146 | State.Allocations.insert(a: Addr, b: EndOfRegion, y: AllocationID++); |
1147 | |
1148 | // Store the label -> address mapping. |
1149 | State.Label2AddrMap[Label] = Addr; |
1150 | |
1151 | return true; |
1152 | } |
1153 | |
1154 | bool opts::irmemorymap::evalFree(StringRef Line, IRMemoryMapTestState &State) { |
1155 | // ::= free <label> |
1156 | if (!Line.consume_front(Prefix: "free" )) |
1157 | return false; |
1158 | StringRef Label = Line.trim(); |
1159 | |
1160 | outs() << formatv(Fmt: "Command: free({0})\n" , Vals&: Label); |
1161 | auto LabelIt = State.Label2AddrMap.find(Key: Label); |
1162 | if (LabelIt == State.Label2AddrMap.end()) { |
1163 | outs() << "Free error: Invalid allocation label\n" ; |
1164 | exit(status: 1); |
1165 | } |
1166 | |
1167 | Status ST; |
1168 | addr_t Addr = LabelIt->getValue(); |
1169 | State.Map.Free(process_address: Addr, error&: ST); |
1170 | if (ST.Fail()) { |
1171 | outs() << formatv(Fmt: "Free error: {0}\n" , Vals&: ST); |
1172 | exit(status: 1); |
1173 | } |
1174 | |
1175 | // Erase the allocation from the live interval map. |
1176 | auto Interval = State.Allocations.find(x: Addr); |
1177 | if (Interval != State.Allocations.end()) { |
1178 | outs() << formatv(Fmt: "Free: [{0:x}, {1:x})\n" , Vals: Interval.start(), |
1179 | Vals: Interval.stop()); |
1180 | Interval.erase(); |
1181 | } |
1182 | |
1183 | return true; |
1184 | } |
1185 | |
1186 | int opts::irmemorymap::evaluateMemoryMapCommands(Debugger &Dbg) { |
1187 | // Set up a Target. |
1188 | TargetSP Target = opts::createTarget(Dbg, Filename: irmemorymap::Target); |
1189 | |
1190 | // Set up a Process. In order to allocate memory within a target, this |
1191 | // process must be alive and must support JIT'ing. |
1192 | CommandReturnObject Result(/*colors*/ false); |
1193 | Dbg.SetAsyncExecution(false); |
1194 | CommandInterpreter &CI = Dbg.GetCommandInterpreter(); |
1195 | auto IssueCmd = [&](const char *Cmd) -> bool { |
1196 | return CI.HandleCommand(command_line: Cmd, add_to_history: eLazyBoolNo, result&: Result); |
1197 | }; |
1198 | if (!IssueCmd("b main" ) || !IssueCmd("run" )) { |
1199 | outs() << formatv(Fmt: "Failed: {0}\n" , Vals: Result.GetErrorString()); |
1200 | exit(status: 1); |
1201 | } |
1202 | |
1203 | ProcessSP Process = Target->GetProcessSP(); |
1204 | if (!Process || !Process->IsAlive() || !Process->CanJIT()) { |
1205 | outs() << "Cannot use process to test IRMemoryMap\n" ; |
1206 | exit(status: 1); |
1207 | } |
1208 | |
1209 | // Set up an IRMemoryMap and associated testing state. |
1210 | IRMemoryMapTestState State(Target); |
1211 | |
1212 | // Parse and apply commands from the command file. |
1213 | std::unique_ptr<MemoryBuffer> MB = opts::openFile(Filename: irmemorymap::CommandFile); |
1214 | StringRef Rest = MB->getBuffer(); |
1215 | while (!Rest.empty()) { |
1216 | StringRef Line; |
1217 | std::tie(args&: Line, args&: Rest) = Rest.split(Separator: '\n'); |
1218 | Line = Line.ltrim().rtrim(); |
1219 | |
1220 | if (Line.empty() || Line[0] == '#') |
1221 | continue; |
1222 | |
1223 | if (evalMalloc(Line, State)) |
1224 | continue; |
1225 | |
1226 | if (evalFree(Line, State)) |
1227 | continue; |
1228 | |
1229 | errs() << "Could not parse line: " << Line << "\n" ; |
1230 | exit(status: 1); |
1231 | } |
1232 | return 0; |
1233 | } |
1234 | |
1235 | int opts::assert::lldb_assert(Debugger &Dbg) { |
1236 | lldbassert(false && "lldb-test assert" ); |
1237 | return 1; |
1238 | } |
1239 | |
1240 | int main(int argc, const char *argv[]) { |
1241 | StringRef ToolName = argv[0]; |
1242 | sys::PrintStackTraceOnErrorSignal(Argv0: ToolName); |
1243 | PrettyStackTraceProgram X(argc, argv); |
1244 | llvm_shutdown_obj Y; |
1245 | |
1246 | cl::ParseCommandLineOptions(argc, argv, Overview: "LLDB Testing Utility\n" ); |
1247 | |
1248 | SystemLifetimeManager DebuggerLifetime; |
1249 | if (auto e = DebuggerLifetime.Initialize( |
1250 | initializer: std::make_unique<SystemInitializerTest>())) { |
1251 | WithColor::error() << "initialization failed: " << toString(E: std::move(e)) |
1252 | << '\n'; |
1253 | return 1; |
1254 | } |
1255 | |
1256 | auto TerminateDebugger = |
1257 | llvm::make_scope_exit(F: [&] { DebuggerLifetime.Terminate(); }); |
1258 | |
1259 | auto Dbg = lldb_private::Debugger::CreateInstance(); |
1260 | ModuleList::GetGlobalModuleListProperties().SetEnableExternalLookup(false); |
1261 | CommandReturnObject Result(/*colors*/ false); |
1262 | Dbg->GetCommandInterpreter().HandleCommand( |
1263 | command_line: "settings set plugin.process.gdb-remote.packet-timeout 60" , |
1264 | /*add_to_history*/ eLazyBoolNo, result&: Result); |
1265 | Dbg->GetCommandInterpreter().HandleCommand( |
1266 | command_line: "settings set target.inherit-tcc true" , |
1267 | /*add_to_history*/ eLazyBoolNo, result&: Result); |
1268 | Dbg->GetCommandInterpreter().HandleCommand( |
1269 | command_line: "settings set target.detach-on-error false" , |
1270 | /*add_to_history*/ eLazyBoolNo, result&: Result); |
1271 | |
1272 | if (!opts::Log.empty()) |
1273 | Dbg->EnableLog(channel: "lldb" , categories: {"all" }, log_file: opts::Log, log_options: 0, buffer_size: 0, log_handler_kind: eLogHandlerStream, error_stream&: errs()); |
1274 | |
1275 | if (opts::BreakpointSubcommand) |
1276 | return opts::breakpoint::evaluateBreakpoints(Dbg&: *Dbg); |
1277 | if (opts::ObjectFileSubcommand) |
1278 | return dumpObjectFiles(Dbg&: *Dbg); |
1279 | if (opts::SymbolsSubcommand) |
1280 | return opts::symbols::dumpSymbols(Dbg&: *Dbg); |
1281 | if (opts::SymTabSubcommand) |
1282 | return opts::symtab::handleSymtabCommand(Dbg&: *Dbg); |
1283 | if (opts::IRMemoryMapSubcommand) |
1284 | return opts::irmemorymap::evaluateMemoryMapCommands(Dbg&: *Dbg); |
1285 | if (opts::AssertSubcommand) |
1286 | return opts::assert::lldb_assert(Dbg&: *Dbg); |
1287 | |
1288 | WithColor::error() << "No command specified.\n" ; |
1289 | return 1; |
1290 | } |
1291 | |