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 inlines_okay = true;
249             bool append = false;
250             num_matches = executable_ptr->FindFunctions (main_name, NULL, lldb::eFunctionNameTypeBase, inlines_okay, symbols_okay, append, sc_list);
251             for (uint32_t idx = 0; idx < num_matches; idx++)
252             {
253                 SymbolContext sc;
254                 sc_list.GetContextAtIndex(idx, sc);
255                 if (sc.function)
256                 {
257                     lldb_private::LineEntry line_entry;
258                     if (sc.function->GetAddressRange().GetBaseAddress().CalculateSymbolContextLineEntry (line_entry))
259                     {
260                         SetDefaultFileAndLine (line_entry.file,
261                                                line_entry.line);
262                         file_spec = m_last_file_sp->GetFileSpec();
263                         line = m_last_file_line;
264                         return true;
265                     }
266                 }
267             }
268         }
269     }
270     return false;
271 }
272 
273 void
274 SourceManager::FindLinesMatchingRegex (FileSpec &file_spec,
275                                        RegularExpression& regex,
276                                        uint32_t start_line,
277                                        uint32_t end_line,
278                                        std::vector<uint32_t> &match_lines)
279 {
280     match_lines.clear();
281     FileSP file_sp = GetFile (file_spec);
282     if (!file_sp)
283         return;
284     return file_sp->FindLinesMatchingRegex (regex, start_line, end_line, match_lines);
285 }
286 
287 SourceManager::File::File(const FileSpec &file_spec, Target *target) :
288     m_file_spec_orig (file_spec),
289     m_file_spec(file_spec),
290     m_mod_time (file_spec.GetModificationTime()),
291     m_data_sp(),
292     m_offsets()
293 {
294     if (!m_mod_time.IsValid())
295     {
296         if (target)
297         {
298             if (!file_spec.GetDirectory() && file_spec.GetFilename())
299             {
300                 // If this is just a file name, lets see if we can find it in the target:
301                 bool check_inlines = false;
302                 SymbolContextList sc_list;
303                 size_t num_matches = target->GetImages().ResolveSymbolContextForFilePath (file_spec.GetFilename().AsCString(),
304                                                                                           0,
305                                                                                           check_inlines,
306                                                                                           lldb::eSymbolContextModule | lldb::eSymbolContextCompUnit,
307                                                                                           sc_list);
308                 bool got_multiple = false;
309                 if (num_matches != 0)
310                 {
311                     if (num_matches > 1)
312                     {
313                         SymbolContext sc;
314                         FileSpec *test_cu_spec = NULL;
315 
316                         for (unsigned i = 0; i < num_matches; i++)
317                         {
318                             sc_list.GetContextAtIndex(i, sc);
319                             if (sc.comp_unit)
320                             {
321                                 if (test_cu_spec)
322                                 {
323                                     if (test_cu_spec != static_cast<FileSpec *> (sc.comp_unit))
324                                         got_multiple = true;
325                                         break;
326                                 }
327                                 else
328                                     test_cu_spec = sc.comp_unit;
329                             }
330                         }
331                     }
332                     if (!got_multiple)
333                     {
334                         SymbolContext sc;
335                         sc_list.GetContextAtIndex (0, sc);
336                         m_file_spec = sc.comp_unit;
337                         m_mod_time = m_file_spec.GetModificationTime();
338                     }
339                 }
340             }
341             // Try remapping if m_file_spec does not correspond to an existing file.
342             if (!m_file_spec.Exists())
343             {
344                 FileSpec new_file_spec;
345                 // Check target specific source remappings first, then fall back to
346                 // modules objects can have individual path remappings that were detected
347                 // when the debug info for a module was found.
348                 // then
349                 if (target->GetSourcePathMap().FindFile (m_file_spec, new_file_spec) ||
350                     target->GetImages().FindSourceFile (m_file_spec, new_file_spec))
351                 {
352                     m_file_spec = new_file_spec;
353                     m_mod_time = m_file_spec.GetModificationTime();
354                 }
355             }
356         }
357     }
358 
359     if (m_mod_time.IsValid())
360         m_data_sp = m_file_spec.ReadFileContents ();
361 }
362 
363 SourceManager::File::~File()
364 {
365 }
366 
367 uint32_t
368 SourceManager::File::GetLineOffset (uint32_t line)
369 {
370     if (line == 0)
371         return UINT32_MAX;
372 
373     if (line == 1)
374         return 0;
375 
376     if (CalculateLineOffsets (line))
377     {
378         if (line < m_offsets.size())
379             return m_offsets[line - 1]; // yes we want "line - 1" in the index
380     }
381     return UINT32_MAX;
382 }
383 
384 bool
385 SourceManager::File::LineIsValid (uint32_t line)
386 {
387     if (line == 0)
388         return false;
389 
390     if (CalculateLineOffsets (line))
391         return line < m_offsets.size();
392     return false;
393 }
394 
395 size_t
396 SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s)
397 {
398     // TODO: use host API to sign up for file modifications to anything in our
399     // source cache and only update when we determine a file has been updated.
400     // For now we check each time we want to display info for the file.
401     TimeValue curr_mod_time (m_file_spec.GetModificationTime());
402 
403     if (curr_mod_time.IsValid() && m_mod_time != curr_mod_time)
404     {
405         m_mod_time = curr_mod_time;
406         m_data_sp = m_file_spec.ReadFileContents ();
407         m_offsets.clear();
408     }
409 
410     // Sanity check m_data_sp before proceeding.
411     if (!m_data_sp)
412         return 0;
413 
414     const uint32_t start_line = line <= context_before ? 1 : line - context_before;
415     const uint32_t start_line_offset = GetLineOffset (start_line);
416     if (start_line_offset != UINT32_MAX)
417     {
418         const uint32_t end_line = line + context_after;
419         uint32_t end_line_offset = GetLineOffset (end_line + 1);
420         if (end_line_offset == UINT32_MAX)
421             end_line_offset = m_data_sp->GetByteSize();
422 
423         assert (start_line_offset <= end_line_offset);
424         size_t bytes_written = 0;
425         if (start_line_offset < end_line_offset)
426         {
427             size_t count = end_line_offset - start_line_offset;
428             const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
429             bytes_written = s->Write(cstr, count);
430             if (!is_newline_char(cstr[count-1]))
431                 bytes_written += s->EOL();
432         }
433         return bytes_written;
434     }
435     return 0;
436 }
437 
438 void
439 SourceManager::File::FindLinesMatchingRegex (RegularExpression& regex, uint32_t start_line, uint32_t end_line, std::vector<uint32_t> &match_lines)
440 {
441     TimeValue curr_mod_time (m_file_spec.GetModificationTime());
442     if (m_mod_time != curr_mod_time)
443     {
444         m_mod_time = curr_mod_time;
445         m_data_sp = m_file_spec.ReadFileContents ();
446         m_offsets.clear();
447     }
448 
449     match_lines.clear();
450 
451     if (!LineIsValid(start_line) || (end_line != UINT32_MAX && !LineIsValid(end_line)))
452         return;
453     if (start_line > end_line)
454         return;
455 
456     for (uint32_t line_no = start_line; line_no < end_line; line_no++)
457     {
458         std::string buffer;
459         if (!GetLine (line_no, buffer))
460             break;
461         if (regex.Execute(buffer.c_str()))
462         {
463             match_lines.push_back(line_no);
464         }
465     }
466 }
467 
468 bool
469 SourceManager::File::FileSpecMatches (const FileSpec &file_spec)
470 {
471     return FileSpec::Equal (m_file_spec, file_spec, false);
472 }
473 
474 bool
475 lldb_private::operator== (const SourceManager::File &lhs, const SourceManager::File &rhs)
476 {
477     if (lhs.m_file_spec == rhs.m_file_spec)
478     {
479         if (lhs.m_mod_time.IsValid())
480         {
481             if (rhs.m_mod_time.IsValid())
482                 return lhs.m_mod_time == rhs.m_mod_time;
483             else
484                 return false;
485         }
486         else if (rhs.m_mod_time.IsValid())
487             return false;
488         else
489             return true;
490     }
491     else
492         return false;
493 }
494 
495 bool
496 SourceManager::File::CalculateLineOffsets (uint32_t line)
497 {
498     line = UINT32_MAX;  // TODO: take this line out when we support partial indexing
499     if (line == UINT32_MAX)
500     {
501         // Already done?
502         if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
503             return true;
504 
505         if (m_offsets.empty())
506         {
507             if (m_data_sp.get() == NULL)
508                 return false;
509 
510             const char *start = (char *)m_data_sp->GetBytes();
511             if (start)
512             {
513                 const char *end = start + m_data_sp->GetByteSize();
514 
515                 // Calculate all line offsets from scratch
516 
517                 // Push a 1 at index zero to indicate the file has been completely indexed.
518                 m_offsets.push_back(UINT32_MAX);
519                 register const char *s;
520                 for (s = start; s < end; ++s)
521                 {
522                     register char curr_ch = *s;
523                     if (is_newline_char (curr_ch))
524                     {
525                         register char next_ch = s[1];
526                         if (is_newline_char (next_ch))
527                         {
528                             if (curr_ch != next_ch)
529                                 ++s;
530                         }
531                         m_offsets.push_back(s + 1 - start);
532                     }
533                 }
534                 if (!m_offsets.empty())
535                 {
536                     if (m_offsets.back() < end - start)
537                         m_offsets.push_back(end - start);
538                 }
539                 return true;
540             }
541         }
542         else
543         {
544             // Some lines have been populated, start where we last left off
545             assert(!"Not implemented yet");
546         }
547 
548     }
549     else
550     {
551         // Calculate all line offsets up to "line"
552         assert(!"Not implemented yet");
553     }
554     return false;
555 }
556 
557 bool
558 SourceManager::File::GetLine (uint32_t line_no, std::string &buffer)
559 {
560     if (!LineIsValid(line_no))
561         return false;
562 
563     uint32_t start_offset = GetLineOffset (line_no);
564     uint32_t end_offset = GetLineOffset (line_no + 1);
565     if (end_offset == UINT32_MAX)
566     {
567         end_offset = m_data_sp->GetByteSize();
568     }
569     buffer.assign((char *) m_data_sp->GetBytes() + start_offset, end_offset - start_offset);
570 
571     return true;
572 }
573 
574 void
575 SourceManager::SourceFileCache::AddSourceFile (const FileSP &file_sp)
576 {
577     FileSpec file_spec;
578     FileCache::iterator pos = m_file_cache.find(file_spec);
579     if (pos == m_file_cache.end())
580         m_file_cache[file_spec] = file_sp;
581     else
582     {
583         if (file_sp != pos->second)
584             m_file_cache[file_spec] = file_sp;
585     }
586 }
587 
588 SourceManager::FileSP
589 SourceManager::SourceFileCache::FindSourceFile (const FileSpec &file_spec) const
590 {
591     FileSP file_sp;
592     FileCache::const_iterator pos = m_file_cache.find(file_spec);
593     if (pos != m_file_cache.end())
594         file_sp = pos->second;
595     return file_sp;
596 }
597 
598