1 //===-- SourceManager.cpp ---------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/Core/SourceManager.h" 11 12 // C Includes 13 // C++ Includes 14 // Other libraries and framework includes 15 // Project includes 16 #include "lldb/Core/DataBuffer.h" 17 #include "lldb/Core/Debugger.h" 18 #include "lldb/Core/Module.h" 19 #include "lldb/Core/RegularExpression.h" 20 #include "lldb/Core/Stream.h" 21 #include "lldb/Symbol/CompileUnit.h" 22 #include "lldb/Symbol/Function.h" 23 #include "lldb/Symbol/SymbolContext.h" 24 #include "lldb/Target/Target.h" 25 26 using namespace lldb; 27 using namespace lldb_private; 28 29 static inline bool is_newline_char(char ch) { return ch == '\n' || ch == '\r'; } 30 31 //---------------------------------------------------------------------- 32 // SourceManager constructor 33 //---------------------------------------------------------------------- 34 SourceManager::SourceManager(const TargetSP &target_sp) 35 : m_last_file_sp(), m_last_line(0), m_last_count(0), m_default_set(false), 36 m_target_wp(target_sp), 37 m_debugger_wp(target_sp->GetDebugger().shared_from_this()) {} 38 39 SourceManager::SourceManager(const DebuggerSP &debugger_sp) 40 : m_last_file_sp(), m_last_line(0), m_last_count(0), m_default_set(false), 41 m_target_wp(), m_debugger_wp(debugger_sp) {} 42 43 //---------------------------------------------------------------------- 44 // Destructor 45 //---------------------------------------------------------------------- 46 SourceManager::~SourceManager() {} 47 48 SourceManager::FileSP SourceManager::GetFile(const FileSpec &file_spec) { 49 bool same_as_previous = 50 m_last_file_sp && m_last_file_sp->FileSpecMatches(file_spec); 51 52 DebuggerSP debugger_sp(m_debugger_wp.lock()); 53 FileSP file_sp; 54 if (same_as_previous) 55 file_sp = m_last_file_sp; 56 else if (debugger_sp) 57 file_sp = debugger_sp->GetSourceFileCache().FindSourceFile(file_spec); 58 59 TargetSP target_sp(m_target_wp.lock()); 60 61 // It the target source path map has been updated, get this file again so we 62 // can successfully remap the source file 63 if (target_sp && file_sp && 64 file_sp->GetSourceMapModificationID() != 65 target_sp->GetSourcePathMap().GetModificationID()) 66 file_sp.reset(); 67 68 // Update the file contents if needed if we found a file 69 if (file_sp) 70 file_sp->UpdateIfNeeded(); 71 72 // If file_sp is no good or it points to a non-existent file, reset it. 73 if (!file_sp || !file_sp->GetFileSpec().Exists()) { 74 file_sp.reset(new File(file_spec, target_sp.get())); 75 76 if (debugger_sp) 77 debugger_sp->GetSourceFileCache().AddSourceFile(file_sp); 78 } 79 return file_sp; 80 } 81 82 size_t SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile( 83 uint32_t start_line, uint32_t count, uint32_t curr_line, 84 const char *current_line_cstr, Stream *s, 85 const SymbolContextList *bp_locs) { 86 if (count == 0) 87 return 0; 88 size_t return_value = 0; 89 if (start_line == 0) { 90 if (m_last_line != 0 && m_last_line != UINT32_MAX) 91 start_line = m_last_line + m_last_count; 92 else 93 start_line = 1; 94 } 95 96 if (!m_default_set) { 97 FileSpec tmp_spec; 98 uint32_t tmp_line; 99 GetDefaultFileAndLine(tmp_spec, tmp_line); 100 } 101 102 m_last_line = start_line; 103 m_last_count = count; 104 105 if (m_last_file_sp.get()) { 106 const uint32_t end_line = start_line + count - 1; 107 for (uint32_t line = start_line; line <= end_line; ++line) { 108 if (!m_last_file_sp->LineIsValid(line)) { 109 m_last_line = UINT32_MAX; 110 break; 111 } 112 113 char prefix[32] = ""; 114 if (bp_locs) { 115 uint32_t bp_count = bp_locs->NumLineEntriesWithLine(line); 116 117 if (bp_count > 0) 118 ::snprintf(prefix, sizeof(prefix), "[%u] ", bp_count); 119 else 120 ::snprintf(prefix, sizeof(prefix), " "); 121 } 122 123 return_value += 124 s->Printf("%s%2.2s %-4u\t", prefix, 125 line == curr_line ? current_line_cstr : "", line); 126 size_t this_line_size = m_last_file_sp->DisplaySourceLines(line, 0, 0, s); 127 if (this_line_size == 0) { 128 m_last_line = UINT32_MAX; 129 break; 130 } else 131 return_value += this_line_size; 132 } 133 } 134 return return_value; 135 } 136 137 size_t SourceManager::DisplaySourceLinesWithLineNumbers( 138 const FileSpec &file_spec, uint32_t line, uint32_t context_before, 139 uint32_t context_after, const char *current_line_cstr, Stream *s, 140 const SymbolContextList *bp_locs) { 141 FileSP file_sp(GetFile(file_spec)); 142 143 uint32_t start_line; 144 uint32_t count = context_before + context_after + 1; 145 if (line > context_before) 146 start_line = line - context_before; 147 else 148 start_line = 1; 149 150 if (m_last_file_sp.get() != file_sp.get()) { 151 if (line == 0) 152 m_last_line = 0; 153 m_last_file_sp = file_sp; 154 } 155 return DisplaySourceLinesWithLineNumbersUsingLastFile( 156 start_line, count, line, current_line_cstr, s, bp_locs); 157 } 158 159 size_t SourceManager::DisplayMoreWithLineNumbers( 160 Stream *s, uint32_t count, bool reverse, const SymbolContextList *bp_locs) { 161 // If we get called before anybody has set a default file and line, then try 162 // to figure it out here. 163 const bool have_default_file_line = m_last_file_sp && m_last_line > 0; 164 if (!m_default_set) { 165 FileSpec tmp_spec; 166 uint32_t tmp_line; 167 GetDefaultFileAndLine(tmp_spec, tmp_line); 168 } 169 170 if (m_last_file_sp) { 171 if (m_last_line == UINT32_MAX) 172 return 0; 173 174 if (reverse && m_last_line == 1) 175 return 0; 176 177 if (count > 0) 178 m_last_count = count; 179 else if (m_last_count == 0) 180 m_last_count = 10; 181 182 if (m_last_line > 0) { 183 if (reverse) { 184 // If this is the first time we've done a reverse, then back up one more 185 // time so we end 186 // up showing the chunk before the last one we've shown: 187 if (m_last_line > m_last_count) 188 m_last_line -= m_last_count; 189 else 190 m_last_line = 1; 191 } else if (have_default_file_line) 192 m_last_line += m_last_count; 193 } else 194 m_last_line = 1; 195 196 return DisplaySourceLinesWithLineNumbersUsingLastFile( 197 m_last_line, m_last_count, UINT32_MAX, "", s, bp_locs); 198 } 199 return 0; 200 } 201 202 bool SourceManager::SetDefaultFileAndLine(const FileSpec &file_spec, 203 uint32_t line) { 204 FileSP old_file_sp = m_last_file_sp; 205 m_last_file_sp = GetFile(file_spec); 206 207 m_default_set = true; 208 if (m_last_file_sp) { 209 m_last_line = line; 210 return true; 211 } else { 212 m_last_file_sp = old_file_sp; 213 return false; 214 } 215 } 216 217 bool SourceManager::GetDefaultFileAndLine(FileSpec &file_spec, uint32_t &line) { 218 if (m_last_file_sp) { 219 file_spec = m_last_file_sp->GetFileSpec(); 220 line = m_last_line; 221 return true; 222 } else if (!m_default_set) { 223 TargetSP target_sp(m_target_wp.lock()); 224 225 if (target_sp) { 226 // If nobody has set the default file and line then try here. If there's 227 // no executable, then we 228 // will try again later when there is one. Otherwise, if we can't find it 229 // we won't look again, 230 // somebody will have to set it (for instance when we stop somewhere...) 231 Module *executable_ptr = target_sp->GetExecutableModulePointer(); 232 if (executable_ptr) { 233 SymbolContextList sc_list; 234 ConstString main_name("main"); 235 bool symbols_okay = false; // Force it to be a debug symbol. 236 bool inlines_okay = true; 237 bool append = false; 238 size_t num_matches = executable_ptr->FindFunctions( 239 main_name, NULL, lldb::eFunctionNameTypeBase, inlines_okay, 240 symbols_okay, append, sc_list); 241 for (size_t idx = 0; idx < num_matches; idx++) { 242 SymbolContext sc; 243 sc_list.GetContextAtIndex(idx, sc); 244 if (sc.function) { 245 lldb_private::LineEntry line_entry; 246 if (sc.function->GetAddressRange() 247 .GetBaseAddress() 248 .CalculateSymbolContextLineEntry(line_entry)) { 249 SetDefaultFileAndLine(line_entry.file, line_entry.line); 250 file_spec = m_last_file_sp->GetFileSpec(); 251 line = m_last_line; 252 return true; 253 } 254 } 255 } 256 } 257 } 258 } 259 return false; 260 } 261 262 void SourceManager::FindLinesMatchingRegex(FileSpec &file_spec, 263 RegularExpression ®ex, 264 uint32_t start_line, 265 uint32_t end_line, 266 std::vector<uint32_t> &match_lines) { 267 match_lines.clear(); 268 FileSP file_sp = GetFile(file_spec); 269 if (!file_sp) 270 return; 271 return file_sp->FindLinesMatchingRegex(regex, start_line, end_line, 272 match_lines); 273 } 274 275 SourceManager::File::File(const FileSpec &file_spec, Target *target) 276 : m_file_spec_orig(file_spec), m_file_spec(file_spec), 277 m_mod_time(file_spec.GetModificationTime()), m_source_map_mod_id(0), 278 m_data_sp(), m_offsets() { 279 if (!m_mod_time.IsValid()) { 280 if (target) { 281 m_source_map_mod_id = target->GetSourcePathMap().GetModificationID(); 282 283 if (!file_spec.GetDirectory() && file_spec.GetFilename()) { 284 // If this is just a file name, lets see if we can find it in the 285 // target: 286 bool check_inlines = false; 287 SymbolContextList sc_list; 288 size_t num_matches = 289 target->GetImages().ResolveSymbolContextForFilePath( 290 file_spec.GetFilename().AsCString(), 0, check_inlines, 291 lldb::eSymbolContextModule | lldb::eSymbolContextCompUnit, 292 sc_list); 293 bool got_multiple = false; 294 if (num_matches != 0) { 295 if (num_matches > 1) { 296 SymbolContext sc; 297 FileSpec *test_cu_spec = NULL; 298 299 for (unsigned i = 0; i < num_matches; i++) { 300 sc_list.GetContextAtIndex(i, sc); 301 if (sc.comp_unit) { 302 if (test_cu_spec) { 303 if (test_cu_spec != static_cast<FileSpec *>(sc.comp_unit)) 304 got_multiple = true; 305 break; 306 } else 307 test_cu_spec = sc.comp_unit; 308 } 309 } 310 } 311 if (!got_multiple) { 312 SymbolContext sc; 313 sc_list.GetContextAtIndex(0, sc); 314 m_file_spec = sc.comp_unit; 315 m_mod_time = m_file_spec.GetModificationTime(); 316 } 317 } 318 } 319 // Try remapping if m_file_spec does not correspond to an existing file. 320 if (!m_file_spec.Exists()) { 321 FileSpec new_file_spec; 322 // Check target specific source remappings first, then fall back to 323 // modules objects can have individual path remappings that were 324 // detected 325 // when the debug info for a module was found. 326 // then 327 if (target->GetSourcePathMap().FindFile(m_file_spec, new_file_spec) || 328 target->GetImages().FindSourceFile(m_file_spec, new_file_spec)) { 329 m_file_spec = new_file_spec; 330 m_mod_time = m_file_spec.GetModificationTime(); 331 } 332 } 333 } 334 } 335 336 if (m_mod_time.IsValid()) 337 m_data_sp = m_file_spec.ReadFileContents(); 338 } 339 340 SourceManager::File::~File() {} 341 342 uint32_t SourceManager::File::GetLineOffset(uint32_t line) { 343 if (line == 0) 344 return UINT32_MAX; 345 346 if (line == 1) 347 return 0; 348 349 if (CalculateLineOffsets(line)) { 350 if (line < m_offsets.size()) 351 return m_offsets[line - 1]; // yes we want "line - 1" in the index 352 } 353 return UINT32_MAX; 354 } 355 356 uint32_t SourceManager::File::GetNumLines() { 357 CalculateLineOffsets(); 358 return m_offsets.size(); 359 } 360 361 const char *SourceManager::File::PeekLineData(uint32_t line) { 362 if (!LineIsValid(line)) 363 return NULL; 364 365 size_t line_offset = GetLineOffset(line); 366 if (line_offset < m_data_sp->GetByteSize()) 367 return (const char *)m_data_sp->GetBytes() + line_offset; 368 return NULL; 369 } 370 371 uint32_t SourceManager::File::GetLineLength(uint32_t line, 372 bool include_newline_chars) { 373 if (!LineIsValid(line)) 374 return false; 375 376 size_t start_offset = GetLineOffset(line); 377 size_t end_offset = GetLineOffset(line + 1); 378 if (end_offset == UINT32_MAX) 379 end_offset = m_data_sp->GetByteSize(); 380 381 if (end_offset > start_offset) { 382 uint32_t length = end_offset - start_offset; 383 if (include_newline_chars == false) { 384 const char *line_start = 385 (const char *)m_data_sp->GetBytes() + start_offset; 386 while (length > 0) { 387 const char last_char = line_start[length - 1]; 388 if ((last_char == '\r') || (last_char == '\n')) 389 --length; 390 else 391 break; 392 } 393 } 394 return length; 395 } 396 return 0; 397 } 398 399 bool SourceManager::File::LineIsValid(uint32_t line) { 400 if (line == 0) 401 return false; 402 403 if (CalculateLineOffsets(line)) 404 return line < m_offsets.size(); 405 return false; 406 } 407 408 void SourceManager::File::UpdateIfNeeded() { 409 // TODO: use host API to sign up for file modifications to anything in our 410 // source cache and only update when we determine a file has been updated. 411 // For now we check each time we want to display info for the file. 412 TimeValue curr_mod_time(m_file_spec.GetModificationTime()); 413 414 if (curr_mod_time.IsValid() && m_mod_time != curr_mod_time) { 415 m_mod_time = curr_mod_time; 416 m_data_sp = m_file_spec.ReadFileContents(); 417 m_offsets.clear(); 418 } 419 } 420 421 size_t SourceManager::File::DisplaySourceLines(uint32_t line, 422 uint32_t context_before, 423 uint32_t context_after, 424 Stream *s) { 425 // Sanity check m_data_sp before proceeding. 426 if (!m_data_sp) 427 return 0; 428 429 const uint32_t start_line = 430 line <= context_before ? 1 : line - context_before; 431 const uint32_t start_line_offset = GetLineOffset(start_line); 432 if (start_line_offset != UINT32_MAX) { 433 const uint32_t end_line = line + context_after; 434 uint32_t end_line_offset = GetLineOffset(end_line + 1); 435 if (end_line_offset == UINT32_MAX) 436 end_line_offset = m_data_sp->GetByteSize(); 437 438 assert(start_line_offset <= end_line_offset); 439 size_t bytes_written = 0; 440 if (start_line_offset < end_line_offset) { 441 size_t count = end_line_offset - start_line_offset; 442 const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset; 443 bytes_written = s->Write(cstr, count); 444 if (!is_newline_char(cstr[count - 1])) 445 bytes_written += s->EOL(); 446 } 447 return bytes_written; 448 } 449 return 0; 450 } 451 452 void SourceManager::File::FindLinesMatchingRegex( 453 RegularExpression ®ex, uint32_t start_line, uint32_t end_line, 454 std::vector<uint32_t> &match_lines) { 455 match_lines.clear(); 456 457 if (!LineIsValid(start_line) || 458 (end_line != UINT32_MAX && !LineIsValid(end_line))) 459 return; 460 if (start_line > end_line) 461 return; 462 463 for (uint32_t line_no = start_line; line_no < end_line; line_no++) { 464 std::string buffer; 465 if (!GetLine(line_no, buffer)) 466 break; 467 if (regex.Execute(buffer.c_str())) { 468 match_lines.push_back(line_no); 469 } 470 } 471 } 472 473 bool SourceManager::File::FileSpecMatches(const FileSpec &file_spec) { 474 return FileSpec::Equal(m_file_spec, file_spec, false); 475 } 476 477 bool lldb_private::operator==(const SourceManager::File &lhs, 478 const SourceManager::File &rhs) { 479 if (lhs.m_file_spec == rhs.m_file_spec) { 480 if (lhs.m_mod_time.IsValid()) { 481 if (rhs.m_mod_time.IsValid()) 482 return lhs.m_mod_time == rhs.m_mod_time; 483 else 484 return false; 485 } else if (rhs.m_mod_time.IsValid()) 486 return false; 487 else 488 return true; 489 } else 490 return false; 491 } 492 493 bool SourceManager::File::CalculateLineOffsets(uint32_t line) { 494 line = 495 UINT32_MAX; // TODO: take this line out when we support partial indexing 496 if (line == UINT32_MAX) { 497 // Already done? 498 if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX) 499 return true; 500 501 if (m_offsets.empty()) { 502 if (m_data_sp.get() == NULL) 503 return false; 504 505 const char *start = (char *)m_data_sp->GetBytes(); 506 if (start) { 507 const char *end = start + m_data_sp->GetByteSize(); 508 509 // Calculate all line offsets from scratch 510 511 // Push a 1 at index zero to indicate the file has been completely 512 // indexed. 513 m_offsets.push_back(UINT32_MAX); 514 const char *s; 515 for (s = start; s < end; ++s) { 516 char curr_ch = *s; 517 if (is_newline_char(curr_ch)) { 518 if (s + 1 < end) { 519 char next_ch = s[1]; 520 if (is_newline_char(next_ch)) { 521 if (curr_ch != next_ch) 522 ++s; 523 } 524 } 525 m_offsets.push_back(s + 1 - start); 526 } 527 } 528 if (!m_offsets.empty()) { 529 if (m_offsets.back() < end - start) 530 m_offsets.push_back(end - start); 531 } 532 return true; 533 } 534 } else { 535 // Some lines have been populated, start where we last left off 536 assert("Not implemented yet" && false); 537 } 538 539 } else { 540 // Calculate all line offsets up to "line" 541 assert("Not implemented yet" && false); 542 } 543 return false; 544 } 545 546 bool SourceManager::File::GetLine(uint32_t line_no, std::string &buffer) { 547 if (!LineIsValid(line_no)) 548 return false; 549 550 size_t start_offset = GetLineOffset(line_no); 551 size_t end_offset = GetLineOffset(line_no + 1); 552 if (end_offset == UINT32_MAX) { 553 end_offset = m_data_sp->GetByteSize(); 554 } 555 buffer.assign((char *)m_data_sp->GetBytes() + start_offset, 556 end_offset - start_offset); 557 558 return true; 559 } 560 561 void SourceManager::SourceFileCache::AddSourceFile(const FileSP &file_sp) { 562 FileSpec file_spec; 563 FileCache::iterator pos = m_file_cache.find(file_spec); 564 if (pos == m_file_cache.end()) 565 m_file_cache[file_spec] = file_sp; 566 else { 567 if (file_sp != pos->second) 568 m_file_cache[file_spec] = file_sp; 569 } 570 } 571 572 SourceManager::FileSP SourceManager::SourceFileCache::FindSourceFile( 573 const FileSpec &file_spec) const { 574 FileSP file_sp; 575 FileCache::const_iterator pos = m_file_cache.find(file_spec); 576 if (pos != m_file_cache.end()) 577 file_sp = pos->second; 578 return file_sp; 579 } 580