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, FileSpec(pathname), cu_sym_id, language, 26 is_optimized) {} 27 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 SupportFileList &&support_files) 33 : ModuleChild(module_sp), UserID(cu_sym_id), m_user_data(user_data), 34 m_language(language), m_flags(0), m_file_spec(fspec), 35 m_support_files(std::move(support_files)), m_is_optimized(is_optimized) { 36 if (language != eLanguageTypeUnknown) 37 m_flags.Set(flagsParsedLanguage); 38 assert(module_sp); 39 } 40 41 void CompileUnit::CalculateSymbolContext(SymbolContext *sc) { 42 sc->comp_unit = this; 43 GetModule()->CalculateSymbolContext(sc); 44 } 45 46 ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); } 47 48 CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; } 49 50 void CompileUnit::DumpSymbolContext(Stream *s) { 51 GetModule()->DumpSymbolContext(s); 52 s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID()); 53 } 54 55 void CompileUnit::GetDescription(Stream *s, 56 lldb::DescriptionLevel level) const { 57 const char *language = GetCachedLanguage(); 58 *s << "id = " << (const UserID &)*this << ", file = \"" 59 << this->GetPrimaryFile() << "\", language = \"" << language << '"'; 60 } 61 62 void CompileUnit::ForeachFunction( 63 llvm::function_ref<bool(const FunctionSP &)> lambda) const { 64 std::vector<lldb::FunctionSP> sorted_functions; 65 sorted_functions.reserve(m_functions_by_uid.size()); 66 for (auto &p : m_functions_by_uid) 67 sorted_functions.push_back(p.second); 68 llvm::sort(sorted_functions, 69 [](const lldb::FunctionSP &a, const lldb::FunctionSP &b) { 70 return a->GetID() < b->GetID(); 71 }); 72 73 for (auto &f : sorted_functions) 74 if (lambda(f)) 75 return; 76 } 77 78 lldb::FunctionSP CompileUnit::FindFunction( 79 llvm::function_ref<bool(const FunctionSP &)> matching_lambda) { 80 LLDB_SCOPED_TIMER(); 81 82 lldb::ModuleSP module = CalculateSymbolContextModule(); 83 84 if (!module) 85 return {}; 86 87 SymbolFile *symbol_file = module->GetSymbolFile(); 88 89 if (!symbol_file) 90 return {}; 91 92 // m_functions_by_uid is filled in lazily but we need all the entries. 93 symbol_file->ParseFunctions(*this); 94 95 for (auto &p : m_functions_by_uid) { 96 if (matching_lambda(p.second)) 97 return p.second; 98 } 99 return {}; 100 } 101 102 const char *CompileUnit::GetCachedLanguage() const { 103 if (m_flags.IsClear(flagsParsedLanguage)) 104 return "<not loaded>"; 105 return Language::GetNameForLanguageType(m_language); 106 } 107 108 // Dump the current contents of this object. No functions that cause on demand 109 // parsing of functions, globals, statics are called, so this is a good 110 // function to call to get an idea of the current contents of the CompileUnit 111 // object. 112 void CompileUnit::Dump(Stream *s, bool show_context) const { 113 const char *language = GetCachedLanguage(); 114 115 s->Printf("%p: ", static_cast<const void *>(this)); 116 s->Indent(); 117 *s << "CompileUnit" << static_cast<const UserID &>(*this) << ", language = \"" 118 << language << "\", file = '" << GetPrimaryFile() << "'\n"; 119 120 // m_types.Dump(s); 121 122 if (m_variables.get()) { 123 s->IndentMore(); 124 m_variables->Dump(s, show_context); 125 s->IndentLess(); 126 } 127 128 if (!m_functions_by_uid.empty()) { 129 s->IndentMore(); 130 ForeachFunction([&s, show_context](const FunctionSP &f) { 131 f->Dump(s, show_context); 132 return false; 133 }); 134 135 s->IndentLess(); 136 s->EOL(); 137 } 138 } 139 140 // Add a function to this compile unit 141 void CompileUnit::AddFunction(FunctionSP &funcSP) { 142 m_functions_by_uid[funcSP->GetID()] = funcSP; 143 } 144 145 FunctionSP CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid) { 146 auto it = m_functions_by_uid.find(func_uid); 147 if (it == m_functions_by_uid.end()) 148 return FunctionSP(); 149 return it->second; 150 } 151 152 lldb::LanguageType CompileUnit::GetLanguage() { 153 if (m_language == eLanguageTypeUnknown) { 154 if (m_flags.IsClear(flagsParsedLanguage)) { 155 m_flags.Set(flagsParsedLanguage); 156 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 157 m_language = symfile->ParseLanguage(*this); 158 } 159 } 160 return m_language; 161 } 162 163 LineTable *CompileUnit::GetLineTable() { 164 if (m_line_table_up == nullptr) { 165 if (m_flags.IsClear(flagsParsedLineTable)) { 166 m_flags.Set(flagsParsedLineTable); 167 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 168 symfile->ParseLineTable(*this); 169 } 170 } 171 return m_line_table_up.get(); 172 } 173 174 void CompileUnit::SetLineTable(LineTable *line_table) { 175 if (line_table == nullptr) 176 m_flags.Clear(flagsParsedLineTable); 177 else 178 m_flags.Set(flagsParsedLineTable); 179 m_line_table_up.reset(line_table); 180 } 181 182 DebugMacros *CompileUnit::GetDebugMacros() { 183 if (m_debug_macros_sp.get() == nullptr) { 184 if (m_flags.IsClear(flagsParsedDebugMacros)) { 185 m_flags.Set(flagsParsedDebugMacros); 186 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 187 symfile->ParseDebugMacros(*this); 188 } 189 } 190 191 return m_debug_macros_sp.get(); 192 } 193 194 void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) { 195 if (debug_macros_sp.get() == nullptr) 196 m_flags.Clear(flagsParsedDebugMacros); 197 else 198 m_flags.Set(flagsParsedDebugMacros); 199 m_debug_macros_sp = debug_macros_sp; 200 } 201 202 VariableListSP CompileUnit::GetVariableList(bool can_create) { 203 if (m_variables.get() == nullptr && can_create) { 204 SymbolContext sc; 205 CalculateSymbolContext(&sc); 206 assert(sc.module_sp); 207 sc.module_sp->GetSymbolFile()->ParseVariablesForContext(sc); 208 } 209 210 return m_variables; 211 } 212 213 std::vector<uint32_t> FindFileIndexes(const SupportFileList &files, 214 const FileSpec &file) { 215 std::vector<uint32_t> result; 216 uint32_t idx = -1; 217 while ((idx = files.FindCompatibleIndex(idx + 1, file)) != 218 UINT32_MAX) 219 result.push_back(idx); 220 return result; 221 } 222 223 uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line, 224 const FileSpec *file_spec_ptr, bool exact, 225 LineEntry *line_entry_ptr) { 226 if (!file_spec_ptr) 227 file_spec_ptr = &GetPrimaryFile(); 228 std::vector<uint32_t> file_indexes = FindFileIndexes(GetSupportFiles(), 229 *file_spec_ptr); 230 if (file_indexes.empty()) 231 return UINT32_MAX; 232 233 // TODO: Handle SourceLocationSpec column information 234 SourceLocationSpec location_spec(*file_spec_ptr, line, 235 /*column=*/std::nullopt, 236 /*check_inlines=*/false, exact); 237 238 LineTable *line_table = GetLineTable(); 239 if (line_table) 240 return line_table->FindLineEntryIndexByFileIndex( 241 start_idx, file_indexes, location_spec, line_entry_ptr); 242 return UINT32_MAX; 243 } 244 245 void CompileUnit::ResolveSymbolContext( 246 const SourceLocationSpec &src_location_spec, 247 SymbolContextItem resolve_scope, SymbolContextList &sc_list) { 248 const FileSpec file_spec = src_location_spec.GetFileSpec(); 249 const uint32_t line = src_location_spec.GetLine().value_or(0); 250 const bool check_inlines = src_location_spec.GetCheckInlines(); 251 252 // First find all of the file indexes that match our "file_spec". If 253 // "file_spec" has an empty directory, then only compare the basenames when 254 // finding file indexes 255 bool file_spec_matches_cu_file_spec = 256 FileSpec::Match(file_spec, this->GetPrimaryFile()); 257 258 // If we are not looking for inlined functions and our file spec doesn't 259 // match then we are done... 260 if (!file_spec_matches_cu_file_spec && !check_inlines) 261 return; 262 263 SymbolContext sc(GetModule()); 264 sc.comp_unit = this; 265 266 if (line == 0) { 267 if (file_spec_matches_cu_file_spec && !check_inlines) { 268 // only append the context if we aren't looking for inline call sites by 269 // file and line and if the file spec matches that of the compile unit 270 sc_list.Append(sc); 271 } 272 return; 273 } 274 275 std::vector<uint32_t> file_indexes = FindFileIndexes(GetSupportFiles(), 276 file_spec); 277 const size_t num_file_indexes = file_indexes.size(); 278 if (num_file_indexes == 0) 279 return; 280 281 // Found a matching source file in this compile unit load its debug info. 282 GetModule()->GetSymbolFile()->SetLoadDebugInfoEnabled(); 283 284 LineTable *line_table = sc.comp_unit->GetLineTable(); 285 286 if (line_table == nullptr) { 287 if (file_spec_matches_cu_file_spec && !check_inlines) { 288 sc_list.Append(sc); 289 } 290 return; 291 } 292 293 uint32_t line_idx; 294 LineEntry line_entry; 295 296 if (num_file_indexes == 1) { 297 // We only have a single support file that matches, so use the line 298 // table function that searches for a line entries that match a single 299 // support file index 300 line_idx = line_table->FindLineEntryIndexByFileIndex( 301 0, file_indexes.front(), src_location_spec, &line_entry); 302 } else { 303 // We found multiple support files that match "file_spec" so use the 304 // line table function that searches for a line entries that match a 305 // multiple support file indexes. 306 line_idx = line_table->FindLineEntryIndexByFileIndex( 307 0, file_indexes, src_location_spec, &line_entry); 308 } 309 310 // If "exact == true", then "found_line" will be the same as "line". If 311 // "exact == false", the "found_line" will be the closest line entry 312 // with a line number greater than "line" and we will use this for our 313 // subsequent line exact matches below. 314 const bool inlines = false; 315 const bool exact = true; 316 const std::optional<uint16_t> column = 317 src_location_spec.GetColumn() ? std::optional<uint16_t>(line_entry.column) 318 : std::nullopt; 319 320 SourceLocationSpec found_entry(line_entry.file, line_entry.line, column, 321 inlines, exact); 322 323 while (line_idx != UINT32_MAX) { 324 // If they only asked for the line entry, then we're done, we can 325 // just copy that over. But if they wanted more than just the line 326 // number, fill it in. 327 SymbolContext resolved_sc; 328 sc.line_entry = line_entry; 329 if (resolve_scope == eSymbolContextLineEntry) { 330 sc_list.Append(sc); 331 } else { 332 line_entry.range.GetBaseAddress().CalculateSymbolContext(&resolved_sc, 333 resolve_scope); 334 // Sometimes debug info is bad and isn't able to resolve the line entry's 335 // address back to the same compile unit and/or line entry. If the compile 336 // unit changed, then revert back to just the compile unit and line entry. 337 // Prior to this fix, the above code might end up not being able to lookup 338 // the address, and then it would clear compile unit and the line entry in 339 // the symbol context and the breakpoint would fail to get set even though 340 // we have a valid line table entry in this compile unit. The address 341 // lookup can also end up finding another function in another compiler 342 // unit if the DWARF has overlappging address ranges. So if we end up with 343 // no compile unit or a different one after the above function call, 344 // revert back to the same results as if resolve_scope was set exactly to 345 // eSymbolContextLineEntry. 346 if (resolved_sc.comp_unit == this) { 347 sc_list.Append(resolved_sc); 348 } else { 349 if (resolved_sc.comp_unit == nullptr && resolved_sc.module_sp) { 350 // Only report an error if we don't map back to any compile unit. With 351 // link time optimizations, the debug info might have many compile 352 // units that have the same address range due to function outlining 353 // or other link time optimizations. If the compile unit is NULL, then 354 // address resolving is completely failing and more deserving of an 355 // error message the user can see. 356 resolved_sc.module_sp->ReportError( 357 "unable to resolve a line table file address {0:x16} back " 358 "to a compile unit, please file a bug and attach the address " 359 "and file.", 360 line_entry.range.GetBaseAddress().GetFileAddress()); 361 } 362 sc_list.Append(sc); 363 } 364 } 365 366 if (num_file_indexes == 1) 367 line_idx = line_table->FindLineEntryIndexByFileIndex( 368 line_idx + 1, file_indexes.front(), found_entry, &line_entry); 369 else 370 line_idx = line_table->FindLineEntryIndexByFileIndex( 371 line_idx + 1, file_indexes, found_entry, &line_entry); 372 } 373 } 374 375 bool CompileUnit::GetIsOptimized() { 376 if (m_is_optimized == eLazyBoolCalculate) { 377 m_is_optimized = eLazyBoolNo; 378 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { 379 if (symfile->ParseIsOptimized(*this)) 380 m_is_optimized = eLazyBoolYes; 381 } 382 } 383 return m_is_optimized; 384 } 385 386 void CompileUnit::SetVariableList(VariableListSP &variables) { 387 m_variables = variables; 388 } 389 390 const std::vector<SourceModule> &CompileUnit::GetImportedModules() { 391 if (m_imported_modules.empty() && 392 m_flags.IsClear(flagsParsedImportedModules)) { 393 m_flags.Set(flagsParsedImportedModules); 394 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { 395 SymbolContext sc; 396 CalculateSymbolContext(&sc); 397 symfile->ParseImportedModules(sc, m_imported_modules); 398 } 399 } 400 return m_imported_modules; 401 } 402 403 bool CompileUnit::ForEachExternalModule( 404 llvm::DenseSet<SymbolFile *> &visited_symbol_files, 405 llvm::function_ref<bool(Module &)> lambda) { 406 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 407 return symfile->ForEachExternalModule(*this, visited_symbol_files, lambda); 408 return false; 409 } 410 411 const SupportFileList &CompileUnit::GetSupportFiles() { 412 if (m_support_files.GetSize() == 0) { 413 if (m_flags.IsClear(flagsParsedSupportFiles)) { 414 m_flags.Set(flagsParsedSupportFiles); 415 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 416 symfile->ParseSupportFiles(*this, m_support_files); 417 } 418 } 419 return m_support_files; 420 } 421 422 void *CompileUnit::GetUserData() const { return m_user_data; } 423