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 
235 SourceManager::File::File(const FileSpec &file_spec, Target *target) :
236     m_file_spec_orig (file_spec),
237     m_file_spec(file_spec),
238     m_mod_time (file_spec.GetModificationTime()),
239     m_data_sp(),
240     m_offsets()
241 {
242     if (!m_mod_time.IsValid())
243     {
244         if (target)
245         {
246             if (!file_spec.GetDirectory() && file_spec.GetFilename())
247             {
248                 // If this is just a file name, lets see if we can find it in the target:
249                 bool check_inlines = false;
250                 SymbolContextList sc_list;
251                 size_t num_matches = target->GetImages().ResolveSymbolContextForFilePath (file_spec.GetFilename().AsCString(),
252                                                                                           0,
253                                                                                           check_inlines,
254                                                                                           lldb::eSymbolContextModule | lldb::eSymbolContextCompUnit,
255                                                                                           sc_list);
256                 bool got_multiple = false;
257                 if (num_matches != 0)
258                 {
259                     if (num_matches > 1)
260                     {
261                         SymbolContext sc;
262                         FileSpec *test_cu_spec = NULL;
263 
264                         for (unsigned i = 0; i < num_matches; i++)
265                         {
266                             sc_list.GetContextAtIndex(i, sc);
267                             if (sc.comp_unit)
268                             {
269                                 if (test_cu_spec)
270                                 {
271                                     if (test_cu_spec != static_cast<FileSpec *> (sc.comp_unit))
272                                         got_multiple = true;
273                                         break;
274                                 }
275                                 else
276                                     test_cu_spec = sc.comp_unit;
277                             }
278                         }
279                     }
280                     if (!got_multiple)
281                     {
282                         SymbolContext sc;
283                         sc_list.GetContextAtIndex (0, sc);
284                         m_file_spec = static_cast<FileSpec *>(sc.comp_unit);
285                         m_mod_time = m_file_spec.GetModificationTime();
286                     }
287                 }
288             }
289             else
290             {
291                 if (target->GetSourcePathMap().RemapPath(file_spec.GetDirectory(), m_file_spec.GetDirectory()))
292                    m_mod_time = file_spec.GetModificationTime();
293             }
294         }
295     }
296 
297     if (m_mod_time.IsValid())
298         m_data_sp = m_file_spec.ReadFileContents ();
299 }
300 
301 SourceManager::File::~File()
302 {
303 }
304 
305 uint32_t
306 SourceManager::File::GetLineOffset (uint32_t line)
307 {
308     if (line == 0)
309         return UINT32_MAX;
310 
311     if (line == 1)
312         return 0;
313 
314     if (CalculateLineOffsets (line))
315     {
316         if (line < m_offsets.size())
317             return m_offsets[line - 1]; // yes we want "line - 1" in the index
318     }
319     return UINT32_MAX;
320 }
321 
322 bool
323 SourceManager::File::LineIsValid (uint32_t line)
324 {
325     if (line == 0)
326         return false;
327 
328     if (CalculateLineOffsets (line))
329         return line < m_offsets.size();
330     return false;
331 }
332 
333 size_t
334 SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s)
335 {
336     // TODO: use host API to sign up for file modifications to anything in our
337     // source cache and only update when we determine a file has been updated.
338     // For now we check each time we want to display info for the file.
339     TimeValue curr_mod_time (m_file_spec.GetModificationTime());
340     if (m_mod_time != curr_mod_time)
341     {
342         m_mod_time = curr_mod_time;
343         m_data_sp = m_file_spec.ReadFileContents ();
344         m_offsets.clear();
345     }
346 
347     const uint32_t start_line = line <= context_before ? 1 : line - context_before;
348     const uint32_t start_line_offset = GetLineOffset (start_line);
349     if (start_line_offset != UINT32_MAX)
350     {
351         const uint32_t end_line = line + context_after;
352         uint32_t end_line_offset = GetLineOffset (end_line + 1);
353         if (end_line_offset == UINT32_MAX)
354             end_line_offset = m_data_sp->GetByteSize();
355 
356         assert (start_line_offset <= end_line_offset);
357         size_t bytes_written = 0;
358         if (start_line_offset < end_line_offset)
359         {
360             size_t count = end_line_offset - start_line_offset;
361             const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
362             bytes_written = s->Write(cstr, count);
363             if (!is_newline_char(cstr[count-1]))
364                 bytes_written += s->EOL();
365         }
366         return bytes_written;
367     }
368     return 0;
369 }
370 
371 bool
372 SourceManager::File::FileSpecMatches (const FileSpec &file_spec)
373 {
374     return FileSpec::Equal (m_file_spec, file_spec, false);
375 }
376 
377 bool
378 lldb_private::operator== (const SourceManager::File &lhs, const SourceManager::File &rhs)
379 {
380     if (lhs.m_file_spec == rhs.m_file_spec)
381     {
382         if (lhs.m_mod_time.IsValid())
383         {
384             if (rhs.m_mod_time.IsValid())
385                 return lhs.m_mod_time == rhs.m_mod_time;
386             else
387                 return false;
388         }
389         else if (rhs.m_mod_time.IsValid())
390             return false;
391         else
392             return true;
393     }
394     else
395         return false;
396 }
397 
398 bool
399 SourceManager::File::CalculateLineOffsets (uint32_t line)
400 {
401     line = UINT32_MAX;  // TODO: take this line out when we support partial indexing
402     if (line == UINT32_MAX)
403     {
404         // Already done?
405         if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
406             return true;
407 
408         if (m_offsets.empty())
409         {
410             if (m_data_sp.get() == NULL)
411                 return false;
412 
413             const char *start = (char *)m_data_sp->GetBytes();
414             if (start)
415             {
416                 const char *end = start + m_data_sp->GetByteSize();
417 
418                 // Calculate all line offsets from scratch
419 
420                 // Push a 1 at index zero to indicate the file has been completely indexed.
421                 m_offsets.push_back(UINT32_MAX);
422                 register const char *s;
423                 for (s = start; s < end; ++s)
424                 {
425                     register char curr_ch = *s;
426                     if (is_newline_char (curr_ch))
427                     {
428                         register char next_ch = s[1];
429                         if (is_newline_char (next_ch))
430                         {
431                             if (curr_ch != next_ch)
432                                 ++s;
433                         }
434                         m_offsets.push_back(s + 1 - start);
435                     }
436                 }
437                 if (!m_offsets.empty())
438                 {
439                     if (m_offsets.back() < end - start)
440                         m_offsets.push_back(end - start);
441                 }
442                 return true;
443             }
444         }
445         else
446         {
447             // Some lines have been populated, start where we last left off
448             assert(!"Not implemented yet");
449         }
450 
451     }
452     else
453     {
454         // Calculate all line offsets up to "line"
455         assert(!"Not implemented yet");
456     }
457     return false;
458 }
459 
460 void
461 SourceManager::SourceFileCache::AddSourceFile (const FileSP &file_sp)
462 {
463     FileSpec file_spec;
464     FileCache::iterator pos = m_file_cache.find(file_spec);
465     if (pos == m_file_cache.end())
466         m_file_cache[file_spec] = file_sp;
467     else
468     {
469         if (file_sp != pos->second)
470             m_file_cache[file_spec] = file_sp;
471     }
472 }
473 
474 SourceManager::FileSP
475 SourceManager::SourceFileCache::FindSourceFile (const FileSpec &file_spec) const
476 {
477     FileSP file_sp;
478     FileCache::const_iterator pos = m_file_cache.find(file_spec);
479     if (pos != m_file_cache.end())
480         file_sp = pos->second;
481     return file_sp;
482 }
483 
484