xref: /llvm-project/lldb/source/Core/SourceManager.cpp (revision e4ca515ae1c8cfabe101f20406b56f27dd2f381d)
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