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"
18 using namespace lldb_private
;
20 CompileUnit::CompileUnit(const lldb::ModuleSP
&module_sp
, void *user_data
,
21 const char *pathname
, const lldb::user_id_t cu_sym_id
,
22 lldb::LanguageType language
,
23 lldb_private::LazyBool is_optimized
)
24 : CompileUnit(module_sp
, user_data
, FileSpec(pathname
), cu_sym_id
, language
,
27 CompileUnit::CompileUnit(const lldb::ModuleSP
&module_sp
, void *user_data
,
28 const FileSpec
&fspec
, const lldb::user_id_t cu_sym_id
,
29 lldb::LanguageType language
,
30 lldb_private::LazyBool is_optimized
)
31 : ModuleChild(module_sp
), UserID(cu_sym_id
), m_user_data(user_data
),
32 m_language(language
), m_flags(0), m_file_spec(fspec
),
33 m_is_optimized(is_optimized
) {
34 if (language
!= eLanguageTypeUnknown
)
35 m_flags
.Set(flagsParsedLanguage
);
39 void CompileUnit::CalculateSymbolContext(SymbolContext
*sc
) {
41 GetModule()->CalculateSymbolContext(sc
);
44 ModuleSP
CompileUnit::CalculateSymbolContextModule() { return GetModule(); }
46 CompileUnit
*CompileUnit::CalculateSymbolContextCompileUnit() { return this; }
48 void CompileUnit::DumpSymbolContext(Stream
*s
) {
49 GetModule()->DumpSymbolContext(s
);
50 s
->Printf(", CompileUnit{0x%8.8" PRIx64
"}", GetID());
53 void CompileUnit::GetDescription(Stream
*s
,
54 lldb::DescriptionLevel level
) const {
55 const char *language
= GetCachedLanguage();
56 *s
<< "id = " << (const UserID
&)*this << ", file = \""
57 << this->GetPrimaryFile() << "\", language = \"" << language
<< '"';
60 void CompileUnit::ForeachFunction(
61 llvm::function_ref
<bool(const FunctionSP
&)> lambda
) const {
62 std::vector
<lldb::FunctionSP
> sorted_functions
;
63 sorted_functions
.reserve(m_functions_by_uid
.size());
64 for (auto &p
: m_functions_by_uid
)
65 sorted_functions
.push_back(p
.second
);
66 llvm::sort(sorted_functions
.begin(), sorted_functions
.end(),
67 [](const lldb::FunctionSP
&a
, const lldb::FunctionSP
&b
) {
68 return a
->GetID() < b
->GetID();
71 for (auto &f
: sorted_functions
)
76 lldb::FunctionSP
CompileUnit::FindFunction(
77 llvm::function_ref
<bool(const FunctionSP
&)> matching_lambda
) {
80 lldb::ModuleSP module
= CalculateSymbolContextModule();
85 SymbolFile
*symbol_file
= module
->GetSymbolFile();
90 // m_functions_by_uid is filled in lazily but we need all the entries.
91 symbol_file
->ParseFunctions(*this);
93 for (auto &p
: m_functions_by_uid
) {
94 if (matching_lambda(p
.second
))
100 const char *CompileUnit::GetCachedLanguage() const {
101 if (m_flags
.IsClear(flagsParsedLanguage
))
102 return "<not loaded>";
103 return Language::GetNameForLanguageType(m_language
);
106 // Dump the current contents of this object. No functions that cause on demand
107 // parsing of functions, globals, statics are called, so this is a good
108 // function to call to get an idea of the current contents of the CompileUnit
110 void CompileUnit::Dump(Stream
*s
, bool show_context
) const {
111 const char *language
= GetCachedLanguage();
113 s
->Printf("%p: ", static_cast<const void *>(this));
115 *s
<< "CompileUnit" << static_cast<const UserID
&>(*this) << ", language = \""
116 << language
<< "\", file = '" << GetPrimaryFile() << "'\n";
120 if (m_variables
.get()) {
122 m_variables
->Dump(s
, show_context
);
126 if (!m_functions_by_uid
.empty()) {
128 ForeachFunction([&s
, show_context
](const FunctionSP
&f
) {
129 f
->Dump(s
, show_context
);
138 // Add a function to this compile unit
139 void CompileUnit::AddFunction(FunctionSP
&funcSP
) {
140 m_functions_by_uid
[funcSP
->GetID()] = funcSP
;
143 FunctionSP
CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid
) {
144 auto it
= m_functions_by_uid
.find(func_uid
);
145 if (it
== m_functions_by_uid
.end())
150 lldb::LanguageType
CompileUnit::GetLanguage() {
151 if (m_language
== eLanguageTypeUnknown
) {
152 if (m_flags
.IsClear(flagsParsedLanguage
)) {
153 m_flags
.Set(flagsParsedLanguage
);
154 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile())
155 m_language
= symfile
->ParseLanguage(*this);
161 LineTable
*CompileUnit::GetLineTable() {
162 if (m_line_table_up
== nullptr) {
163 if (m_flags
.IsClear(flagsParsedLineTable
)) {
164 m_flags
.Set(flagsParsedLineTable
);
165 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile())
166 symfile
->ParseLineTable(*this);
169 return m_line_table_up
.get();
172 void CompileUnit::SetLineTable(LineTable
*line_table
) {
173 if (line_table
== nullptr)
174 m_flags
.Clear(flagsParsedLineTable
);
176 m_flags
.Set(flagsParsedLineTable
);
177 m_line_table_up
.reset(line_table
);
180 void CompileUnit::SetSupportFiles(const FileSpecList
&support_files
) {
181 m_support_files
= support_files
;
184 void CompileUnit::SetSupportFiles(FileSpecList
&&support_files
) {
185 m_support_files
= std::move(support_files
);
188 DebugMacros
*CompileUnit::GetDebugMacros() {
189 if (m_debug_macros_sp
.get() == nullptr) {
190 if (m_flags
.IsClear(flagsParsedDebugMacros
)) {
191 m_flags
.Set(flagsParsedDebugMacros
);
192 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile())
193 symfile
->ParseDebugMacros(*this);
197 return m_debug_macros_sp
.get();
200 void CompileUnit::SetDebugMacros(const DebugMacrosSP
&debug_macros_sp
) {
201 if (debug_macros_sp
.get() == nullptr)
202 m_flags
.Clear(flagsParsedDebugMacros
);
204 m_flags
.Set(flagsParsedDebugMacros
);
205 m_debug_macros_sp
= debug_macros_sp
;
208 VariableListSP
CompileUnit::GetVariableList(bool can_create
) {
209 if (m_variables
.get() == nullptr && can_create
) {
211 CalculateSymbolContext(&sc
);
212 assert(sc
.module_sp
);
213 sc
.module_sp
->GetSymbolFile()->ParseVariablesForContext(sc
);
219 std::vector
<uint32_t> FindFileIndexes(const FileSpecList
&files
, const FileSpec
&file
) {
220 std::vector
<uint32_t> result
;
222 while ((idx
= files
.FindFileIndex(idx
+ 1, file
, /*full=*/true)) !=
224 result
.push_back(idx
);
228 uint32_t CompileUnit::FindLineEntry(uint32_t start_idx
, uint32_t line
,
229 const FileSpec
*file_spec_ptr
, bool exact
,
230 LineEntry
*line_entry_ptr
) {
232 file_spec_ptr
= &GetPrimaryFile();
233 std::vector
<uint32_t> file_indexes
= FindFileIndexes(GetSupportFiles(), *file_spec_ptr
);
234 if (file_indexes
.empty())
237 // TODO: Handle SourceLocationSpec column information
238 SourceLocationSpec
location_spec(*file_spec_ptr
, line
, /*column=*/llvm::None
,
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().getValueOr(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 std::vector
<uint32_t> file_indexes
;
259 bool file_spec_matches_cu_file_spec
=
260 FileSpec::Match(file_spec
, this->GetPrimaryFile());
262 // If we are not looking for inlined functions and our file spec doesn't
263 // match then we are done...
264 if (!file_spec_matches_cu_file_spec
&& !check_inlines
)
267 SymbolContext
sc(GetModule());
271 if (file_spec_matches_cu_file_spec
&& !check_inlines
) {
272 // only append the context if we aren't looking for inline call sites by
273 // file and line and if the file spec matches that of the compile unit
280 GetSupportFiles().FindFileIndex(0, file_spec
, true);
281 while (file_idx
!= UINT32_MAX
) {
282 file_indexes
.push_back(file_idx
);
283 file_idx
= GetSupportFiles().FindFileIndex(file_idx
+ 1, file_spec
, true);
286 const size_t num_file_indexes
= file_indexes
.size();
287 if (num_file_indexes
== 0)
290 LineTable
*line_table
= sc
.comp_unit
->GetLineTable();
292 if (line_table
== nullptr) {
293 if (file_spec_matches_cu_file_spec
&& !check_inlines
) {
300 LineEntry line_entry
;
302 if (num_file_indexes
== 1) {
303 // We only have a single support file that matches, so use the line
304 // table function that searches for a line entries that match a single
305 // support file index
306 line_idx
= line_table
->FindLineEntryIndexByFileIndex(
307 0, file_indexes
.front(), src_location_spec
, &line_entry
);
309 // We found multiple support files that match "file_spec" so use the
310 // line table function that searches for a line entries that match a
311 // multiple support file indexes.
312 line_idx
= line_table
->FindLineEntryIndexByFileIndex(
313 0, file_indexes
, src_location_spec
, &line_entry
);
316 // If "exact == true", then "found_line" will be the same as "line". If
317 // "exact == false", the "found_line" will be the closest line entry
318 // with a line number greater than "line" and we will use this for our
319 // subsequent line exact matches below.
320 const bool inlines
= false;
321 const bool exact
= true;
322 const llvm::Optional
<uint16_t> column
=
323 src_location_spec
.GetColumn().hasValue()
324 ? llvm::Optional
<uint16_t>(line_entry
.column
)
327 SourceLocationSpec
found_entry(line_entry
.file
, line_entry
.line
, column
,
330 while (line_idx
!= UINT32_MAX
) {
331 // If they only asked for the line entry, then we're done, we can
332 // just copy that over. But if they wanted more than just the line
333 // number, fill it in.
334 if (resolve_scope
== eSymbolContextLineEntry
) {
335 sc
.line_entry
= line_entry
;
337 line_entry
.range
.GetBaseAddress().CalculateSymbolContext(&sc
,
342 if (num_file_indexes
== 1)
343 line_idx
= line_table
->FindLineEntryIndexByFileIndex(
344 line_idx
+ 1, file_indexes
.front(), found_entry
, &line_entry
);
346 line_idx
= line_table
->FindLineEntryIndexByFileIndex(
347 line_idx
+ 1, file_indexes
, found_entry
, &line_entry
);
351 bool CompileUnit::GetIsOptimized() {
352 if (m_is_optimized
== eLazyBoolCalculate
) {
353 m_is_optimized
= eLazyBoolNo
;
354 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile()) {
355 if (symfile
->ParseIsOptimized(*this))
356 m_is_optimized
= eLazyBoolYes
;
359 return m_is_optimized
;
362 void CompileUnit::SetVariableList(VariableListSP
&variables
) {
363 m_variables
= variables
;
366 const std::vector
<SourceModule
> &CompileUnit::GetImportedModules() {
367 if (m_imported_modules
.empty() &&
368 m_flags
.IsClear(flagsParsedImportedModules
)) {
369 m_flags
.Set(flagsParsedImportedModules
);
370 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile()) {
372 CalculateSymbolContext(&sc
);
373 symfile
->ParseImportedModules(sc
, m_imported_modules
);
376 return m_imported_modules
;
379 bool CompileUnit::ForEachExternalModule(
380 llvm::DenseSet
<SymbolFile
*> &visited_symbol_files
,
381 llvm::function_ref
<bool(Module
&)> lambda
) {
382 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile())
383 return symfile
->ForEachExternalModule(*this, visited_symbol_files
, lambda
);
387 const FileSpecList
&CompileUnit::GetSupportFiles() {
388 if (m_support_files
.GetSize() == 0) {
389 if (m_flags
.IsClear(flagsParsedSupportFiles
)) {
390 m_flags
.Set(flagsParsedSupportFiles
);
391 if (SymbolFile
*symfile
= GetModule()->GetSymbolFile())
392 symfile
->ParseSupportFiles(*this, m_support_files
);
395 return m_support_files
;
398 void *CompileUnit::GetUserData() const { return m_user_data
; }