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