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