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 #include "lldb/Utility/Timer.h" 16 17 using namespace lldb; 18 using namespace lldb_private; 19 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 : ModuleChild(module_sp), FileSpec(pathname), UserID(cu_sym_id), 25 m_user_data(user_data), m_language(language), m_flags(0), 26 m_support_files(), m_line_table_up(), m_variables(), 27 m_is_optimized(is_optimized) { 28 if (language != eLanguageTypeUnknown) 29 m_flags.Set(flagsParsedLanguage); 30 assert(module_sp); 31 } 32 33 CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, 34 const FileSpec &fspec, const lldb::user_id_t cu_sym_id, 35 lldb::LanguageType language, 36 lldb_private::LazyBool is_optimized) 37 : ModuleChild(module_sp), FileSpec(fspec), UserID(cu_sym_id), 38 m_user_data(user_data), m_language(language), m_flags(0), 39 m_support_files(), m_line_table_up(), m_variables(), 40 m_is_optimized(is_optimized) { 41 if (language != eLanguageTypeUnknown) 42 m_flags.Set(flagsParsedLanguage); 43 assert(module_sp); 44 } 45 46 CompileUnit::~CompileUnit() {} 47 48 void CompileUnit::CalculateSymbolContext(SymbolContext *sc) { 49 sc->comp_unit = this; 50 GetModule()->CalculateSymbolContext(sc); 51 } 52 53 ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); } 54 55 CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; } 56 57 void CompileUnit::DumpSymbolContext(Stream *s) { 58 GetModule()->DumpSymbolContext(s); 59 s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID()); 60 } 61 62 void CompileUnit::GetDescription(Stream *s, 63 lldb::DescriptionLevel level) const { 64 const char *language = Language::GetNameForLanguageType(m_language); 65 *s << "id = " << (const UserID &)*this << ", file = \"" 66 << (const FileSpec &)*this << "\", language = \"" << language << '"'; 67 } 68 69 void CompileUnit::ForeachFunction( 70 llvm::function_ref<bool(const FunctionSP &)> lambda) const { 71 std::vector<lldb::FunctionSP> sorted_functions; 72 sorted_functions.reserve(m_functions_by_uid.size()); 73 for (auto &p : m_functions_by_uid) 74 sorted_functions.push_back(p.second); 75 llvm::sort(sorted_functions.begin(), sorted_functions.end(), 76 [](const lldb::FunctionSP &a, const lldb::FunctionSP &b) { 77 return a->GetID() < b->GetID(); 78 }); 79 80 for (auto &f : sorted_functions) 81 if (lambda(f)) 82 return; 83 } 84 85 lldb::FunctionSP CompileUnit::FindFunction( 86 llvm::function_ref<bool(const FunctionSP &)> matching_lambda) { 87 static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); 88 Timer scoped_timer(func_cat, "CompileUnit::FindFunction"); 89 90 lldb::ModuleSP module = CalculateSymbolContextModule(); 91 92 if (!module) 93 return {}; 94 95 SymbolFile *symbol_file = module->GetSymbolFile(); 96 97 if (!symbol_file) 98 return {}; 99 100 // m_functions_by_uid is filled in lazily but we need all the entries. 101 symbol_file->ParseFunctions(*this); 102 103 for (auto &p : m_functions_by_uid) { 104 if (matching_lambda(p.second)) 105 return p.second; 106 } 107 return {}; 108 } 109 110 // Dump the current contents of this object. No functions that cause on demand 111 // parsing of functions, globals, statics are called, so this is a good 112 // function to call to get an idea of the current contents of the CompileUnit 113 // object. 114 void CompileUnit::Dump(Stream *s, bool show_context) const { 115 const char *language = Language::GetNameForLanguageType(m_language); 116 117 s->Printf("%p: ", static_cast<const void *>(this)); 118 s->Indent(); 119 *s << "CompileUnit" << static_cast<const UserID &>(*this) << ", language = \"" 120 << language << "\", file = '" << static_cast<const FileSpec &>(*this) 121 << "'\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([&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(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(flagsParsedLanguage)) { 158 m_flags.Set(flagsParsedLanguage); 159 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 160 m_language = symfile->ParseLanguage(*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(flagsParsedLineTable)) { 169 m_flags.Set(flagsParsedLineTable); 170 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 171 symfile->ParseLineTable(*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(flagsParsedLineTable); 180 else 181 m_flags.Set(flagsParsedLineTable); 182 m_line_table_up.reset(line_table); 183 } 184 185 void CompileUnit::SetSupportFiles(const FileSpecList &support_files) { 186 m_support_files = support_files; 187 } 188 189 DebugMacros *CompileUnit::GetDebugMacros() { 190 if (m_debug_macros_sp.get() == nullptr) { 191 if (m_flags.IsClear(flagsParsedDebugMacros)) { 192 m_flags.Set(flagsParsedDebugMacros); 193 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 194 symfile->ParseDebugMacros(*this); 195 } 196 } 197 198 return m_debug_macros_sp.get(); 199 } 200 201 void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) { 202 if (debug_macros_sp.get() == nullptr) 203 m_flags.Clear(flagsParsedDebugMacros); 204 else 205 m_flags.Set(flagsParsedDebugMacros); 206 m_debug_macros_sp = debug_macros_sp; 207 } 208 209 VariableListSP CompileUnit::GetVariableList(bool can_create) { 210 if (m_variables.get() == nullptr && can_create) { 211 SymbolContext sc; 212 CalculateSymbolContext(&sc); 213 assert(sc.module_sp); 214 sc.module_sp->GetSymbolFile()->ParseVariablesForContext(sc); 215 } 216 217 return m_variables; 218 } 219 220 uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line, 221 const FileSpec *file_spec_ptr, bool exact, 222 LineEntry *line_entry_ptr) { 223 uint32_t file_idx = 0; 224 225 if (file_spec_ptr) { 226 file_idx = GetSupportFiles().FindFileIndex(1, *file_spec_ptr, true); 227 if (file_idx == UINT32_MAX) 228 return UINT32_MAX; 229 } else { 230 // All the line table entries actually point to the version of the Compile 231 // Unit that is in the support files (the one at 0 was artificially added.) 232 // So prefer the one further on in the support files if it exists... 233 const FileSpecList &support_files = GetSupportFiles(); 234 const bool full = true; 235 file_idx = support_files.FindFileIndex( 236 1, support_files.GetFileSpecAtIndex(0), full); 237 if (file_idx == UINT32_MAX) 238 file_idx = 0; 239 } 240 LineTable *line_table = GetLineTable(); 241 if (line_table) 242 return line_table->FindLineEntryIndexByFileIndex(start_idx, file_idx, line, 243 exact, line_entry_ptr); 244 return UINT32_MAX; 245 } 246 247 uint32_t CompileUnit::ResolveSymbolContext(const FileSpec &file_spec, 248 uint32_t line, bool check_inlines, 249 bool exact, 250 SymbolContextItem resolve_scope, 251 SymbolContextList &sc_list) { 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 std::vector<uint32_t> file_indexes; 256 const bool full_match = (bool)file_spec.GetDirectory(); 257 bool file_spec_matches_cu_file_spec = 258 FileSpec::Equal(file_spec, *this, full_match); 259 260 // If we are not looking for inlined functions and our file spec doesn't 261 // match then we are done... 262 if (!file_spec_matches_cu_file_spec && !check_inlines) 263 return 0; 264 265 uint32_t file_idx = 266 GetSupportFiles().FindFileIndex(1, file_spec, true); 267 while (file_idx != UINT32_MAX) { 268 file_indexes.push_back(file_idx); 269 file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true); 270 } 271 272 const size_t num_file_indexes = file_indexes.size(); 273 if (num_file_indexes == 0) 274 return 0; 275 276 const uint32_t prev_size = sc_list.GetSize(); 277 278 SymbolContext sc(GetModule()); 279 sc.comp_unit = this; 280 281 if (line != 0) { 282 LineTable *line_table = sc.comp_unit->GetLineTable(); 283 284 if (line_table != nullptr) { 285 uint32_t found_line; 286 uint32_t line_idx; 287 288 if (num_file_indexes == 1) { 289 // We only have a single support file that matches, so use the line 290 // table function that searches for a line entries that match a single 291 // support file index 292 LineEntry line_entry; 293 line_idx = line_table->FindLineEntryIndexByFileIndex( 294 0, file_indexes.front(), 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 they only asked for the line entry, then we're done, we can 304 // just copy that over. But if they wanted more than just the line 305 // number, fill it in. 306 if (resolve_scope == eSymbolContextLineEntry) { 307 sc.line_entry = line_entry; 308 } else { 309 line_entry.range.GetBaseAddress().CalculateSymbolContext( 310 &sc, resolve_scope); 311 } 312 313 sc_list.Append(sc); 314 line_idx = line_table->FindLineEntryIndexByFileIndex( 315 line_idx + 1, file_indexes.front(), found_line, true, 316 &line_entry); 317 } 318 } else { 319 // We found multiple support files that match "file_spec" so use the 320 // line table function that searches for a line entries that match a 321 // multiple support file indexes. 322 LineEntry line_entry; 323 line_idx = line_table->FindLineEntryIndexByFileIndex( 324 0, file_indexes, line, exact, &line_entry); 325 326 // If "exact == true", then "found_line" will be the same as "line". If 327 // "exact == false", the "found_line" will be the closest line entry 328 // with a line number greater than "line" and we will use this for our 329 // subsequent line exact matches below. 330 found_line = line_entry.line; 331 332 while (line_idx != UINT32_MAX) { 333 if (resolve_scope == eSymbolContextLineEntry) { 334 sc.line_entry = line_entry; 335 } else { 336 line_entry.range.GetBaseAddress().CalculateSymbolContext( 337 &sc, resolve_scope); 338 } 339 340 sc_list.Append(sc); 341 line_idx = line_table->FindLineEntryIndexByFileIndex( 342 line_idx + 1, file_indexes, found_line, true, &line_entry); 343 } 344 } 345 } 346 } else if (file_spec_matches_cu_file_spec && !check_inlines) { 347 // only append the context if we aren't looking for inline call sites by 348 // file and line and if the file spec matches that of the compile unit 349 sc_list.Append(sc); 350 } 351 return sc_list.GetSize() - prev_size; 352 } 353 354 bool CompileUnit::GetIsOptimized() { 355 if (m_is_optimized == eLazyBoolCalculate) { 356 m_is_optimized = eLazyBoolNo; 357 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { 358 if (symfile->ParseIsOptimized(*this)) 359 m_is_optimized = eLazyBoolYes; 360 } 361 } 362 return m_is_optimized; 363 } 364 365 void CompileUnit::SetVariableList(VariableListSP &variables) { 366 m_variables = variables; 367 } 368 369 const std::vector<SourceModule> &CompileUnit::GetImportedModules() { 370 if (m_imported_modules.empty() && 371 m_flags.IsClear(flagsParsedImportedModules)) { 372 m_flags.Set(flagsParsedImportedModules); 373 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { 374 SymbolContext sc; 375 CalculateSymbolContext(&sc); 376 symfile->ParseImportedModules(sc, m_imported_modules); 377 } 378 } 379 return m_imported_modules; 380 } 381 382 bool CompileUnit::ForEachExternalModule( 383 llvm::DenseSet<SymbolFile *> &visited_symbol_files, 384 llvm::function_ref<bool(Module &)> lambda) { 385 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 386 return symfile->ForEachExternalModule(*this, visited_symbol_files, lambda); 387 return false; 388 } 389 390 const FileSpecList &CompileUnit::GetSupportFiles() { 391 if (m_support_files.GetSize() == 0) { 392 if (m_flags.IsClear(flagsParsedSupportFiles)) { 393 m_flags.Set(flagsParsedSupportFiles); 394 if (SymbolFile *symfile = GetModule()->GetSymbolFile()) 395 symfile->ParseSupportFiles(*this, m_support_files); 396 } 397 } 398 return m_support_files; 399 } 400 401 void *CompileUnit::GetUserData() const { return m_user_data; } 402