Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Core/SourceManager.cpp
39587 views
1
//===-- SourceManager.cpp -------------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "lldb/Core/SourceManager.h"
10
11
#include "lldb/Core/Address.h"
12
#include "lldb/Core/AddressRange.h"
13
#include "lldb/Core/Debugger.h"
14
#include "lldb/Core/FormatEntity.h"
15
#include "lldb/Core/Highlighter.h"
16
#include "lldb/Core/Module.h"
17
#include "lldb/Core/ModuleList.h"
18
#include "lldb/Host/FileSystem.h"
19
#include "lldb/Symbol/CompileUnit.h"
20
#include "lldb/Symbol/Function.h"
21
#include "lldb/Symbol/LineEntry.h"
22
#include "lldb/Symbol/SymbolContext.h"
23
#include "lldb/Target/PathMappingList.h"
24
#include "lldb/Target/Process.h"
25
#include "lldb/Target/Target.h"
26
#include "lldb/Utility/AnsiTerminal.h"
27
#include "lldb/Utility/ConstString.h"
28
#include "lldb/Utility/DataBuffer.h"
29
#include "lldb/Utility/LLDBLog.h"
30
#include "lldb/Utility/Log.h"
31
#include "lldb/Utility/RegularExpression.h"
32
#include "lldb/Utility/Stream.h"
33
#include "lldb/lldb-enumerations.h"
34
35
#include "llvm/ADT/Twine.h"
36
37
#include <memory>
38
#include <optional>
39
#include <utility>
40
41
#include <cassert>
42
#include <cstdio>
43
44
namespace lldb_private {
45
class ExecutionContext;
46
}
47
namespace lldb_private {
48
class ValueObject;
49
}
50
51
using namespace lldb;
52
using namespace lldb_private;
53
54
static inline bool is_newline_char(char ch) { return ch == '\n' || ch == '\r'; }
55
56
static void resolve_tilde(FileSpec &file_spec) {
57
if (!FileSystem::Instance().Exists(file_spec) &&
58
file_spec.GetDirectory() &&
59
file_spec.GetDirectory().GetCString()[0] == '~') {
60
FileSystem::Instance().Resolve(file_spec);
61
}
62
}
63
64
// SourceManager constructor
65
SourceManager::SourceManager(const TargetSP &target_sp)
66
: m_last_line(0), m_last_count(0), m_default_set(false),
67
m_target_wp(target_sp),
68
m_debugger_wp(target_sp->GetDebugger().shared_from_this()) {}
69
70
SourceManager::SourceManager(const DebuggerSP &debugger_sp)
71
: m_last_line(0), m_last_count(0), m_default_set(false), m_target_wp(),
72
m_debugger_wp(debugger_sp) {}
73
74
// Destructor
75
SourceManager::~SourceManager() = default;
76
77
SourceManager::FileSP SourceManager::GetFile(const FileSpec &file_spec) {
78
if (!file_spec)
79
return {};
80
81
Log *log = GetLog(LLDBLog::Source);
82
83
DebuggerSP debugger_sp(m_debugger_wp.lock());
84
TargetSP target_sp(m_target_wp.lock());
85
86
if (!debugger_sp || !debugger_sp->GetUseSourceCache()) {
87
LLDB_LOG(log, "Source file caching disabled: creating new source file: {0}",
88
file_spec);
89
if (target_sp)
90
return std::make_shared<File>(file_spec, target_sp);
91
return std::make_shared<File>(file_spec, debugger_sp);
92
}
93
94
ProcessSP process_sp = target_sp ? target_sp->GetProcessSP() : ProcessSP();
95
96
// Check the process source cache first. This is the fast path which avoids
97
// touching the file system unless the path remapping has changed.
98
if (process_sp) {
99
if (FileSP file_sp =
100
process_sp->GetSourceFileCache().FindSourceFile(file_spec)) {
101
LLDB_LOG(log, "Found source file in the process cache: {0}", file_spec);
102
if (file_sp->PathRemappingIsStale()) {
103
LLDB_LOG(log, "Path remapping is stale: removing file from caches: {0}",
104
file_spec);
105
106
// Remove the file from the debugger and process cache. Otherwise we'll
107
// hit the same issue again below when querying the debugger cache.
108
debugger_sp->GetSourceFileCache().RemoveSourceFile(file_sp);
109
process_sp->GetSourceFileCache().RemoveSourceFile(file_sp);
110
111
file_sp.reset();
112
} else {
113
return file_sp;
114
}
115
}
116
}
117
118
// Cache miss in the process cache. Check the debugger source cache.
119
FileSP file_sp = debugger_sp->GetSourceFileCache().FindSourceFile(file_spec);
120
121
// We found the file in the debugger cache. Check if anything invalidated our
122
// cache result.
123
if (file_sp)
124
LLDB_LOG(log, "Found source file in the debugger cache: {0}", file_spec);
125
126
// Check if the path remapping has changed.
127
if (file_sp && file_sp->PathRemappingIsStale()) {
128
LLDB_LOG(log, "Path remapping is stale: {0}", file_spec);
129
file_sp.reset();
130
}
131
132
// Check if the modification time has changed.
133
if (file_sp && file_sp->ModificationTimeIsStale()) {
134
LLDB_LOG(log, "Modification time is stale: {0}", file_spec);
135
file_sp.reset();
136
}
137
138
// Check if the file exists on disk.
139
if (file_sp && !FileSystem::Instance().Exists(file_sp->GetFileSpec())) {
140
LLDB_LOG(log, "File doesn't exist on disk: {0}", file_spec);
141
file_sp.reset();
142
}
143
144
// If at this point we don't have a valid file, it means we either didn't find
145
// it in the debugger cache or something caused it to be invalidated.
146
if (!file_sp) {
147
LLDB_LOG(log, "Creating and caching new source file: {0}", file_spec);
148
149
// (Re)create the file.
150
if (target_sp)
151
file_sp = std::make_shared<File>(file_spec, target_sp);
152
else
153
file_sp = std::make_shared<File>(file_spec, debugger_sp);
154
155
// Add the file to the debugger and process cache. If the file was
156
// invalidated, this will overwrite it.
157
debugger_sp->GetSourceFileCache().AddSourceFile(file_spec, file_sp);
158
if (process_sp)
159
process_sp->GetSourceFileCache().AddSourceFile(file_spec, file_sp);
160
}
161
162
return file_sp;
163
}
164
165
static bool should_highlight_source(DebuggerSP debugger_sp) {
166
if (!debugger_sp)
167
return false;
168
169
// We don't use ANSI stop column formatting if the debugger doesn't think it
170
// should be using color.
171
if (!debugger_sp->GetUseColor())
172
return false;
173
174
return debugger_sp->GetHighlightSource();
175
}
176
177
static bool should_show_stop_column_with_ansi(DebuggerSP debugger_sp) {
178
// We don't use ANSI stop column formatting if we can't lookup values from
179
// the debugger.
180
if (!debugger_sp)
181
return false;
182
183
// We don't use ANSI stop column formatting if the debugger doesn't think it
184
// should be using color.
185
if (!debugger_sp->GetUseColor())
186
return false;
187
188
// We only use ANSI stop column formatting if we're either supposed to show
189
// ANSI where available (which we know we have when we get to this point), or
190
// if we're only supposed to use ANSI.
191
const auto value = debugger_sp->GetStopShowColumn();
192
return ((value == eStopShowColumnAnsiOrCaret) ||
193
(value == eStopShowColumnAnsi));
194
}
195
196
static bool should_show_stop_column_with_caret(DebuggerSP debugger_sp) {
197
// We don't use text-based stop column formatting if we can't lookup values
198
// from the debugger.
199
if (!debugger_sp)
200
return false;
201
202
// If we're asked to show the first available of ANSI or caret, then we do
203
// show the caret when ANSI is not available.
204
const auto value = debugger_sp->GetStopShowColumn();
205
if ((value == eStopShowColumnAnsiOrCaret) && !debugger_sp->GetUseColor())
206
return true;
207
208
// The only other time we use caret is if we're explicitly asked to show
209
// caret.
210
return value == eStopShowColumnCaret;
211
}
212
213
static bool should_show_stop_line_with_ansi(DebuggerSP debugger_sp) {
214
return debugger_sp && debugger_sp->GetUseColor();
215
}
216
217
size_t SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile(
218
uint32_t start_line, uint32_t count, uint32_t curr_line, uint32_t column,
219
const char *current_line_cstr, Stream *s,
220
const SymbolContextList *bp_locs) {
221
if (count == 0)
222
return 0;
223
224
Stream::ByteDelta delta(*s);
225
226
if (start_line == 0) {
227
if (m_last_line != 0 && m_last_line != UINT32_MAX)
228
start_line = m_last_line + m_last_count;
229
else
230
start_line = 1;
231
}
232
233
if (!m_default_set) {
234
FileSpec tmp_spec;
235
uint32_t tmp_line;
236
GetDefaultFileAndLine(tmp_spec, tmp_line);
237
}
238
239
m_last_line = start_line;
240
m_last_count = count;
241
242
if (FileSP last_file_sp = GetLastFile()) {
243
const uint32_t end_line = start_line + count - 1;
244
for (uint32_t line = start_line; line <= end_line; ++line) {
245
if (!last_file_sp->LineIsValid(line)) {
246
m_last_line = UINT32_MAX;
247
break;
248
}
249
250
std::string prefix;
251
if (bp_locs) {
252
uint32_t bp_count = bp_locs->NumLineEntriesWithLine(line);
253
254
if (bp_count > 0)
255
prefix = llvm::formatv("[{0}]", bp_count);
256
else
257
prefix = " ";
258
}
259
260
char buffer[3];
261
snprintf(buffer, sizeof(buffer), "%2.2s",
262
(line == curr_line) ? current_line_cstr : "");
263
std::string current_line_highlight(buffer);
264
265
auto debugger_sp = m_debugger_wp.lock();
266
if (should_show_stop_line_with_ansi(debugger_sp)) {
267
current_line_highlight = ansi::FormatAnsiTerminalCodes(
268
(debugger_sp->GetStopShowLineMarkerAnsiPrefix() +
269
current_line_highlight +
270
debugger_sp->GetStopShowLineMarkerAnsiSuffix())
271
.str());
272
}
273
274
s->Printf("%s%s %-4u\t", prefix.c_str(), current_line_highlight.c_str(),
275
line);
276
277
// So far we treated column 0 as a special 'no column value', but
278
// DisplaySourceLines starts counting columns from 0 (and no column is
279
// expressed by passing an empty optional).
280
std::optional<size_t> columnToHighlight;
281
if (line == curr_line && column)
282
columnToHighlight = column - 1;
283
284
size_t this_line_size =
285
last_file_sp->DisplaySourceLines(line, columnToHighlight, 0, 0, s);
286
if (column != 0 && line == curr_line &&
287
should_show_stop_column_with_caret(debugger_sp)) {
288
// Display caret cursor.
289
std::string src_line;
290
last_file_sp->GetLine(line, src_line);
291
s->Printf(" \t");
292
// Insert a space for every non-tab character in the source line.
293
for (size_t i = 0; i + 1 < column && i < src_line.length(); ++i)
294
s->PutChar(src_line[i] == '\t' ? '\t' : ' ');
295
// Now add the caret.
296
s->Printf("^\n");
297
}
298
if (this_line_size == 0) {
299
m_last_line = UINT32_MAX;
300
break;
301
}
302
}
303
}
304
return *delta;
305
}
306
307
size_t SourceManager::DisplaySourceLinesWithLineNumbers(
308
const FileSpec &file_spec, uint32_t line, uint32_t column,
309
uint32_t context_before, uint32_t context_after,
310
const char *current_line_cstr, Stream *s,
311
const SymbolContextList *bp_locs) {
312
FileSP file_sp(GetFile(file_spec));
313
314
uint32_t start_line;
315
uint32_t count = context_before + context_after + 1;
316
if (line > context_before)
317
start_line = line - context_before;
318
else
319
start_line = 1;
320
321
FileSP last_file_sp(GetLastFile());
322
if (last_file_sp.get() != file_sp.get()) {
323
if (line == 0)
324
m_last_line = 0;
325
m_last_file_spec = file_spec;
326
}
327
return DisplaySourceLinesWithLineNumbersUsingLastFile(
328
start_line, count, line, column, current_line_cstr, s, bp_locs);
329
}
330
331
size_t SourceManager::DisplayMoreWithLineNumbers(
332
Stream *s, uint32_t count, bool reverse, const SymbolContextList *bp_locs) {
333
// If we get called before anybody has set a default file and line, then try
334
// to figure it out here.
335
FileSP last_file_sp(GetLastFile());
336
const bool have_default_file_line = last_file_sp && m_last_line > 0;
337
if (!m_default_set) {
338
FileSpec tmp_spec;
339
uint32_t tmp_line;
340
GetDefaultFileAndLine(tmp_spec, tmp_line);
341
}
342
343
if (last_file_sp) {
344
if (m_last_line == UINT32_MAX)
345
return 0;
346
347
if (reverse && m_last_line == 1)
348
return 0;
349
350
if (count > 0)
351
m_last_count = count;
352
else if (m_last_count == 0)
353
m_last_count = 10;
354
355
if (m_last_line > 0) {
356
if (reverse) {
357
// If this is the first time we've done a reverse, then back up one
358
// more time so we end up showing the chunk before the last one we've
359
// shown:
360
if (m_last_line > m_last_count)
361
m_last_line -= m_last_count;
362
else
363
m_last_line = 1;
364
} else if (have_default_file_line)
365
m_last_line += m_last_count;
366
} else
367
m_last_line = 1;
368
369
const uint32_t column = 0;
370
return DisplaySourceLinesWithLineNumbersUsingLastFile(
371
m_last_line, m_last_count, UINT32_MAX, column, "", s, bp_locs);
372
}
373
return 0;
374
}
375
376
bool SourceManager::SetDefaultFileAndLine(const FileSpec &file_spec,
377
uint32_t line) {
378
m_default_set = true;
379
FileSP file_sp(GetFile(file_spec));
380
381
if (file_sp) {
382
m_last_line = line;
383
m_last_file_spec = file_spec;
384
return true;
385
} else {
386
return false;
387
}
388
}
389
390
bool SourceManager::GetDefaultFileAndLine(FileSpec &file_spec, uint32_t &line) {
391
if (FileSP last_file_sp = GetLastFile()) {
392
file_spec = m_last_file_spec;
393
line = m_last_line;
394
return true;
395
} else if (!m_default_set) {
396
TargetSP target_sp(m_target_wp.lock());
397
398
if (target_sp) {
399
// If nobody has set the default file and line then try here. If there's
400
// no executable, then we will try again later when there is one.
401
// Otherwise, if we can't find it we won't look again, somebody will have
402
// to set it (for instance when we stop somewhere...)
403
Module *executable_ptr = target_sp->GetExecutableModulePointer();
404
if (executable_ptr) {
405
SymbolContextList sc_list;
406
ConstString main_name("main");
407
408
ModuleFunctionSearchOptions function_options;
409
function_options.include_symbols =
410
false; // Force it to be a debug symbol.
411
function_options.include_inlines = true;
412
executable_ptr->FindFunctions(main_name, CompilerDeclContext(),
413
lldb::eFunctionNameTypeBase,
414
function_options, sc_list);
415
for (const SymbolContext &sc : sc_list) {
416
if (sc.function) {
417
lldb_private::LineEntry line_entry;
418
if (sc.function->GetAddressRange()
419
.GetBaseAddress()
420
.CalculateSymbolContextLineEntry(line_entry)) {
421
SetDefaultFileAndLine(line_entry.GetFile(), line_entry.line);
422
file_spec = m_last_file_spec;
423
line = m_last_line;
424
return true;
425
}
426
}
427
}
428
}
429
}
430
}
431
return false;
432
}
433
434
void SourceManager::FindLinesMatchingRegex(FileSpec &file_spec,
435
RegularExpression &regex,
436
uint32_t start_line,
437
uint32_t end_line,
438
std::vector<uint32_t> &match_lines) {
439
match_lines.clear();
440
FileSP file_sp = GetFile(file_spec);
441
if (!file_sp)
442
return;
443
return file_sp->FindLinesMatchingRegex(regex, start_line, end_line,
444
match_lines);
445
}
446
447
SourceManager::File::File(const FileSpec &file_spec,
448
lldb::DebuggerSP debugger_sp)
449
: m_file_spec_orig(file_spec), m_file_spec(), m_mod_time(),
450
m_debugger_wp(debugger_sp), m_target_wp(TargetSP()) {
451
CommonInitializer(file_spec, {});
452
}
453
454
SourceManager::File::File(const FileSpec &file_spec, TargetSP target_sp)
455
: m_file_spec_orig(file_spec), m_file_spec(), m_mod_time(),
456
m_debugger_wp(target_sp ? target_sp->GetDebugger().shared_from_this()
457
: DebuggerSP()),
458
m_target_wp(target_sp) {
459
CommonInitializer(file_spec, target_sp);
460
}
461
462
void SourceManager::File::CommonInitializer(const FileSpec &file_spec,
463
TargetSP target_sp) {
464
// Set the file and update the modification time.
465
SetFileSpec(file_spec);
466
467
// Always update the source map modification ID if we have a target.
468
if (target_sp)
469
m_source_map_mod_id = target_sp->GetSourcePathMap().GetModificationID();
470
471
// File doesn't exist.
472
if (m_mod_time == llvm::sys::TimePoint<>()) {
473
if (target_sp) {
474
// If this is just a file name, try finding it in the target.
475
if (!file_spec.GetDirectory() && file_spec.GetFilename()) {
476
bool check_inlines = false;
477
SymbolContextList sc_list;
478
size_t num_matches =
479
target_sp->GetImages().ResolveSymbolContextForFilePath(
480
file_spec.GetFilename().AsCString(), 0, check_inlines,
481
SymbolContextItem(eSymbolContextModule |
482
eSymbolContextCompUnit),
483
sc_list);
484
bool got_multiple = false;
485
if (num_matches != 0) {
486
if (num_matches > 1) {
487
CompileUnit *test_cu = nullptr;
488
for (const SymbolContext &sc : sc_list) {
489
if (sc.comp_unit) {
490
if (test_cu) {
491
if (test_cu != sc.comp_unit)
492
got_multiple = true;
493
break;
494
} else
495
test_cu = sc.comp_unit;
496
}
497
}
498
}
499
if (!got_multiple) {
500
SymbolContext sc;
501
sc_list.GetContextAtIndex(0, sc);
502
if (sc.comp_unit)
503
SetFileSpec(sc.comp_unit->GetPrimaryFile());
504
}
505
}
506
}
507
508
// Try remapping the file if it doesn't exist.
509
if (!FileSystem::Instance().Exists(m_file_spec)) {
510
// Check target specific source remappings (i.e., the
511
// target.source-map setting), then fall back to the module
512
// specific remapping (i.e., the .dSYM remapping dictionary).
513
auto remapped = target_sp->GetSourcePathMap().FindFile(m_file_spec);
514
if (!remapped) {
515
FileSpec new_spec;
516
if (target_sp->GetImages().FindSourceFile(m_file_spec, new_spec))
517
remapped = new_spec;
518
}
519
if (remapped)
520
SetFileSpec(*remapped);
521
}
522
}
523
}
524
525
// If the file exists, read in the data.
526
if (m_mod_time != llvm::sys::TimePoint<>())
527
m_data_sp = FileSystem::Instance().CreateDataBuffer(m_file_spec);
528
}
529
530
void SourceManager::File::SetFileSpec(FileSpec file_spec) {
531
resolve_tilde(file_spec);
532
m_file_spec = std::move(file_spec);
533
m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
534
}
535
536
uint32_t SourceManager::File::GetLineOffset(uint32_t line) {
537
if (line == 0)
538
return UINT32_MAX;
539
540
if (line == 1)
541
return 0;
542
543
if (CalculateLineOffsets(line)) {
544
if (line < m_offsets.size())
545
return m_offsets[line - 1]; // yes we want "line - 1" in the index
546
}
547
return UINT32_MAX;
548
}
549
550
uint32_t SourceManager::File::GetNumLines() {
551
CalculateLineOffsets();
552
return m_offsets.size();
553
}
554
555
const char *SourceManager::File::PeekLineData(uint32_t line) {
556
if (!LineIsValid(line))
557
return nullptr;
558
559
size_t line_offset = GetLineOffset(line);
560
if (line_offset < m_data_sp->GetByteSize())
561
return (const char *)m_data_sp->GetBytes() + line_offset;
562
return nullptr;
563
}
564
565
uint32_t SourceManager::File::GetLineLength(uint32_t line,
566
bool include_newline_chars) {
567
if (!LineIsValid(line))
568
return false;
569
570
size_t start_offset = GetLineOffset(line);
571
size_t end_offset = GetLineOffset(line + 1);
572
if (end_offset == UINT32_MAX)
573
end_offset = m_data_sp->GetByteSize();
574
575
if (end_offset > start_offset) {
576
uint32_t length = end_offset - start_offset;
577
if (!include_newline_chars) {
578
const char *line_start =
579
(const char *)m_data_sp->GetBytes() + start_offset;
580
while (length > 0) {
581
const char last_char = line_start[length - 1];
582
if ((last_char == '\r') || (last_char == '\n'))
583
--length;
584
else
585
break;
586
}
587
}
588
return length;
589
}
590
return 0;
591
}
592
593
bool SourceManager::File::LineIsValid(uint32_t line) {
594
if (line == 0)
595
return false;
596
597
if (CalculateLineOffsets(line))
598
return line < m_offsets.size();
599
return false;
600
}
601
602
bool SourceManager::File::ModificationTimeIsStale() const {
603
// TODO: use host API to sign up for file modifications to anything in our
604
// source cache and only update when we determine a file has been updated.
605
// For now we check each time we want to display info for the file.
606
auto curr_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
607
return curr_mod_time != llvm::sys::TimePoint<>() &&
608
m_mod_time != curr_mod_time;
609
}
610
611
bool SourceManager::File::PathRemappingIsStale() const {
612
if (TargetSP target_sp = m_target_wp.lock())
613
return GetSourceMapModificationID() !=
614
target_sp->GetSourcePathMap().GetModificationID();
615
return false;
616
}
617
618
size_t SourceManager::File::DisplaySourceLines(uint32_t line,
619
std::optional<size_t> column,
620
uint32_t context_before,
621
uint32_t context_after,
622
Stream *s) {
623
// Nothing to write if there's no stream.
624
if (!s)
625
return 0;
626
627
// Sanity check m_data_sp before proceeding.
628
if (!m_data_sp)
629
return 0;
630
631
size_t bytes_written = s->GetWrittenBytes();
632
633
auto debugger_sp = m_debugger_wp.lock();
634
635
HighlightStyle style;
636
// Use the default Vim style if source highlighting is enabled.
637
if (should_highlight_source(debugger_sp))
638
style = HighlightStyle::MakeVimStyle();
639
640
// If we should mark the stop column with color codes, then copy the prefix
641
// and suffix to our color style.
642
if (should_show_stop_column_with_ansi(debugger_sp))
643
style.selected.Set(debugger_sp->GetStopShowColumnAnsiPrefix(),
644
debugger_sp->GetStopShowColumnAnsiSuffix());
645
646
HighlighterManager mgr;
647
std::string path = GetFileSpec().GetPath(/*denormalize*/ false);
648
// FIXME: Find a way to get the definitive language this file was written in
649
// and pass it to the highlighter.
650
const auto &h = mgr.getHighlighterFor(lldb::eLanguageTypeUnknown, path);
651
652
const uint32_t start_line =
653
line <= context_before ? 1 : line - context_before;
654
const uint32_t start_line_offset = GetLineOffset(start_line);
655
if (start_line_offset != UINT32_MAX) {
656
const uint32_t end_line = line + context_after;
657
uint32_t end_line_offset = GetLineOffset(end_line + 1);
658
if (end_line_offset == UINT32_MAX)
659
end_line_offset = m_data_sp->GetByteSize();
660
661
assert(start_line_offset <= end_line_offset);
662
if (start_line_offset < end_line_offset) {
663
size_t count = end_line_offset - start_line_offset;
664
const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
665
666
auto ref = llvm::StringRef(reinterpret_cast<const char *>(cstr), count);
667
668
h.Highlight(style, ref, column, "", *s);
669
670
// Ensure we get an end of line character one way or another.
671
if (!is_newline_char(ref.back()))
672
s->EOL();
673
}
674
}
675
return s->GetWrittenBytes() - bytes_written;
676
}
677
678
void SourceManager::File::FindLinesMatchingRegex(
679
RegularExpression &regex, uint32_t start_line, uint32_t end_line,
680
std::vector<uint32_t> &match_lines) {
681
match_lines.clear();
682
683
if (!LineIsValid(start_line) ||
684
(end_line != UINT32_MAX && !LineIsValid(end_line)))
685
return;
686
if (start_line > end_line)
687
return;
688
689
for (uint32_t line_no = start_line; line_no < end_line; line_no++) {
690
std::string buffer;
691
if (!GetLine(line_no, buffer))
692
break;
693
if (regex.Execute(buffer)) {
694
match_lines.push_back(line_no);
695
}
696
}
697
}
698
699
bool lldb_private::operator==(const SourceManager::File &lhs,
700
const SourceManager::File &rhs) {
701
if (lhs.m_file_spec != rhs.m_file_spec)
702
return false;
703
return lhs.m_mod_time == rhs.m_mod_time;
704
}
705
706
bool SourceManager::File::CalculateLineOffsets(uint32_t line) {
707
line =
708
UINT32_MAX; // TODO: take this line out when we support partial indexing
709
if (line == UINT32_MAX) {
710
// Already done?
711
if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
712
return true;
713
714
if (m_offsets.empty()) {
715
if (m_data_sp.get() == nullptr)
716
return false;
717
718
const char *start = (const char *)m_data_sp->GetBytes();
719
if (start) {
720
const char *end = start + m_data_sp->GetByteSize();
721
722
// Calculate all line offsets from scratch
723
724
// Push a 1 at index zero to indicate the file has been completely
725
// indexed.
726
m_offsets.push_back(UINT32_MAX);
727
const char *s;
728
for (s = start; s < end; ++s) {
729
char curr_ch = *s;
730
if (is_newline_char(curr_ch)) {
731
if (s + 1 < end) {
732
char next_ch = s[1];
733
if (is_newline_char(next_ch)) {
734
if (curr_ch != next_ch)
735
++s;
736
}
737
}
738
m_offsets.push_back(s + 1 - start);
739
}
740
}
741
if (!m_offsets.empty()) {
742
if (m_offsets.back() < size_t(end - start))
743
m_offsets.push_back(end - start);
744
}
745
return true;
746
}
747
} else {
748
// Some lines have been populated, start where we last left off
749
assert("Not implemented yet" && false);
750
}
751
752
} else {
753
// Calculate all line offsets up to "line"
754
assert("Not implemented yet" && false);
755
}
756
return false;
757
}
758
759
bool SourceManager::File::GetLine(uint32_t line_no, std::string &buffer) {
760
if (!LineIsValid(line_no))
761
return false;
762
763
size_t start_offset = GetLineOffset(line_no);
764
size_t end_offset = GetLineOffset(line_no + 1);
765
if (end_offset == UINT32_MAX) {
766
end_offset = m_data_sp->GetByteSize();
767
}
768
buffer.assign((const char *)m_data_sp->GetBytes() + start_offset,
769
end_offset - start_offset);
770
771
return true;
772
}
773
774
void SourceManager::SourceFileCache::AddSourceFile(const FileSpec &file_spec,
775
FileSP file_sp) {
776
llvm::sys::ScopedWriter guard(m_mutex);
777
778
assert(file_sp && "invalid FileSP");
779
780
AddSourceFileImpl(file_spec, file_sp);
781
const FileSpec &resolved_file_spec = file_sp->GetFileSpec();
782
if (file_spec != resolved_file_spec)
783
AddSourceFileImpl(file_sp->GetFileSpec(), file_sp);
784
}
785
786
void SourceManager::SourceFileCache::RemoveSourceFile(const FileSP &file_sp) {
787
llvm::sys::ScopedWriter guard(m_mutex);
788
789
assert(file_sp && "invalid FileSP");
790
791
// Iterate over all the elements in the cache.
792
// This is expensive but a relatively uncommon operation.
793
auto it = m_file_cache.begin();
794
while (it != m_file_cache.end()) {
795
if (it->second == file_sp)
796
it = m_file_cache.erase(it);
797
else
798
it++;
799
}
800
}
801
802
void SourceManager::SourceFileCache::AddSourceFileImpl(
803
const FileSpec &file_spec, FileSP file_sp) {
804
FileCache::iterator pos = m_file_cache.find(file_spec);
805
if (pos == m_file_cache.end()) {
806
m_file_cache[file_spec] = file_sp;
807
} else {
808
if (file_sp != pos->second)
809
m_file_cache[file_spec] = file_sp;
810
}
811
}
812
813
SourceManager::FileSP SourceManager::SourceFileCache::FindSourceFile(
814
const FileSpec &file_spec) const {
815
llvm::sys::ScopedReader guard(m_mutex);
816
817
FileCache::const_iterator pos = m_file_cache.find(file_spec);
818
if (pos != m_file_cache.end())
819
return pos->second;
820
return {};
821
}
822
823
void SourceManager::SourceFileCache::Dump(Stream &stream) const {
824
stream << "Modification time Lines Path\n";
825
stream << "------------------- -------- --------------------------------\n";
826
for (auto &entry : m_file_cache) {
827
if (!entry.second)
828
continue;
829
FileSP file = entry.second;
830
stream.Format("{0:%Y-%m-%d %H:%M:%S} {1,8:d} {2}\n", file->GetTimestamp(),
831
file->GetNumLines(), entry.first.GetPath());
832
}
833
}
834
835