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