1 //===-- CompileUnit.cpp ---------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
19 using namespace lldb_private
;
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
, FileSpec(pathname
), cu_sym_id
, language
,
28 CompileUnit::CompileUnit(const lldb::ModuleSP
&module_sp
, void *user_data
,
29 const FileSpec
&fspec
, const lldb::user_id_t cu_sym_id
,
30 lldb::LanguageType language
,
31 lldb_private::LazyBool is_optimized
)
32 : ModuleChild(module_sp
), UserID(cu_sym_id
), m_user_data(user_data
),
33 m_language(language
), m_flags(0), m_file_spec(fspec
),
34 m_is_optimized(is_optimized
) {
35 if (language
!= eLanguageTypeUnknown
)
36 m_flags
.Set(flagsParsedLanguage
);
40 void CompileUnit::CalculateSymbolContext(SymbolContext
*sc
) {
42 GetModule()->CalculateSymbolContext(sc
);
45 ModuleSP
CompileUnit::CalculateSymbolContextModule() { return GetModule(); }
47 CompileUnit
*CompileUnit::CalculateSymbolContextCompileUnit() { return this; }
49 void CompileUnit::DumpSymbolContext(Stream
*s
) {
50 GetModule()->DumpSymbolContext(s
);
51 s
->Printf(", CompileUnit{0x%8.8" PRIx64
"}", GetID());
54 void CompileUnit::GetDescription(Stream
*s
,
55 lldb::DescriptionLevel level
) const {
56 const char *language
= GetCachedLanguage();
57 *s
<< "id = " << (const UserID
&)*this << ", file = \""
58 << this->GetPrimaryFile() << "\", language = \"" << language
<< '"';
61 void CompileUnit::ForeachFunction(
62 llvm::function_ref
<bool(const FunctionSP
&)> lambda
) const {
63 std::vector
<lldb::FunctionSP
> sorted_functions
;
64 sorted_functions
.reserve(m_functions_by_uid
.size());
65 for (auto &p
: m_functions_by_uid
)
66 sorted_functions
.push_back(p
.second
);
67 llvm::sort(sorted_functions
,
68 [](const lldb::FunctionSP
&a
, const lldb::FunctionSP
&b
) {
69 return a
->GetID() < b
->GetID();
72 for (auto &f
: sorted_functions
)
77 lldb::FunctionSP
CompileUnit::FindFunction(
78 llvm::function_ref
<bool(const FunctionSP
&)> matching_lambda
) {
81 lldb::ModuleSP module
= CalculateSymbolContextModule();
86 SymbolFile
*symbol_file
= module
->GetSymbolFile();
91 // m_functions_by_uid is filled in lazily but we need all the entries.
92 symbol_file
->ParseFunctions(*this);
94 for (auto &p
: m_functions_by_uid
) {
95 if (matching_lambda(p
.second
))
101 const char *CompileUnit::GetCachedLanguage() const {
102 if (m_flags
.IsClear(flagsParsedLanguage
))
103 return "<not loaded>";
104 return Language::GetNameForLanguageType(m_language
);
107 // Dump the current contents of this object. No functions that cause on demand
108 // parsing of functions, globals, statics are called, so this is a good
109 // function to call to get an idea of the current contents of the CompileUnit
111 void CompileUnit::Dump(Stream
*s
, bool show_context
) const {
112 const char *language
= GetCachedLanguage();
114 s
->Printf("%p: ", static_cast<const void *>(this));
116 *s
<< "CompileUnit" << static_cast<const UserID
&>(*this) << ", language = \""
117 << language
<< "\", file = '" << GetPrimaryFile() << "'\n";
121 if (m_variables
.get()) {
123 m_variables
->Dump(s
, show_context
);
127 if (!m_functions_by_uid
.empty()) {
129 ForeachFunction([&s
, show_context
](const FunctionSP
&f
) {
130 f
->Dump(s
, show_context
);
139 // Add a function to this compile unit
140 void CompileUnit::AddFunction(FunctionSP
&funcSP
) {
141 m_functions_by_uid
[funcSP
->GetID()] = funcSP
;
144 FunctionSP
CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid
) {
145 auto it
= m_functions_by_uid
.find(func_uid
);
146 if (it
== m_functions_by_uid
.end())
151 lldb::LanguageType
CompileUnit::GetLanguage() {
152 if (m_language
== eLanguageTypeUnknown
) {
153 if (m_flags
.IsClear(flagsParsedLanguage
)) {
154 m_flags
.Set(flagsParsedLanguage
);
155 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile())
156 m_language
= symfile
->ParseLanguage(*this);
162 LineTable
*CompileUnit::GetLineTable() {
163 if (m_line_table_up
== nullptr) {
164 if (m_flags
.IsClear(flagsParsedLineTable
)) {
165 m_flags
.Set(flagsParsedLineTable
);
166 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile())
167 symfile
->ParseLineTable(*this);
170 return m_line_table_up
.get();
173 void CompileUnit::SetLineTable(LineTable
*line_table
) {
174 if (line_table
== nullptr)
175 m_flags
.Clear(flagsParsedLineTable
);
177 m_flags
.Set(flagsParsedLineTable
);
178 m_line_table_up
.reset(line_table
);
181 void CompileUnit::SetSupportFiles(FileSpecList support_files
) {
182 m_support_files
= std::move(support_files
);
185 DebugMacros
*CompileUnit::GetDebugMacros() {
186 if (m_debug_macros_sp
.get() == nullptr) {
187 if (m_flags
.IsClear(flagsParsedDebugMacros
)) {
188 m_flags
.Set(flagsParsedDebugMacros
);
189 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile())
190 symfile
->ParseDebugMacros(*this);
194 return m_debug_macros_sp
.get();
197 void CompileUnit::SetDebugMacros(const DebugMacrosSP
&debug_macros_sp
) {
198 if (debug_macros_sp
.get() == nullptr)
199 m_flags
.Clear(flagsParsedDebugMacros
);
201 m_flags
.Set(flagsParsedDebugMacros
);
202 m_debug_macros_sp
= debug_macros_sp
;
205 VariableListSP
CompileUnit::GetVariableList(bool can_create
) {
206 if (m_variables
.get() == nullptr && can_create
) {
208 CalculateSymbolContext(&sc
);
209 assert(sc
.module_sp
);
210 sc
.module_sp
->GetSymbolFile()->ParseVariablesForContext(sc
);
216 std::vector
<uint32_t> FindFileIndexes(const FileSpecList
&files
,
217 const FileSpec
&file
) {
218 std::vector
<uint32_t> result
;
220 while ((idx
= files
.FindCompatibleIndex(idx
+ 1, file
)) !=
222 result
.push_back(idx
);
226 uint32_t CompileUnit::FindLineEntry(uint32_t start_idx
, uint32_t line
,
227 const FileSpec
*file_spec_ptr
, bool exact
,
228 LineEntry
*line_entry_ptr
) {
230 file_spec_ptr
= &GetPrimaryFile();
231 std::vector
<uint32_t> file_indexes
= FindFileIndexes(GetSupportFiles(),
233 if (file_indexes
.empty())
236 // TODO: Handle SourceLocationSpec column information
237 SourceLocationSpec
location_spec(*file_spec_ptr
, line
,
238 /*column=*/std::nullopt
,
239 /*check_inlines=*/false, exact
);
241 LineTable
*line_table
= GetLineTable();
243 return line_table
->FindLineEntryIndexByFileIndex(
244 start_idx
, file_indexes
, location_spec
, line_entry_ptr
);
248 void CompileUnit::ResolveSymbolContext(
249 const SourceLocationSpec
&src_location_spec
,
250 SymbolContextItem resolve_scope
, SymbolContextList
&sc_list
) {
251 const FileSpec file_spec
= src_location_spec
.GetFileSpec();
252 const uint32_t line
= src_location_spec
.GetLine().value_or(0);
253 const bool check_inlines
= src_location_spec
.GetCheckInlines();
255 // First find all of the file indexes that match our "file_spec". If
256 // "file_spec" has an empty directory, then only compare the basenames when
257 // finding file indexes
258 bool file_spec_matches_cu_file_spec
=
259 FileSpec::Match(file_spec
, this->GetPrimaryFile());
261 // If we are not looking for inlined functions and our file spec doesn't
262 // match then we are done...
263 if (!file_spec_matches_cu_file_spec
&& !check_inlines
)
266 SymbolContext
sc(GetModule());
270 if (file_spec_matches_cu_file_spec
&& !check_inlines
) {
271 // only append the context if we aren't looking for inline call sites by
272 // file and line and if the file spec matches that of the compile unit
278 std::vector
<uint32_t> file_indexes
= FindFileIndexes(GetSupportFiles(),
280 const size_t num_file_indexes
= file_indexes
.size();
281 if (num_file_indexes
== 0)
284 // Found a matching source file in this compile unit load its debug info.
285 GetModule()->GetSymbolFile()->SetLoadDebugInfoEnabled();
287 LineTable
*line_table
= sc
.comp_unit
->GetLineTable();
289 if (line_table
== nullptr) {
290 if (file_spec_matches_cu_file_spec
&& !check_inlines
) {
297 LineEntry line_entry
;
299 if (num_file_indexes
== 1) {
300 // We only have a single support file that matches, so use the line
301 // table function that searches for a line entries that match a single
302 // support file index
303 line_idx
= line_table
->FindLineEntryIndexByFileIndex(
304 0, file_indexes
.front(), src_location_spec
, &line_entry
);
306 // We found multiple support files that match "file_spec" so use the
307 // line table function that searches for a line entries that match a
308 // multiple support file indexes.
309 line_idx
= line_table
->FindLineEntryIndexByFileIndex(
310 0, file_indexes
, src_location_spec
, &line_entry
);
313 // If "exact == true", then "found_line" will be the same as "line". If
314 // "exact == false", the "found_line" will be the closest line entry
315 // with a line number greater than "line" and we will use this for our
316 // subsequent line exact matches below.
317 const bool inlines
= false;
318 const bool exact
= true;
319 const std::optional
<uint16_t> column
=
320 src_location_spec
.GetColumn() ? std::optional
<uint16_t>(line_entry
.column
)
323 SourceLocationSpec
found_entry(line_entry
.file
, line_entry
.line
, column
,
326 while (line_idx
!= UINT32_MAX
) {
327 // If they only asked for the line entry, then we're done, we can
328 // just copy that over. But if they wanted more than just the line
329 // number, fill it in.
330 SymbolContext resolved_sc
;
331 sc
.line_entry
= line_entry
;
332 if (resolve_scope
== eSymbolContextLineEntry
) {
335 line_entry
.range
.GetBaseAddress().CalculateSymbolContext(&resolved_sc
,
337 // Sometimes debug info is bad and isn't able to resolve the line entry's
338 // address back to the same compile unit and/or line entry. If the compile
339 // unit changed, then revert back to just the compile unit and line entry.
340 // Prior to this fix, the above code might end up not being able to lookup
341 // the address, and then it would clear compile unit and the line entry in
342 // the symbol context and the breakpoint would fail to get set even though
343 // we have a valid line table entry in this compile unit. The address
344 // lookup can also end up finding another function in another compiler
345 // unit if the DWARF has overlappging address ranges. So if we end up with
346 // no compile unit or a different one after the above function call,
347 // revert back to the same results as if resolve_scope was set exactly to
348 // eSymbolContextLineEntry.
349 if (resolved_sc
.comp_unit
== this) {
350 sc_list
.Append(resolved_sc
);
352 if (resolved_sc
.comp_unit
== nullptr && resolved_sc
.module_sp
) {
353 // Only report an error if we don't map back to any compile unit. With
354 // link time optimizations, the debug info might have many compile
355 // units that have the same address range due to function outlining
356 // or other link time optimizations. If the compile unit is NULL, then
357 // address resolving is completely failing and more deserving of an
358 // error message the user can see.
359 resolved_sc
.module_sp
->ReportError(
360 "unable to resolve a line table file address {0:x16} back "
361 "to a compile unit, please file a bug and attach the address "
363 line_entry
.range
.GetBaseAddress().GetFileAddress());
369 if (num_file_indexes
== 1)
370 line_idx
= line_table
->FindLineEntryIndexByFileIndex(
371 line_idx
+ 1, file_indexes
.front(), found_entry
, &line_entry
);
373 line_idx
= line_table
->FindLineEntryIndexByFileIndex(
374 line_idx
+ 1, file_indexes
, found_entry
, &line_entry
);
378 bool CompileUnit::GetIsOptimized() {
379 if (m_is_optimized
== eLazyBoolCalculate
) {
380 m_is_optimized
= eLazyBoolNo
;
381 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile()) {
382 if (symfile
->ParseIsOptimized(*this))
383 m_is_optimized
= eLazyBoolYes
;
386 return m_is_optimized
;
389 void CompileUnit::SetVariableList(VariableListSP
&variables
) {
390 m_variables
= variables
;
393 const std::vector
<SourceModule
> &CompileUnit::GetImportedModules() {
394 if (m_imported_modules
.empty() &&
395 m_flags
.IsClear(flagsParsedImportedModules
)) {
396 m_flags
.Set(flagsParsedImportedModules
);
397 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile()) {
399 CalculateSymbolContext(&sc
);
400 symfile
->ParseImportedModules(sc
, m_imported_modules
);
403 return m_imported_modules
;
406 bool CompileUnit::ForEachExternalModule(
407 llvm::DenseSet
<SymbolFile
*> &visited_symbol_files
,
408 llvm::function_ref
<bool(Module
&)> lambda
) {
409 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile())
410 return symfile
->ForEachExternalModule(*this, visited_symbol_files
, lambda
);
414 const FileSpecList
&CompileUnit::GetSupportFiles() {
415 if (m_support_files
.GetSize() == 0) {
416 if (m_flags
.IsClear(flagsParsedSupportFiles
)) {
417 m_flags
.Set(flagsParsedSupportFiles
);
418 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile())
419 symfile
->ParseSupportFiles(*this, m_support_files
);
422 return m_support_files
;
425 void *CompileUnit::GetUserData() const { return m_user_data
; }