1 //===-- CompileUnit.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 "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 16 using namespace lldb; 17 using namespace lldb_private; 18 19 CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, 20 const char *pathname, const lldb::user_id_t cu_sym_id, 21 lldb::LanguageType language, 22 lldb_private::LazyBool is_optimized) 23 : ModuleChild(module_sp), FileSpec(pathname), UserID(cu_sym_id), 24 m_user_data(user_data), m_language(language), m_flags(0), 25 m_support_files(), m_line_table_up(), m_variables(), 26 m_is_optimized(is_optimized) { 27 if (language != eLanguageTypeUnknown) 28 m_flags.Set(flagsParsedLanguage); 29 assert(module_sp); 30 } 31 32 CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, 33 const FileSpec &fspec, const lldb::user_id_t cu_sym_id, 34 lldb::LanguageType language, 35 lldb_private::LazyBool is_optimized) 36 : ModuleChild(module_sp), FileSpec(fspec), UserID(cu_sym_id), 37 m_user_data(user_data), m_language(language), m_flags(0), 38 m_support_files(), m_line_table_up(), m_variables(), 39 m_is_optimized(is_optimized) { 40 if (language != eLanguageTypeUnknown) 41 m_flags.Set(flagsParsedLanguage); 42 assert(module_sp); 43 } 44 45 CompileUnit::~CompileUnit() {} 46 47 void CompileUnit::CalculateSymbolContext(SymbolContext *sc) { 48 sc->comp_unit = this; 49 GetModule()->CalculateSymbolContext(sc); 50 } 51 52 ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); } 53 54 CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; } 55 56 void CompileUnit::DumpSymbolContext(Stream *s) { 57 GetModule()->DumpSymbolContext(s); 58 s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID()); 59 } 60 61 void CompileUnit::GetDescription(Stream *s, 62 lldb::DescriptionLevel level) const { 63 const char *language = Language::GetNameForLanguageType(m_language); 64 *s << "id = " << (const UserID &)*this << ", file = \"" 65 << (const FileSpec &)*this << "\", language = \"" << language << '"'; 66 } 67 68 void CompileUnit::ForeachFunction( 69 llvm::function_ref<bool(const FunctionSP &)> lambda) const { 70 std::vector<lldb::FunctionSP> sorted_functions; 71 sorted_functions.reserve(m_functions_by_uid.size()); 72 for (auto &p : m_functions_by_uid) 73 sorted_functions.push_back(p.second); 74 llvm::sort(sorted_functions.begin(), sorted_functions.end(), 75 [](const lldb::FunctionSP &a, const lldb::FunctionSP &b) { 76 return a->GetID() < b->GetID(); 77 }); 78 79 for (auto &f : sorted_functions) 80 if (lambda(f)) 81 return; 82 } 83 84 // Dump the current contents of this object. No functions that cause on demand 85 // parsing of functions, globals, statics are called, so this is a good 86 // function to call to get an idea of the current contents of the CompileUnit 87 // object. 88 void CompileUnit::Dump(Stream *s, bool show_context) const { 89 const char *language = Language::GetNameForLanguageType(m_language); 90 91 s->Printf("%p: ", static_cast<const void *>(this)); 92 s->Indent(); 93 *s << "CompileUnit" << static_cast<const UserID &>(*this) << ", language = \"" 94 << language << "\", file = '" << static_cast<const FileSpec &>(*this) 95 << "'\n"; 96 97 // m_types.Dump(s); 98 99 if (m_variables.get()) { 100 s->IndentMore(); 101 m_variables->Dump(s, show_context); 102 s->IndentLess(); 103 } 104 105 if (!m_functions_by_uid.empty()) { 106 s->IndentMore(); 107 ForeachFunction([&s, show_context](const FunctionSP &f) { 108 f->Dump(s, show_context); 109 return false; 110 }); 111 112 s->IndentLess(); 113 s->EOL(); 114 } 115 } 116 117 // Add a function to this compile unit 118 void CompileUnit::AddFunction(FunctionSP &funcSP) { 119 m_functions_by_uid[funcSP->GetID()] = funcSP; 120 } 121 122 FunctionSP CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid) { 123 auto it = m_functions_by_uid.find(func_uid); 124 if (it == m_functions_by_uid.end()) 125 return FunctionSP(); 126 return it->second; 127 } 128 129 lldb::LanguageType CompileUnit::GetLanguage() { 130 if (m_language == eLanguageTypeUnknown) { 131 if (m_flags.IsClear(flagsParsedLanguage)) { 132 m_flags.Set(flagsParsedLanguage); 133 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 134 m_language = symfile->ParseLanguage(*this); 135 } 136 } 137 return m_language; 138 } 139 140 LineTable *CompileUnit::GetLineTable() { 141 if (m_line_table_up == nullptr) { 142 if (m_flags.IsClear(flagsParsedLineTable)) { 143 m_flags.Set(flagsParsedLineTable); 144 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 145 symfile->ParseLineTable(*this); 146 } 147 } 148 return m_line_table_up.get(); 149 } 150 151 void CompileUnit::SetLineTable(LineTable *line_table) { 152 if (line_table == nullptr) 153 m_flags.Clear(flagsParsedLineTable); 154 else 155 m_flags.Set(flagsParsedLineTable); 156 m_line_table_up.reset(line_table); 157 } 158 159 DebugMacros *CompileUnit::GetDebugMacros() { 160 if (m_debug_macros_sp.get() == nullptr) { 161 if (m_flags.IsClear(flagsParsedDebugMacros)) { 162 m_flags.Set(flagsParsedDebugMacros); 163 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 164 symfile->ParseDebugMacros(*this); 165 } 166 } 167 168 return m_debug_macros_sp.get(); 169 } 170 171 void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) { 172 if (debug_macros_sp.get() == nullptr) 173 m_flags.Clear(flagsParsedDebugMacros); 174 else 175 m_flags.Set(flagsParsedDebugMacros); 176 m_debug_macros_sp = debug_macros_sp; 177 } 178 179 VariableListSP CompileUnit::GetVariableList(bool can_create) { 180 if (m_variables.get() == nullptr && can_create) { 181 SymbolContext sc; 182 CalculateSymbolContext(&sc); 183 assert(sc.module_sp); 184 sc.module_sp->GetSymbolFile()->ParseVariablesForContext(sc); 185 } 186 187 return m_variables; 188 } 189 190 uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line, 191 const FileSpec *file_spec_ptr, bool exact, 192 LineEntry *line_entry_ptr) { 193 uint32_t file_idx = 0; 194 195 if (file_spec_ptr) { 196 file_idx = GetSupportFiles().FindFileIndex(1, *file_spec_ptr, true); 197 if (file_idx == UINT32_MAX) 198 return UINT32_MAX; 199 } else { 200 // All the line table entries actually point to the version of the Compile 201 // Unit that is in the support files (the one at 0 was artificially added.) 202 // So prefer the one further on in the support files if it exists... 203 const FileSpecList &support_files = GetSupportFiles(); 204 const bool full = true; 205 file_idx = support_files.FindFileIndex( 206 1, support_files.GetFileSpecAtIndex(0), full); 207 if (file_idx == UINT32_MAX) 208 file_idx = 0; 209 } 210 LineTable *line_table = GetLineTable(); 211 if (line_table) 212 return line_table->FindLineEntryIndexByFileIndex(start_idx, file_idx, line, 213 exact, line_entry_ptr); 214 return UINT32_MAX; 215 } 216 217 uint32_t CompileUnit::ResolveSymbolContext(const FileSpec &file_spec, 218 uint32_t line, bool check_inlines, 219 bool exact, 220 SymbolContextItem resolve_scope, 221 SymbolContextList &sc_list) { 222 // First find all of the file indexes that match our "file_spec". If 223 // "file_spec" has an empty directory, then only compare the basenames when 224 // finding file indexes 225 std::vector<uint32_t> file_indexes; 226 const bool full_match = (bool)file_spec.GetDirectory(); 227 bool file_spec_matches_cu_file_spec = 228 FileSpec::Equal(file_spec, *this, full_match); 229 230 // If we are not looking for inlined functions and our file spec doesn't 231 // match then we are done... 232 if (!file_spec_matches_cu_file_spec && !check_inlines) 233 return 0; 234 235 uint32_t file_idx = 236 GetSupportFiles().FindFileIndex(1, file_spec, true); 237 while (file_idx != UINT32_MAX) { 238 file_indexes.push_back(file_idx); 239 file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true); 240 } 241 242 const size_t num_file_indexes = file_indexes.size(); 243 if (num_file_indexes == 0) 244 return 0; 245 246 const uint32_t prev_size = sc_list.GetSize(); 247 248 SymbolContext sc(GetModule()); 249 sc.comp_unit = this; 250 251 if (line != 0) { 252 LineTable *line_table = sc.comp_unit->GetLineTable(); 253 254 if (line_table != nullptr) { 255 uint32_t found_line; 256 uint32_t line_idx; 257 258 if (num_file_indexes == 1) { 259 // We only have a single support file that matches, so use the line 260 // table function that searches for a line entries that match a single 261 // support file index 262 LineEntry line_entry; 263 line_idx = line_table->FindLineEntryIndexByFileIndex( 264 0, file_indexes.front(), line, exact, &line_entry); 265 266 // If "exact == true", then "found_line" will be the same as "line". If 267 // "exact == false", the "found_line" will be the closest line entry 268 // with a line number greater than "line" and we will use this for our 269 // subsequent line exact matches below. 270 found_line = line_entry.line; 271 272 while (line_idx != UINT32_MAX) { 273 // If they only asked for the line entry, then we're done, we can 274 // just copy that over. But if they wanted more than just the line 275 // number, fill it in. 276 if (resolve_scope == eSymbolContextLineEntry) { 277 sc.line_entry = line_entry; 278 } else { 279 line_entry.range.GetBaseAddress().CalculateSymbolContext( 280 &sc, resolve_scope); 281 } 282 283 sc_list.Append(sc); 284 line_idx = line_table->FindLineEntryIndexByFileIndex( 285 line_idx + 1, file_indexes.front(), found_line, true, 286 &line_entry); 287 } 288 } else { 289 // We found multiple support files that match "file_spec" so use the 290 // line table function that searches for a line entries that match a 291 // multiple support file indexes. 292 LineEntry line_entry; 293 line_idx = line_table->FindLineEntryIndexByFileIndex( 294 0, file_indexes, line, exact, &line_entry); 295 296 // If "exact == true", then "found_line" will be the same as "line". If 297 // "exact == false", the "found_line" will be the closest line entry 298 // with a line number greater than "line" and we will use this for our 299 // subsequent line exact matches below. 300 found_line = line_entry.line; 301 302 while (line_idx != UINT32_MAX) { 303 if (resolve_scope == eSymbolContextLineEntry) { 304 sc.line_entry = line_entry; 305 } else { 306 line_entry.range.GetBaseAddress().CalculateSymbolContext( 307 &sc, resolve_scope); 308 } 309 310 sc_list.Append(sc); 311 line_idx = line_table->FindLineEntryIndexByFileIndex( 312 line_idx + 1, file_indexes, found_line, true, &line_entry); 313 } 314 } 315 } 316 } else if (file_spec_matches_cu_file_spec && !check_inlines) { 317 // only append the context if we aren't looking for inline call sites by 318 // file and line and if the file spec matches that of the compile unit 319 sc_list.Append(sc); 320 } 321 return sc_list.GetSize() - prev_size; 322 } 323 324 bool CompileUnit::GetIsOptimized() { 325 if (m_is_optimized == eLazyBoolCalculate) { 326 m_is_optimized = eLazyBoolNo; 327 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { 328 if (symfile->ParseIsOptimized(*this)) 329 m_is_optimized = eLazyBoolYes; 330 } 331 } 332 return m_is_optimized; 333 } 334 335 void CompileUnit::SetVariableList(VariableListSP &variables) { 336 m_variables = variables; 337 } 338 339 const std::vector<SourceModule> &CompileUnit::GetImportedModules() { 340 if (m_imported_modules.empty() && 341 m_flags.IsClear(flagsParsedImportedModules)) { 342 m_flags.Set(flagsParsedImportedModules); 343 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { 344 SymbolContext sc; 345 CalculateSymbolContext(&sc); 346 symfile->ParseImportedModules(sc, m_imported_modules); 347 } 348 } 349 return m_imported_modules; 350 } 351 352 const FileSpecList &CompileUnit::GetSupportFiles() { 353 if (m_support_files.GetSize() == 0) { 354 if (m_flags.IsClear(flagsParsedSupportFiles)) { 355 m_flags.Set(flagsParsedSupportFiles); 356 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 357 symfile->ParseSupportFiles(*this, m_support_files); 358 } 359 } 360 return m_support_files; 361 } 362 363 void *CompileUnit::GetUserData() const { return m_user_data; } 364