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 = static_cast<FileSpec *>(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                 ConstString new_path;
345                 if (target->GetSourcePathMap().RemapPath(m_file_spec.GetDirectory(), new_path))
346                 {
347                     char resolved_path[PATH_MAX];
348                     ::snprintf(resolved_path, PATH_MAX, "%s/%s", new_path.AsCString(), m_file_spec.GetFilename().AsCString());
349                     m_file_spec = new FileSpec(resolved_path, true);
350                     m_mod_time = m_file_spec.GetModificationTime();
351                 }
352             }
353         }
354     }
355 
356     if (m_mod_time.IsValid())
357         m_data_sp = m_file_spec.ReadFileContents ();
358 }
359 
360 SourceManager::File::~File()
361 {
362 }
363 
364 uint32_t
365 SourceManager::File::GetLineOffset (uint32_t line)
366 {
367     if (line == 0)
368         return UINT32_MAX;
369 
370     if (line == 1)
371         return 0;
372 
373     if (CalculateLineOffsets (line))
374     {
375         if (line < m_offsets.size())
376             return m_offsets[line - 1]; // yes we want "line - 1" in the index
377     }
378     return UINT32_MAX;
379 }
380 
381 bool
382 SourceManager::File::LineIsValid (uint32_t line)
383 {
384     if (line == 0)
385         return false;
386 
387     if (CalculateLineOffsets (line))
388         return line < m_offsets.size();
389     return false;
390 }
391 
392 size_t
393 SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s)
394 {
395     // TODO: use host API to sign up for file modifications to anything in our
396     // source cache and only update when we determine a file has been updated.
397     // For now we check each time we want to display info for the file.
398     TimeValue curr_mod_time (m_file_spec.GetModificationTime());
399 
400     if (curr_mod_time.IsValid() && m_mod_time != curr_mod_time)
401     {
402         m_mod_time = curr_mod_time;
403         m_data_sp = m_file_spec.ReadFileContents ();
404         m_offsets.clear();
405     }
406 
407     // Sanity check m_data_sp before proceeding.
408     if (!m_data_sp)
409         return 0;
410 
411     const uint32_t start_line = line <= context_before ? 1 : line - context_before;
412     const uint32_t start_line_offset = GetLineOffset (start_line);
413     if (start_line_offset != UINT32_MAX)
414     {
415         const uint32_t end_line = line + context_after;
416         uint32_t end_line_offset = GetLineOffset (end_line + 1);
417         if (end_line_offset == UINT32_MAX)
418             end_line_offset = m_data_sp->GetByteSize();
419 
420         assert (start_line_offset <= end_line_offset);
421         size_t bytes_written = 0;
422         if (start_line_offset < end_line_offset)
423         {
424             size_t count = end_line_offset - start_line_offset;
425             const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
426             bytes_written = s->Write(cstr, count);
427             if (!is_newline_char(cstr[count-1]))
428                 bytes_written += s->EOL();
429         }
430         return bytes_written;
431     }
432     return 0;
433 }
434 
435 void
436 SourceManager::File::FindLinesMatchingRegex (RegularExpression& regex, uint32_t start_line, uint32_t end_line, std::vector<uint32_t> &match_lines)
437 {
438     TimeValue curr_mod_time (m_file_spec.GetModificationTime());
439     if (m_mod_time != curr_mod_time)
440     {
441         m_mod_time = curr_mod_time;
442         m_data_sp = m_file_spec.ReadFileContents ();
443         m_offsets.clear();
444     }
445 
446     match_lines.clear();
447 
448     if (!LineIsValid(start_line) || (end_line != UINT32_MAX && !LineIsValid(end_line)))
449         return;
450     if (start_line > end_line)
451         return;
452 
453     for (uint32_t line_no = start_line; line_no < end_line; line_no++)
454     {
455         std::string buffer;
456         if (!GetLine (line_no, buffer))
457             break;
458         if (regex.Execute(buffer.c_str()))
459         {
460             match_lines.push_back(line_no);
461         }
462     }
463 }
464 
465 bool
466 SourceManager::File::FileSpecMatches (const FileSpec &file_spec)
467 {
468     return FileSpec::Equal (m_file_spec, file_spec, false);
469 }
470 
471 bool
472 lldb_private::operator== (const SourceManager::File &lhs, const SourceManager::File &rhs)
473 {
474     if (lhs.m_file_spec == rhs.m_file_spec)
475     {
476         if (lhs.m_mod_time.IsValid())
477         {
478             if (rhs.m_mod_time.IsValid())
479                 return lhs.m_mod_time == rhs.m_mod_time;
480             else
481                 return false;
482         }
483         else if (rhs.m_mod_time.IsValid())
484             return false;
485         else
486             return true;
487     }
488     else
489         return false;
490 }
491 
492 bool
493 SourceManager::File::CalculateLineOffsets (uint32_t line)
494 {
495     line = UINT32_MAX;  // TODO: take this line out when we support partial indexing
496     if (line == UINT32_MAX)
497     {
498         // Already done?
499         if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
500             return true;
501 
502         if (m_offsets.empty())
503         {
504             if (m_data_sp.get() == NULL)
505                 return false;
506 
507             const char *start = (char *)m_data_sp->GetBytes();
508             if (start)
509             {
510                 const char *end = start + m_data_sp->GetByteSize();
511 
512                 // Calculate all line offsets from scratch
513 
514                 // Push a 1 at index zero to indicate the file has been completely indexed.
515                 m_offsets.push_back(UINT32_MAX);
516                 register const char *s;
517                 for (s = start; s < end; ++s)
518                 {
519                     register char curr_ch = *s;
520                     if (is_newline_char (curr_ch))
521                     {
522                         register char next_ch = s[1];
523                         if (is_newline_char (next_ch))
524                         {
525                             if (curr_ch != next_ch)
526                                 ++s;
527                         }
528                         m_offsets.push_back(s + 1 - start);
529                     }
530                 }
531                 if (!m_offsets.empty())
532                 {
533                     if (m_offsets.back() < end - start)
534                         m_offsets.push_back(end - start);
535                 }
536                 return true;
537             }
538         }
539         else
540         {
541             // Some lines have been populated, start where we last left off
542             assert(!"Not implemented yet");
543         }
544 
545     }
546     else
547     {
548         // Calculate all line offsets up to "line"
549         assert(!"Not implemented yet");
550     }
551     return false;
552 }
553 
554 bool
555 SourceManager::File::GetLine (uint32_t line_no, std::string &buffer)
556 {
557     if (!LineIsValid(line_no))
558         return false;
559 
560     uint32_t start_offset = GetLineOffset (line_no);
561     uint32_t end_offset = GetLineOffset (line_no + 1);
562     if (end_offset == UINT32_MAX)
563     {
564         end_offset = m_data_sp->GetByteSize();
565     }
566     buffer.assign((char *) m_data_sp->GetBytes() + start_offset, end_offset - start_offset);
567 
568     return true;
569 }
570 
571 void
572 SourceManager::SourceFileCache::AddSourceFile (const FileSP &file_sp)
573 {
574     FileSpec file_spec;
575     FileCache::iterator pos = m_file_cache.find(file_spec);
576     if (pos == m_file_cache.end())
577         m_file_cache[file_spec] = file_sp;
578     else
579     {
580         if (file_sp != pos->second)
581             m_file_cache[file_spec] = file_sp;
582     }
583 }
584 
585 SourceManager::FileSP
586 SourceManager::SourceFileCache::FindSourceFile (const FileSpec &file_spec) const
587 {
588     FileSP file_sp;
589     FileCache::const_iterator pos = m_file_cache.find(file_spec);
590     if (pos != m_file_cache.end())
591         file_sp = pos->second;
592     return file_sp;
593 }
594 
595