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