1 //===-- SourceManager.cpp -------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Core/SourceManager.h"
11 #include "lldb/Core/Address.h"
12 #include "lldb/Core/AddressRange.h"
13 #include "lldb/Core/Debugger.h"
14 #include "lldb/Core/FormatEntity.h"
15 #include "lldb/Core/Highlighter.h"
16 #include "lldb/Core/Module.h"
17 #include "lldb/Core/ModuleList.h"
18 #include "lldb/Host/FileSystem.h"
19 #include "lldb/Symbol/CompileUnit.h"
20 #include "lldb/Symbol/Function.h"
21 #include "lldb/Symbol/LineEntry.h"
22 #include "lldb/Symbol/SymbolContext.h"
23 #include "lldb/Target/PathMappingList.h"
24 #include "lldb/Target/Process.h"
25 #include "lldb/Target/Target.h"
26 #include "lldb/Utility/AnsiTerminal.h"
27 #include "lldb/Utility/ConstString.h"
28 #include "lldb/Utility/DataBuffer.h"
29 #include "lldb/Utility/LLDBLog.h"
30 #include "lldb/Utility/Log.h"
31 #include "lldb/Utility/RegularExpression.h"
32 #include "lldb/Utility/Stream.h"
33 #include "lldb/lldb-enumerations.h"
35 #include "llvm/ADT/Twine.h"
44 namespace lldb_private
{
45 class ExecutionContext
;
47 namespace lldb_private
{
52 using namespace lldb_private
;
54 static inline bool is_newline_char(char ch
) { return ch
== '\n' || ch
== '\r'; }
56 static void resolve_tilde(FileSpec
&file_spec
) {
57 if (!FileSystem::Instance().Exists(file_spec
) &&
58 file_spec
.GetDirectory() &&
59 file_spec
.GetDirectory().GetCString()[0] == '~') {
60 FileSystem::Instance().Resolve(file_spec
);
64 static std::string
toString(const Checksum
&checksum
) {
67 return std::string(llvm::formatv("{0}", checksum
.digest()));
70 // SourceManager constructor
71 SourceManager::SourceManager(const TargetSP
&target_sp
)
72 : m_last_support_file_sp(std::make_shared
<SupportFile
>()), m_last_line(0),
73 m_last_count(0), m_default_set(false), m_target_wp(target_sp
),
74 m_debugger_wp(target_sp
->GetDebugger().shared_from_this()) {}
76 SourceManager::SourceManager(const DebuggerSP
&debugger_sp
)
77 : m_last_support_file_sp(std::make_shared
<SupportFile
>()), m_last_line(0),
78 m_last_count(0), m_default_set(false), m_target_wp(),
79 m_debugger_wp(debugger_sp
) {}
82 SourceManager::~SourceManager() = default;
84 SourceManager::FileSP
SourceManager::GetFile(SupportFileSP support_file_sp
) {
85 assert(support_file_sp
&& "SupportFileSP must be valid");
87 FileSpec file_spec
= support_file_sp
->GetSpecOnly();
91 Log
*log
= GetLog(LLDBLog::Source
);
93 DebuggerSP
debugger_sp(m_debugger_wp
.lock());
94 TargetSP
target_sp(m_target_wp
.lock());
96 if (!debugger_sp
|| !debugger_sp
->GetUseSourceCache()) {
97 LLDB_LOG(log
, "Source file caching disabled: creating new source file: {0}",
100 return std::make_shared
<File
>(support_file_sp
, target_sp
);
101 return std::make_shared
<File
>(support_file_sp
, debugger_sp
);
104 ProcessSP process_sp
= target_sp
? target_sp
->GetProcessSP() : ProcessSP();
106 // Check the process source cache first. This is the fast path which avoids
107 // touching the file system unless the path remapping has changed.
110 process_sp
->GetSourceFileCache().FindSourceFile(file_spec
)) {
111 LLDB_LOG(log
, "Found source file in the process cache: {0}", file_spec
);
112 if (file_sp
->PathRemappingIsStale()) {
113 LLDB_LOG(log
, "Path remapping is stale: removing file from caches: {0}",
116 // Remove the file from the debugger and process cache. Otherwise we'll
117 // hit the same issue again below when querying the debugger cache.
118 debugger_sp
->GetSourceFileCache().RemoveSourceFile(file_sp
);
119 process_sp
->GetSourceFileCache().RemoveSourceFile(file_sp
);
128 // Cache miss in the process cache. Check the debugger source cache.
129 FileSP file_sp
= debugger_sp
->GetSourceFileCache().FindSourceFile(file_spec
);
131 // We found the file in the debugger cache. Check if anything invalidated our
134 LLDB_LOG(log
, "Found source file in the debugger cache: {0}", file_spec
);
136 // Check if the path remapping has changed.
137 if (file_sp
&& file_sp
->PathRemappingIsStale()) {
138 LLDB_LOG(log
, "Path remapping is stale: {0}", file_spec
);
142 // Check if the modification time has changed.
143 if (file_sp
&& file_sp
->ModificationTimeIsStale()) {
144 LLDB_LOG(log
, "Modification time is stale: {0}", file_spec
);
148 // Check if the file exists on disk.
149 if (file_sp
&& !FileSystem::Instance().Exists(
150 file_sp
->GetSupportFile()->GetSpecOnly())) {
151 LLDB_LOG(log
, "File doesn't exist on disk: {0}", file_spec
);
155 // If at this point we don't have a valid file, it means we either didn't find
156 // it in the debugger cache or something caused it to be invalidated.
158 LLDB_LOG(log
, "Creating and caching new source file: {0}", file_spec
);
160 // (Re)create the file.
162 file_sp
= std::make_shared
<File
>(support_file_sp
, target_sp
);
164 file_sp
= std::make_shared
<File
>(support_file_sp
, debugger_sp
);
166 // Add the file to the debugger and process cache. If the file was
167 // invalidated, this will overwrite it.
168 debugger_sp
->GetSourceFileCache().AddSourceFile(file_spec
, file_sp
);
170 process_sp
->GetSourceFileCache().AddSourceFile(file_spec
, file_sp
);
176 static bool should_highlight_source(DebuggerSP debugger_sp
) {
180 // We don't use ANSI stop column formatting if the debugger doesn't think it
181 // should be using color.
182 if (!debugger_sp
->GetUseColor())
185 return debugger_sp
->GetHighlightSource();
188 static bool should_show_stop_column_with_ansi(DebuggerSP debugger_sp
) {
189 // We don't use ANSI stop column formatting if we can't lookup values from
194 // We don't use ANSI stop column formatting if the debugger doesn't think it
195 // should be using color.
196 if (!debugger_sp
->GetUseColor())
199 // We only use ANSI stop column formatting if we're either supposed to show
200 // ANSI where available (which we know we have when we get to this point), or
201 // if we're only supposed to use ANSI.
202 const auto value
= debugger_sp
->GetStopShowColumn();
203 return ((value
== eStopShowColumnAnsiOrCaret
) ||
204 (value
== eStopShowColumnAnsi
));
207 static bool should_show_stop_column_with_caret(DebuggerSP debugger_sp
) {
208 // We don't use text-based stop column formatting if we can't lookup values
209 // from the debugger.
213 // If we're asked to show the first available of ANSI or caret, then we do
214 // show the caret when ANSI is not available.
215 const auto value
= debugger_sp
->GetStopShowColumn();
216 if ((value
== eStopShowColumnAnsiOrCaret
) && !debugger_sp
->GetUseColor())
219 // The only other time we use caret is if we're explicitly asked to show
221 return value
== eStopShowColumnCaret
;
224 static bool should_show_stop_line_with_ansi(DebuggerSP debugger_sp
) {
225 return debugger_sp
&& debugger_sp
->GetUseColor();
228 size_t SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile(
229 uint32_t start_line
, uint32_t count
, uint32_t curr_line
, uint32_t column
,
230 const char *current_line_cstr
, Stream
*s
,
231 const SymbolContextList
*bp_locs
) {
235 Stream::ByteDelta
delta(*s
);
237 if (start_line
== 0) {
238 if (m_last_line
!= 0 && m_last_line
!= UINT32_MAX
)
239 start_line
= m_last_line
+ m_last_count
;
245 GetDefaultFileAndLine();
247 m_last_line
= start_line
;
248 m_last_count
= count
;
250 if (FileSP last_file_sp
= GetLastFile()) {
251 const uint32_t end_line
= start_line
+ count
- 1;
252 for (uint32_t line
= start_line
; line
<= end_line
; ++line
) {
253 if (!last_file_sp
->LineIsValid(line
)) {
254 m_last_line
= UINT32_MAX
;
260 uint32_t bp_count
= bp_locs
->NumLineEntriesWithLine(line
);
263 prefix
= llvm::formatv("[{0}]", bp_count
);
269 snprintf(buffer
, sizeof(buffer
), "%2.2s",
270 (line
== curr_line
) ? current_line_cstr
: "");
271 std::string
current_line_highlight(buffer
);
273 auto debugger_sp
= m_debugger_wp
.lock();
274 if (should_show_stop_line_with_ansi(debugger_sp
)) {
275 current_line_highlight
= ansi::FormatAnsiTerminalCodes(
276 (debugger_sp
->GetStopShowLineMarkerAnsiPrefix() +
277 current_line_highlight
+
278 debugger_sp
->GetStopShowLineMarkerAnsiSuffix())
282 s
->Printf("%s%s %-4u\t", prefix
.c_str(), current_line_highlight
.c_str(),
285 // So far we treated column 0 as a special 'no column value', but
286 // DisplaySourceLines starts counting columns from 0 (and no column is
287 // expressed by passing an empty optional).
288 std::optional
<size_t> columnToHighlight
;
289 if (line
== curr_line
&& column
)
290 columnToHighlight
= column
- 1;
292 size_t this_line_size
=
293 last_file_sp
->DisplaySourceLines(line
, columnToHighlight
, 0, 0, s
);
294 if (column
!= 0 && line
== curr_line
&&
295 should_show_stop_column_with_caret(debugger_sp
)) {
296 // Display caret cursor.
297 std::string src_line
;
298 last_file_sp
->GetLine(line
, src_line
);
300 // Insert a space for every non-tab character in the source line.
301 for (size_t i
= 0; i
+ 1 < column
&& i
< src_line
.length(); ++i
)
302 s
->PutChar(src_line
[i
] == '\t' ? '\t' : ' ');
303 // Now add the caret.
306 if (this_line_size
== 0) {
307 m_last_line
= UINT32_MAX
;
312 Checksum line_table_checksum
=
313 last_file_sp
->GetSupportFile()->GetChecksum();
314 Checksum on_disk_checksum
= last_file_sp
->GetChecksum();
315 if (line_table_checksum
&& line_table_checksum
!= on_disk_checksum
)
316 Debugger::ReportWarning(
318 "{0}: source file checksum mismatch between line table "
319 "({1}) and file on disk ({2})",
320 last_file_sp
->GetSupportFile()->GetSpecOnly().GetFilename(),
321 toString(line_table_checksum
), toString(on_disk_checksum
)),
322 std::nullopt
, &last_file_sp
->GetChecksumWarningOnceFlag());
327 size_t SourceManager::DisplaySourceLinesWithLineNumbers(
328 lldb::SupportFileSP support_file_sp
, uint32_t line
, uint32_t column
,
329 uint32_t context_before
, uint32_t context_after
,
330 const char *current_line_cstr
, Stream
*s
,
331 const SymbolContextList
*bp_locs
) {
332 assert(support_file_sp
&& "SupportFile must be valid");
333 FileSP
file_sp(GetFile(support_file_sp
));
336 uint32_t count
= context_before
+ context_after
+ 1;
337 if (line
> context_before
)
338 start_line
= line
- context_before
;
342 FileSP
last_file_sp(GetLastFile());
343 if (last_file_sp
.get() != file_sp
.get()) {
346 m_last_support_file_sp
= support_file_sp
;
349 return DisplaySourceLinesWithLineNumbersUsingLastFile(
350 start_line
, count
, line
, column
, current_line_cstr
, s
, bp_locs
);
353 size_t SourceManager::DisplayMoreWithLineNumbers(
354 Stream
*s
, uint32_t count
, bool reverse
, const SymbolContextList
*bp_locs
) {
355 // If we get called before anybody has set a default file and line, then try
356 // to figure it out here.
357 FileSP
last_file_sp(GetLastFile());
358 const bool have_default_file_line
= last_file_sp
&& m_last_line
> 0;
360 GetDefaultFileAndLine();
363 if (m_last_line
== UINT32_MAX
)
366 if (reverse
&& m_last_line
== 1)
370 m_last_count
= count
;
371 else if (m_last_count
== 0)
374 if (m_last_line
> 0) {
376 // If this is the first time we've done a reverse, then back up one
377 // more time so we end up showing the chunk before the last one we've
379 if (m_last_line
> m_last_count
)
380 m_last_line
-= m_last_count
;
383 } else if (have_default_file_line
)
384 m_last_line
+= m_last_count
;
388 const uint32_t column
= 0;
389 return DisplaySourceLinesWithLineNumbersUsingLastFile(
390 m_last_line
, m_last_count
, UINT32_MAX
, column
, "", s
, bp_locs
);
395 bool SourceManager::SetDefaultFileAndLine(lldb::SupportFileSP support_file_sp
,
397 assert(support_file_sp
&& "SupportFile must be valid");
399 m_default_set
= true;
401 if (FileSP file_sp
= GetFile(support_file_sp
)) {
403 m_last_support_file_sp
= support_file_sp
;
410 std::optional
<SourceManager::SupportFileAndLine
>
411 SourceManager::GetDefaultFileAndLine() {
412 if (FileSP last_file_sp
= GetLastFile())
413 return SupportFileAndLine(m_last_support_file_sp
, m_last_line
);
415 if (!m_default_set
) {
416 TargetSP
target_sp(m_target_wp
.lock());
419 // If nobody has set the default file and line then try here. If there's
420 // no executable, then we will try again later when there is one.
421 // Otherwise, if we can't find it we won't look again, somebody will have
422 // to set it (for instance when we stop somewhere...)
423 Module
*executable_ptr
= target_sp
->GetExecutableModulePointer();
424 if (executable_ptr
) {
425 SymbolContextList sc_list
;
426 ConstString
main_name("main");
428 ModuleFunctionSearchOptions function_options
;
429 function_options
.include_symbols
=
430 false; // Force it to be a debug symbol.
431 function_options
.include_inlines
= true;
432 executable_ptr
->FindFunctions(main_name
, CompilerDeclContext(),
433 lldb::eFunctionNameTypeFull
,
434 function_options
, sc_list
);
435 for (const SymbolContext
&sc
: sc_list
) {
437 lldb_private::LineEntry line_entry
;
438 if (sc
.function
->GetAddressRange()
440 .CalculateSymbolContextLineEntry(line_entry
)) {
441 SetDefaultFileAndLine(line_entry
.file_sp
, line_entry
.line
);
442 return SupportFileAndLine(line_entry
.file_sp
, m_last_line
);
453 void SourceManager::FindLinesMatchingRegex(SupportFileSP support_file_sp
,
454 RegularExpression
®ex
,
457 std::vector
<uint32_t> &match_lines
) {
459 FileSP file_sp
= GetFile(support_file_sp
);
462 return file_sp
->FindLinesMatchingRegex(regex
, start_line
, end_line
,
466 SourceManager::File::File(SupportFileSP support_file_sp
,
467 lldb::DebuggerSP debugger_sp
)
468 : m_support_file_sp(std::make_shared
<SupportFile
>()), m_checksum(),
469 m_mod_time(), m_debugger_wp(debugger_sp
), m_target_wp(TargetSP()) {
470 CommonInitializer(support_file_sp
, {});
473 SourceManager::File::File(SupportFileSP support_file_sp
, TargetSP target_sp
)
474 : m_support_file_sp(std::make_shared
<SupportFile
>()), m_checksum(),
476 m_debugger_wp(target_sp
? target_sp
->GetDebugger().shared_from_this()
478 m_target_wp(target_sp
) {
479 CommonInitializer(support_file_sp
, target_sp
);
482 void SourceManager::File::CommonInitializer(SupportFileSP support_file_sp
,
483 TargetSP target_sp
) {
484 // Set the file and update the modification time.
485 SetSupportFile(support_file_sp
);
487 // Always update the source map modification ID if we have a target.
489 m_source_map_mod_id
= target_sp
->GetSourcePathMap().GetModificationID();
491 // File doesn't exist.
492 if (m_mod_time
== llvm::sys::TimePoint
<>()) {
494 // If this is just a file name, try finding it in the target.
496 FileSpec file_spec
= support_file_sp
->GetSpecOnly();
497 if (!file_spec
.GetDirectory() && file_spec
.GetFilename()) {
498 bool check_inlines
= false;
499 SymbolContextList sc_list
;
501 target_sp
->GetImages().ResolveSymbolContextForFilePath(
502 file_spec
.GetFilename().AsCString(), 0, check_inlines
,
503 SymbolContextItem(eSymbolContextModule
|
504 eSymbolContextCompUnit
),
506 bool got_multiple
= false;
507 if (num_matches
!= 0) {
508 if (num_matches
> 1) {
509 CompileUnit
*test_cu
= nullptr;
510 for (const SymbolContext
&sc
: sc_list
) {
513 if (test_cu
!= sc
.comp_unit
)
517 test_cu
= sc
.comp_unit
;
523 sc_list
.GetContextAtIndex(0, sc
);
525 SetSupportFile(sc
.comp_unit
->GetPrimarySupportFile());
531 // Try remapping the file if it doesn't exist.
533 FileSpec file_spec
= support_file_sp
->GetSpecOnly();
534 if (!FileSystem::Instance().Exists(file_spec
)) {
535 // Check target specific source remappings (i.e., the
536 // target.source-map setting), then fall back to the module
537 // specific remapping (i.e., the .dSYM remapping dictionary).
538 auto remapped
= target_sp
->GetSourcePathMap().FindFile(file_spec
);
541 if (target_sp
->GetImages().FindSourceFile(file_spec
, new_spec
))
545 SetSupportFile(std::make_shared
<SupportFile
>(
546 *remapped
, support_file_sp
->GetChecksum()));
552 // If the file exists, read in the data.
553 if (m_mod_time
!= llvm::sys::TimePoint
<>()) {
554 m_data_sp
= FileSystem::Instance().CreateDataBuffer(
555 m_support_file_sp
->GetSpecOnly());
556 m_checksum
= llvm::MD5::hash(m_data_sp
->GetData());
560 void SourceManager::File::SetSupportFile(lldb::SupportFileSP support_file_sp
) {
561 FileSpec file_spec
= support_file_sp
->GetSpecOnly();
562 resolve_tilde(file_spec
);
564 std::make_shared
<SupportFile
>(file_spec
, support_file_sp
->GetChecksum());
565 m_mod_time
= FileSystem::Instance().GetModificationTime(file_spec
);
568 uint32_t SourceManager::File::GetLineOffset(uint32_t line
) {
575 if (CalculateLineOffsets(line
)) {
576 if (line
< m_offsets
.size())
577 return m_offsets
[line
- 1]; // yes we want "line - 1" in the index
582 uint32_t SourceManager::File::GetNumLines() {
583 CalculateLineOffsets();
584 return m_offsets
.size();
587 const char *SourceManager::File::PeekLineData(uint32_t line
) {
588 if (!LineIsValid(line
))
591 size_t line_offset
= GetLineOffset(line
);
592 if (line_offset
< m_data_sp
->GetByteSize())
593 return (const char *)m_data_sp
->GetBytes() + line_offset
;
597 uint32_t SourceManager::File::GetLineLength(uint32_t line
,
598 bool include_newline_chars
) {
599 if (!LineIsValid(line
))
602 size_t start_offset
= GetLineOffset(line
);
603 size_t end_offset
= GetLineOffset(line
+ 1);
604 if (end_offset
== UINT32_MAX
)
605 end_offset
= m_data_sp
->GetByteSize();
607 if (end_offset
> start_offset
) {
608 uint32_t length
= end_offset
- start_offset
;
609 if (!include_newline_chars
) {
610 const char *line_start
=
611 (const char *)m_data_sp
->GetBytes() + start_offset
;
613 const char last_char
= line_start
[length
- 1];
614 if ((last_char
== '\r') || (last_char
== '\n'))
625 bool SourceManager::File::LineIsValid(uint32_t line
) {
629 if (CalculateLineOffsets(line
))
630 return line
< m_offsets
.size();
634 bool SourceManager::File::ModificationTimeIsStale() const {
635 // TODO: use host API to sign up for file modifications to anything in our
636 // source cache and only update when we determine a file has been updated.
637 // For now we check each time we want to display info for the file.
638 auto curr_mod_time
= FileSystem::Instance().GetModificationTime(
639 m_support_file_sp
->GetSpecOnly());
640 return curr_mod_time
!= llvm::sys::TimePoint
<>() &&
641 m_mod_time
!= curr_mod_time
;
644 bool SourceManager::File::PathRemappingIsStale() const {
645 if (TargetSP target_sp
= m_target_wp
.lock())
646 return GetSourceMapModificationID() !=
647 target_sp
->GetSourcePathMap().GetModificationID();
651 size_t SourceManager::File::DisplaySourceLines(uint32_t line
,
652 std::optional
<size_t> column
,
653 uint32_t context_before
,
654 uint32_t context_after
,
656 // Nothing to write if there's no stream.
660 // Sanity check m_data_sp before proceeding.
664 size_t bytes_written
= s
->GetWrittenBytes();
666 auto debugger_sp
= m_debugger_wp
.lock();
668 HighlightStyle style
;
669 // Use the default Vim style if source highlighting is enabled.
670 if (should_highlight_source(debugger_sp
))
671 style
= HighlightStyle::MakeVimStyle();
673 // If we should mark the stop column with color codes, then copy the prefix
674 // and suffix to our color style.
675 if (should_show_stop_column_with_ansi(debugger_sp
))
676 style
.selected
.Set(debugger_sp
->GetStopShowColumnAnsiPrefix(),
677 debugger_sp
->GetStopShowColumnAnsiSuffix());
679 HighlighterManager mgr
;
681 GetSupportFile()->GetSpecOnly().GetPath(/*denormalize*/ false);
682 // FIXME: Find a way to get the definitive language this file was written in
683 // and pass it to the highlighter.
684 const auto &h
= mgr
.getHighlighterFor(lldb::eLanguageTypeUnknown
, path
);
686 const uint32_t start_line
=
687 line
<= context_before
? 1 : line
- context_before
;
688 const uint32_t start_line_offset
= GetLineOffset(start_line
);
689 if (start_line_offset
!= UINT32_MAX
) {
690 const uint32_t end_line
= line
+ context_after
;
691 uint32_t end_line_offset
= GetLineOffset(end_line
+ 1);
692 if (end_line_offset
== UINT32_MAX
)
693 end_line_offset
= m_data_sp
->GetByteSize();
695 assert(start_line_offset
<= end_line_offset
);
696 if (start_line_offset
< end_line_offset
) {
697 size_t count
= end_line_offset
- start_line_offset
;
698 const uint8_t *cstr
= m_data_sp
->GetBytes() + start_line_offset
;
700 auto ref
= llvm::StringRef(reinterpret_cast<const char *>(cstr
), count
);
702 h
.Highlight(style
, ref
, column
, "", *s
);
704 // Ensure we get an end of line character one way or another.
705 if (!is_newline_char(ref
.back()))
709 return s
->GetWrittenBytes() - bytes_written
;
712 void SourceManager::File::FindLinesMatchingRegex(
713 RegularExpression
®ex
, uint32_t start_line
, uint32_t end_line
,
714 std::vector
<uint32_t> &match_lines
) {
717 if (!LineIsValid(start_line
) ||
718 (end_line
!= UINT32_MAX
&& !LineIsValid(end_line
)))
720 if (start_line
> end_line
)
723 for (uint32_t line_no
= start_line
; line_no
< end_line
; line_no
++) {
725 if (!GetLine(line_no
, buffer
))
727 if (regex
.Execute(buffer
)) {
728 match_lines
.push_back(line_no
);
733 bool lldb_private::operator==(const SourceManager::File
&lhs
,
734 const SourceManager::File
&rhs
) {
735 if (!lhs
.GetSupportFile()->Equal(*rhs
.GetSupportFile(),
736 SupportFile::eEqualChecksumIfSet
))
738 return lhs
.m_mod_time
== rhs
.m_mod_time
;
741 bool SourceManager::File::CalculateLineOffsets(uint32_t line
) {
743 UINT32_MAX
; // TODO: take this line out when we support partial indexing
744 if (line
== UINT32_MAX
) {
746 if (!m_offsets
.empty() && m_offsets
[0] == UINT32_MAX
)
749 if (m_offsets
.empty()) {
750 if (m_data_sp
.get() == nullptr)
753 const char *start
= (const char *)m_data_sp
->GetBytes();
755 const char *end
= start
+ m_data_sp
->GetByteSize();
757 // Calculate all line offsets from scratch
759 // Push a 1 at index zero to indicate the file has been completely
761 m_offsets
.push_back(UINT32_MAX
);
763 for (s
= start
; s
< end
; ++s
) {
765 if (is_newline_char(curr_ch
)) {
768 if (is_newline_char(next_ch
)) {
769 if (curr_ch
!= next_ch
)
773 m_offsets
.push_back(s
+ 1 - start
);
776 if (!m_offsets
.empty()) {
777 if (m_offsets
.back() < size_t(end
- start
))
778 m_offsets
.push_back(end
- start
);
783 // Some lines have been populated, start where we last left off
784 assert("Not implemented yet" && false);
788 // Calculate all line offsets up to "line"
789 assert("Not implemented yet" && false);
794 bool SourceManager::File::GetLine(uint32_t line_no
, std::string
&buffer
) {
795 if (!LineIsValid(line_no
))
798 size_t start_offset
= GetLineOffset(line_no
);
799 size_t end_offset
= GetLineOffset(line_no
+ 1);
800 if (end_offset
== UINT32_MAX
) {
801 end_offset
= m_data_sp
->GetByteSize();
803 buffer
.assign((const char *)m_data_sp
->GetBytes() + start_offset
,
804 end_offset
- start_offset
);
809 void SourceManager::SourceFileCache::AddSourceFile(const FileSpec
&file_spec
,
811 llvm::sys::ScopedWriter
guard(m_mutex
);
813 assert(file_sp
&& "invalid FileSP");
815 AddSourceFileImpl(file_spec
, file_sp
);
816 const FileSpec
&resolved_file_spec
= file_sp
->GetSupportFile()->GetSpecOnly();
817 if (file_spec
!= resolved_file_spec
)
818 AddSourceFileImpl(file_sp
->GetSupportFile()->GetSpecOnly(), file_sp
);
821 void SourceManager::SourceFileCache::RemoveSourceFile(const FileSP
&file_sp
) {
822 llvm::sys::ScopedWriter
guard(m_mutex
);
824 assert(file_sp
&& "invalid FileSP");
826 // Iterate over all the elements in the cache.
827 // This is expensive but a relatively uncommon operation.
828 auto it
= m_file_cache
.begin();
829 while (it
!= m_file_cache
.end()) {
830 if (it
->second
== file_sp
)
831 it
= m_file_cache
.erase(it
);
837 void SourceManager::SourceFileCache::AddSourceFileImpl(
838 const FileSpec
&file_spec
, FileSP file_sp
) {
839 FileCache::iterator pos
= m_file_cache
.find(file_spec
);
840 if (pos
== m_file_cache
.end()) {
841 m_file_cache
[file_spec
] = file_sp
;
843 if (file_sp
!= pos
->second
)
844 m_file_cache
[file_spec
] = file_sp
;
848 SourceManager::FileSP
SourceManager::SourceFileCache::FindSourceFile(
849 const FileSpec
&file_spec
) const {
850 llvm::sys::ScopedReader
guard(m_mutex
);
852 FileCache::const_iterator pos
= m_file_cache
.find(file_spec
);
853 if (pos
!= m_file_cache
.end())
858 void SourceManager::SourceFileCache::Dump(Stream
&stream
) const {
860 stream
<< "Modification time MD5 Checksum (on-disk) MD5 Checksum (line table) Lines Path\n";
861 stream
<< "------------------- -------------------------------- -------------------------------- -------- --------------------------------\n";
863 for (auto &entry
: m_file_cache
) {
866 FileSP file
= entry
.second
;
867 stream
.Format("{0:%Y-%m-%d %H:%M:%S} {1,32} {2,32} {3,8:d} {4}\n",
868 file
->GetTimestamp(), toString(file
->GetChecksum()),
869 toString(file
->GetSupportFile()->GetChecksum()),
870 file
->GetNumLines(), entry
.first
.GetPath());