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