1 | //===-- CompileUnit.cpp ---------------------------------------------------===// |
---|---|
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 "lldb/Symbol/CompileUnit.h" |
10 | #include "lldb/Core/Module.h" |
11 | #include "lldb/Symbol/LineTable.h" |
12 | #include "lldb/Symbol/SymbolFile.h" |
13 | #include "lldb/Symbol/VariableList.h" |
14 | #include "lldb/Target/Language.h" |
15 | #include "lldb/Utility/Timer.h" |
16 | #include <optional> |
17 | |
18 | using namespace lldb; |
19 | using namespace lldb_private; |
20 | |
21 | CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, |
22 | const char *pathname, const lldb::user_id_t cu_sym_id, |
23 | lldb::LanguageType language, |
24 | lldb_private::LazyBool is_optimized) |
25 | : CompileUnit(module_sp, user_data, |
26 | std::make_shared<SupportFile>(args: FileSpec(pathname)), cu_sym_id, |
27 | language, is_optimized) {} |
28 | |
29 | CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, |
30 | lldb::SupportFileSP support_file_sp, |
31 | const lldb::user_id_t cu_sym_id, |
32 | lldb::LanguageType language, |
33 | lldb_private::LazyBool is_optimized, |
34 | SupportFileList &&support_files) |
35 | : ModuleChild(module_sp), UserID(cu_sym_id), m_user_data(user_data), |
36 | m_language(language), m_flags(0), |
37 | m_primary_support_file_sp(support_file_sp), |
38 | m_support_files(std::move(support_files)), m_is_optimized(is_optimized) { |
39 | if (language != eLanguageTypeUnknown) |
40 | m_flags.Set(flagsParsedLanguage); |
41 | assert(module_sp); |
42 | } |
43 | |
44 | void CompileUnit::CalculateSymbolContext(SymbolContext *sc) { |
45 | sc->comp_unit = this; |
46 | GetModule()->CalculateSymbolContext(sc); |
47 | } |
48 | |
49 | ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); } |
50 | |
51 | CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; } |
52 | |
53 | void CompileUnit::DumpSymbolContext(Stream *s) { |
54 | GetModule()->DumpSymbolContext(s); |
55 | s->Printf(format: ", CompileUnit{0x%8.8"PRIx64 "}", GetID()); |
56 | } |
57 | |
58 | void CompileUnit::GetDescription(Stream *s, |
59 | lldb::DescriptionLevel level) const { |
60 | const char *language = GetCachedLanguage(); |
61 | *s << "id = "<< (const UserID &)*this << ", file = \"" |
62 | << this->GetPrimaryFile() << "\", language = \""<< language << '"'; |
63 | } |
64 | |
65 | void CompileUnit::ForeachFunction( |
66 | llvm::function_ref<bool(const FunctionSP &)> lambda) const { |
67 | std::vector<lldb::FunctionSP> sorted_functions; |
68 | sorted_functions.reserve(n: m_functions_by_uid.size()); |
69 | for (auto &p : m_functions_by_uid) |
70 | sorted_functions.push_back(x: p.second); |
71 | llvm::sort(C&: sorted_functions, |
72 | Comp: [](const lldb::FunctionSP &a, const lldb::FunctionSP &b) { |
73 | return a->GetID() < b->GetID(); |
74 | }); |
75 | |
76 | for (auto &f : sorted_functions) |
77 | if (lambda(f)) |
78 | return; |
79 | } |
80 | |
81 | lldb::FunctionSP CompileUnit::FindFunction( |
82 | llvm::function_ref<bool(const FunctionSP &)> matching_lambda) { |
83 | LLDB_SCOPED_TIMER(); |
84 | |
85 | lldb::ModuleSP module = CalculateSymbolContextModule(); |
86 | |
87 | if (!module) |
88 | return {}; |
89 | |
90 | SymbolFile *symbol_file = module->GetSymbolFile(); |
91 | |
92 | if (!symbol_file) |
93 | return {}; |
94 | |
95 | // m_functions_by_uid is filled in lazily but we need all the entries. |
96 | symbol_file->ParseFunctions(comp_unit&: *this); |
97 | |
98 | for (auto &p : m_functions_by_uid) { |
99 | if (matching_lambda(p.second)) |
100 | return p.second; |
101 | } |
102 | return {}; |
103 | } |
104 | |
105 | const char *CompileUnit::GetCachedLanguage() const { |
106 | if (m_flags.IsClear(bit: flagsParsedLanguage)) |
107 | return "<not loaded>"; |
108 | return Language::GetNameForLanguageType(language: m_language); |
109 | } |
110 | |
111 | // Dump the current contents of this object. No functions that cause on demand |
112 | // parsing of functions, globals, statics are called, so this is a good |
113 | // function to call to get an idea of the current contents of the CompileUnit |
114 | // object. |
115 | void CompileUnit::Dump(Stream *s, bool show_context) const { |
116 | const char *language = GetCachedLanguage(); |
117 | |
118 | s->Printf(format: "%p: ", static_cast<const void *>(this)); |
119 | s->Indent(); |
120 | *s << "CompileUnit"<< static_cast<const UserID &>(*this) << ", language = \"" |
121 | << language << "\", file = '"<< GetPrimaryFile() << "'\n"; |
122 | |
123 | // m_types.Dump(s); |
124 | |
125 | if (m_variables.get()) { |
126 | s->IndentMore(); |
127 | m_variables->Dump(s, show_context); |
128 | s->IndentLess(); |
129 | } |
130 | |
131 | if (!m_functions_by_uid.empty()) { |
132 | s->IndentMore(); |
133 | ForeachFunction(lambda: [&s, show_context](const FunctionSP &f) { |
134 | f->Dump(s, show_context); |
135 | return false; |
136 | }); |
137 | |
138 | s->IndentLess(); |
139 | s->EOL(); |
140 | } |
141 | } |
142 | |
143 | // Add a function to this compile unit |
144 | void CompileUnit::AddFunction(FunctionSP &funcSP) { |
145 | m_functions_by_uid[funcSP->GetID()] = funcSP; |
146 | } |
147 | |
148 | FunctionSP CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid) { |
149 | auto it = m_functions_by_uid.find(Val: func_uid); |
150 | if (it == m_functions_by_uid.end()) |
151 | return FunctionSP(); |
152 | return it->second; |
153 | } |
154 | |
155 | lldb::LanguageType CompileUnit::GetLanguage() { |
156 | if (m_language == eLanguageTypeUnknown) { |
157 | if (m_flags.IsClear(bit: flagsParsedLanguage)) { |
158 | m_flags.Set(flagsParsedLanguage); |
159 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) |
160 | m_language = symfile->ParseLanguage(comp_unit&: *this); |
161 | } |
162 | } |
163 | return m_language; |
164 | } |
165 | |
166 | LineTable *CompileUnit::GetLineTable() { |
167 | if (m_line_table_up == nullptr) { |
168 | if (m_flags.IsClear(bit: flagsParsedLineTable)) { |
169 | m_flags.Set(flagsParsedLineTable); |
170 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) |
171 | symfile->ParseLineTable(comp_unit&: *this); |
172 | } |
173 | } |
174 | return m_line_table_up.get(); |
175 | } |
176 | |
177 | void CompileUnit::SetLineTable(LineTable *line_table) { |
178 | if (line_table == nullptr) |
179 | m_flags.Clear(mask: flagsParsedLineTable); |
180 | else |
181 | m_flags.Set(flagsParsedLineTable); |
182 | m_line_table_up.reset(p: line_table); |
183 | } |
184 | |
185 | DebugMacros *CompileUnit::GetDebugMacros() { |
186 | if (m_debug_macros_sp.get() == nullptr) { |
187 | if (m_flags.IsClear(bit: flagsParsedDebugMacros)) { |
188 | m_flags.Set(flagsParsedDebugMacros); |
189 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) |
190 | symfile->ParseDebugMacros(comp_unit&: *this); |
191 | } |
192 | } |
193 | |
194 | return m_debug_macros_sp.get(); |
195 | } |
196 | |
197 | void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) { |
198 | if (debug_macros_sp.get() == nullptr) |
199 | m_flags.Clear(mask: flagsParsedDebugMacros); |
200 | else |
201 | m_flags.Set(flagsParsedDebugMacros); |
202 | m_debug_macros_sp = debug_macros_sp; |
203 | } |
204 | |
205 | VariableListSP CompileUnit::GetVariableList(bool can_create) { |
206 | if (m_variables.get() == nullptr && can_create) { |
207 | SymbolContext sc; |
208 | CalculateSymbolContext(sc: &sc); |
209 | assert(sc.module_sp); |
210 | sc.module_sp->GetSymbolFile()->ParseVariablesForContext(sc); |
211 | } |
212 | |
213 | return m_variables; |
214 | } |
215 | |
216 | std::vector<uint32_t> |
217 | FindFileIndexes(const SupportFileList &files, const FileSpec &file, |
218 | RealpathPrefixes *realpath_prefixes = nullptr) { |
219 | std::vector<uint32_t> result; |
220 | uint32_t idx = -1; |
221 | while ((idx = files.FindCompatibleIndex(idx: idx + 1, file, realpath_prefixes)) != |
222 | UINT32_MAX) |
223 | result.push_back(x: idx); |
224 | return result; |
225 | } |
226 | |
227 | uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line, |
228 | const FileSpec *file_spec_ptr, bool exact, |
229 | LineEntry *line_entry_ptr) { |
230 | if (!file_spec_ptr) |
231 | file_spec_ptr = &GetPrimaryFile(); |
232 | std::vector<uint32_t> file_indexes = FindFileIndexes(files: GetSupportFiles(), |
233 | file: *file_spec_ptr); |
234 | if (file_indexes.empty()) |
235 | return UINT32_MAX; |
236 | |
237 | // TODO: Handle SourceLocationSpec column information |
238 | SourceLocationSpec location_spec(*file_spec_ptr, line, |
239 | /*column=*/std::nullopt, |
240 | /*check_inlines=*/false, exact); |
241 | |
242 | LineTable *line_table = GetLineTable(); |
243 | if (line_table) |
244 | return line_table->FindLineEntryIndexByFileIndex( |
245 | start_idx, file_idx: file_indexes, src_location_spec: location_spec, line_entry_ptr); |
246 | return UINT32_MAX; |
247 | } |
248 | |
249 | void CompileUnit::ResolveSymbolContext( |
250 | const SourceLocationSpec &src_location_spec, |
251 | SymbolContextItem resolve_scope, SymbolContextList &sc_list, |
252 | RealpathPrefixes *realpath_prefixes) { |
253 | const FileSpec file_spec = src_location_spec.GetFileSpec(); |
254 | const uint32_t line = |
255 | src_location_spec.GetLine().value_or(LLDB_INVALID_LINE_NUMBER); |
256 | const uint32_t column_num = |
257 | src_location_spec.GetColumn().value_or(LLDB_INVALID_COLUMN_NUMBER); |
258 | const bool check_inlines = src_location_spec.GetCheckInlines(); |
259 | |
260 | // First find all of the file indexes that match our "file_spec". If |
261 | // "file_spec" has an empty directory, then only compare the basenames when |
262 | // finding file indexes |
263 | bool file_spec_matches_cu_file_spec = |
264 | FileSpec::Match(pattern: file_spec, file: this->GetPrimaryFile()); |
265 | |
266 | // If we are not looking for inlined functions and our file spec doesn't |
267 | // match then we are done... |
268 | if (!file_spec_matches_cu_file_spec && !check_inlines) |
269 | return; |
270 | |
271 | SymbolContext sc(GetModule()); |
272 | sc.comp_unit = this; |
273 | |
274 | if (line == LLDB_INVALID_LINE_NUMBER) { |
275 | if (file_spec_matches_cu_file_spec && !check_inlines) { |
276 | // only append the context if we aren't looking for inline call sites by |
277 | // file and line and if the file spec matches that of the compile unit |
278 | sc_list.Append(sc); |
279 | } |
280 | return; |
281 | } |
282 | |
283 | std::vector<uint32_t> file_indexes = |
284 | FindFileIndexes(files: GetSupportFiles(), file: file_spec, realpath_prefixes); |
285 | const size_t num_file_indexes = file_indexes.size(); |
286 | if (num_file_indexes == 0) |
287 | return; |
288 | |
289 | // Found a matching source file in this compile unit load its debug info. |
290 | GetModule()->GetSymbolFile()->SetLoadDebugInfoEnabled(); |
291 | |
292 | LineTable *line_table = sc.comp_unit->GetLineTable(); |
293 | |
294 | if (line_table == nullptr) { |
295 | if (file_spec_matches_cu_file_spec && !check_inlines) { |
296 | sc_list.Append(sc); |
297 | } |
298 | return; |
299 | } |
300 | |
301 | uint32_t line_idx; |
302 | LineEntry line_entry; |
303 | |
304 | if (num_file_indexes == 1) { |
305 | // We only have a single support file that matches, so use the line |
306 | // table function that searches for a line entries that match a single |
307 | // support file index |
308 | line_idx = line_table->FindLineEntryIndexByFileIndex( |
309 | start_idx: 0, file_idx: file_indexes.front(), src_location_spec, line_entry_ptr: &line_entry); |
310 | } else { |
311 | // We found multiple support files that match "file_spec" so use the |
312 | // line table function that searches for a line entries that match a |
313 | // multiple support file indexes. |
314 | line_idx = line_table->FindLineEntryIndexByFileIndex( |
315 | start_idx: 0, file_idx: file_indexes, src_location_spec, line_entry_ptr: &line_entry); |
316 | } |
317 | |
318 | // If we didn't manage to find a breakpoint that matched the line number |
319 | // requested, that might be because it is only an inline call site, and |
320 | // doesn't have a line entry in the line table. Scan for that here. |
321 | // |
322 | // We are making the assumption that if there was an inlined function it will |
323 | // contribute at least 1 non-call-site entry to the line table. That's handy |
324 | // because we don't move line breakpoints over function boundaries, so if we |
325 | // found a hit, and there were also a call site entry, it would have to be in |
326 | // the function containing the PC of the line table match. That way we can |
327 | // limit the call site search to that function. |
328 | // We will miss functions that ONLY exist as a call site entry. |
329 | |
330 | if (line_entry.IsValid() && |
331 | (line_entry.line != line || |
332 | (column_num != 0 && line_entry.column != column_num)) && |
333 | (resolve_scope & eSymbolContextLineEntry) && check_inlines) { |
334 | // We don't move lines over function boundaries, so the address in the |
335 | // line entry will be the in function that contained the line that might |
336 | // be a CallSite, and we can just iterate over that function to find any |
337 | // inline records, and dig up their call sites. |
338 | Address start_addr = line_entry.range.GetBaseAddress(); |
339 | Function *function = start_addr.CalculateSymbolContextFunction(); |
340 | // Record the size of the list to see if we added to it: |
341 | size_t old_sc_list_size = sc_list.GetSize(); |
342 | |
343 | Declaration sought_decl(file_spec, line, column_num); |
344 | // We use this recursive function to descend the block structure looking |
345 | // for a block that has this Declaration as in it's CallSite info. |
346 | // This function recursively scans the sibling blocks of the incoming |
347 | // block parameter. |
348 | std::function<void(Block &)> examine_block = |
349 | [&sought_decl, &sc_list, &src_location_spec, resolve_scope, |
350 | &examine_block](Block &block) -> void { |
351 | // Iterate over the sibling child blocks of the incoming block. |
352 | Block *sibling_block = block.GetFirstChild(); |
353 | while (sibling_block) { |
354 | // We only have to descend through the regular blocks, looking for |
355 | // immediate inlines, since those are the only ones that will have this |
356 | // callsite. |
357 | const InlineFunctionInfo *inline_info = |
358 | sibling_block->GetInlinedFunctionInfo(); |
359 | if (inline_info) { |
360 | // If this is the call-site we are looking for, record that: |
361 | // We need to be careful because the call site from the debug info |
362 | // will generally have a column, but the user might not have specified |
363 | // it. |
364 | Declaration found_decl = inline_info->GetCallSite(); |
365 | uint32_t sought_column = sought_decl.GetColumn(); |
366 | if (found_decl.FileAndLineEqual(declaration: sought_decl, full: false) && |
367 | (sought_column == LLDB_INVALID_COLUMN_NUMBER || |
368 | sought_column == found_decl.GetColumn())) { |
369 | // If we found a call site, it belongs not in this inlined block, |
370 | // but in the parent block that inlined it. |
371 | Address parent_start_addr; |
372 | if (sibling_block->GetParent()->GetStartAddress( |
373 | addr&: parent_start_addr)) { |
374 | SymbolContext sc; |
375 | parent_start_addr.CalculateSymbolContext(sc: &sc, resolve_scope); |
376 | // Now swap out the line entry for the one we found. |
377 | LineEntry call_site_line = sc.line_entry; |
378 | call_site_line.line = found_decl.GetLine(); |
379 | call_site_line.column = found_decl.GetColumn(); |
380 | bool matches_spec = true; |
381 | // If the user asked for an exact match, we need to make sure the |
382 | // call site we found actually matches the location. |
383 | if (src_location_spec.GetExactMatch()) { |
384 | matches_spec = false; |
385 | if ((src_location_spec.GetFileSpec() == |
386 | sc.line_entry.GetFile()) && |
387 | (src_location_spec.GetLine() && |
388 | *src_location_spec.GetLine() == call_site_line.line) && |
389 | (src_location_spec.GetColumn() && |
390 | *src_location_spec.GetColumn() == call_site_line.column)) |
391 | matches_spec = true; |
392 | } |
393 | if (matches_spec && |
394 | sibling_block->GetRangeAtIndex(range_idx: 0, range&: call_site_line.range)) { |
395 | SymbolContext call_site_sc(sc.target_sp, sc.module_sp, |
396 | sc.comp_unit, sc.function, sc.block, |
397 | &call_site_line, sc.symbol); |
398 | sc_list.Append(sc: call_site_sc); |
399 | } |
400 | } |
401 | } |
402 | } |
403 | |
404 | // Descend into the child blocks: |
405 | examine_block(*sibling_block); |
406 | // Now go to the next sibling: |
407 | sibling_block = sibling_block->GetSibling(); |
408 | } |
409 | }; |
410 | |
411 | if (function) { |
412 | // We don't need to examine the function block, it can't be inlined. |
413 | Block &func_block = function->GetBlock(can_create: true); |
414 | examine_block(func_block); |
415 | } |
416 | // If we found entries here, we are done. We only get here because we |
417 | // didn't find an exact line entry for this line & column, but if we found |
418 | // an exact match from the call site info that's strictly better than |
419 | // continuing to look for matches further on in the file. |
420 | // FIXME: Should I also do this for "call site line exists between the |
421 | // given line number and the later line we found in the line table"? That's |
422 | // a closer approximation to our general sliding algorithm. |
423 | if (sc_list.GetSize() > old_sc_list_size) |
424 | return; |
425 | } |
426 | |
427 | // If "exact == true", then "found_line" will be the same as "line". If |
428 | // "exact == false", the "found_line" will be the closest line entry |
429 | // with a line number greater than "line" and we will use this for our |
430 | // subsequent line exact matches below. |
431 | const bool inlines = false; |
432 | const bool exact = true; |
433 | const std::optional<uint16_t> column = |
434 | src_location_spec.GetColumn() ? std::optional<uint16_t>(line_entry.column) |
435 | : std::nullopt; |
436 | |
437 | SourceLocationSpec found_entry(line_entry.GetFile(), line_entry.line, column, |
438 | inlines, exact); |
439 | |
440 | while (line_idx != UINT32_MAX) { |
441 | // If they only asked for the line entry, then we're done, we can |
442 | // just copy that over. But if they wanted more than just the line |
443 | // number, fill it in. |
444 | SymbolContext resolved_sc; |
445 | sc.line_entry = line_entry; |
446 | if (resolve_scope == eSymbolContextLineEntry) { |
447 | sc_list.Append(sc); |
448 | } else { |
449 | line_entry.range.GetBaseAddress().CalculateSymbolContext(sc: &resolved_sc, |
450 | resolve_scope); |
451 | // Sometimes debug info is bad and isn't able to resolve the line entry's |
452 | // address back to the same compile unit and/or line entry. If the compile |
453 | // unit changed, then revert back to just the compile unit and line entry. |
454 | // Prior to this fix, the above code might end up not being able to lookup |
455 | // the address, and then it would clear compile unit and the line entry in |
456 | // the symbol context and the breakpoint would fail to get set even though |
457 | // we have a valid line table entry in this compile unit. The address |
458 | // lookup can also end up finding another function in another compiler |
459 | // unit if the DWARF has overlappging address ranges. So if we end up with |
460 | // no compile unit or a different one after the above function call, |
461 | // revert back to the same results as if resolve_scope was set exactly to |
462 | // eSymbolContextLineEntry. |
463 | if (resolved_sc.comp_unit == this) { |
464 | sc_list.Append(sc: resolved_sc); |
465 | } else { |
466 | if (resolved_sc.comp_unit == nullptr && resolved_sc.module_sp) { |
467 | // Only report an error if we don't map back to any compile unit. With |
468 | // link time optimizations, the debug info might have many compile |
469 | // units that have the same address range due to function outlining |
470 | // or other link time optimizations. If the compile unit is NULL, then |
471 | // address resolving is completely failing and more deserving of an |
472 | // error message the user can see. |
473 | resolved_sc.module_sp->ReportError( |
474 | format: "unable to resolve a line table file address {0:x16} back " |
475 | "to a compile unit, please file a bug and attach the address " |
476 | "and file.", |
477 | args: line_entry.range.GetBaseAddress().GetFileAddress()); |
478 | } |
479 | sc_list.Append(sc); |
480 | } |
481 | } |
482 | |
483 | if (num_file_indexes == 1) |
484 | line_idx = line_table->FindLineEntryIndexByFileIndex( |
485 | start_idx: line_idx + 1, file_idx: file_indexes.front(), src_location_spec: found_entry, line_entry_ptr: &line_entry); |
486 | else |
487 | line_idx = line_table->FindLineEntryIndexByFileIndex( |
488 | start_idx: line_idx + 1, file_idx: file_indexes, src_location_spec: found_entry, line_entry_ptr: &line_entry); |
489 | } |
490 | } |
491 | |
492 | bool CompileUnit::GetIsOptimized() { |
493 | if (m_is_optimized == eLazyBoolCalculate) { |
494 | m_is_optimized = eLazyBoolNo; |
495 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { |
496 | if (symfile->ParseIsOptimized(comp_unit&: *this)) |
497 | m_is_optimized = eLazyBoolYes; |
498 | } |
499 | } |
500 | return m_is_optimized; |
501 | } |
502 | |
503 | void CompileUnit::SetVariableList(VariableListSP &variables) { |
504 | m_variables = variables; |
505 | } |
506 | |
507 | const std::vector<SourceModule> &CompileUnit::GetImportedModules() { |
508 | if (m_imported_modules.empty() && |
509 | m_flags.IsClear(bit: flagsParsedImportedModules)) { |
510 | m_flags.Set(flagsParsedImportedModules); |
511 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { |
512 | SymbolContext sc; |
513 | CalculateSymbolContext(sc: &sc); |
514 | symfile->ParseImportedModules(sc, imported_modules&: m_imported_modules); |
515 | } |
516 | } |
517 | return m_imported_modules; |
518 | } |
519 | |
520 | bool CompileUnit::ForEachExternalModule( |
521 | llvm::DenseSet<SymbolFile *> &visited_symbol_files, |
522 | llvm::function_ref<bool(Module &)> lambda) { |
523 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) |
524 | return symfile->ForEachExternalModule(comp_unit&: *this, visited_symbol_files, lambda); |
525 | return false; |
526 | } |
527 | |
528 | const SupportFileList &CompileUnit::GetSupportFiles() { |
529 | if (m_support_files.GetSize() == 0) { |
530 | if (m_flags.IsClear(bit: flagsParsedSupportFiles)) { |
531 | m_flags.Set(flagsParsedSupportFiles); |
532 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) |
533 | symfile->ParseSupportFiles(comp_unit&: *this, support_files&: m_support_files); |
534 | } |
535 | } |
536 | return m_support_files; |
537 | } |
538 | |
539 | void *CompileUnit::GetUserData() const { return m_user_data; } |
540 |
Definitions
- CompileUnit
- CompileUnit
- CalculateSymbolContext
- CalculateSymbolContextModule
- CalculateSymbolContextCompileUnit
- DumpSymbolContext
- GetDescription
- ForeachFunction
- FindFunction
- GetCachedLanguage
- Dump
- AddFunction
- FindFunctionByUID
- GetLanguage
- GetLineTable
- SetLineTable
- GetDebugMacros
- SetDebugMacros
- GetVariableList
- FindFileIndexes
- FindLineEntry
- ResolveSymbolContext
- GetIsOptimized
- SetVariableList
- GetImportedModules
- ForEachExternalModule
- GetSupportFiles
Learn to use CMake with our Intro Training
Find out more