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