1 | //===-- LineEntry.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/Symbol/LineEntry.h" |
10 | #include "lldb/Symbol/CompileUnit.h" |
11 | #include "lldb/Target/Process.h" |
12 | #include "lldb/Target/Target.h" |
13 | |
14 | using namespace lldb_private; |
15 | |
16 | LineEntry::LineEntry() |
17 | : range(), file_sp(std::make_shared<SupportFile>()), |
18 | original_file_sp(std::make_shared<SupportFile>()), |
19 | is_start_of_statement(0), is_start_of_basic_block(0), is_prologue_end(0), |
20 | is_epilogue_begin(0), is_terminal_entry(0) {} |
21 | |
22 | void LineEntry::Clear() { |
23 | range.Clear(); |
24 | file_sp = std::make_shared<SupportFile>(); |
25 | original_file_sp = std::make_shared<SupportFile>(); |
26 | line = LLDB_INVALID_LINE_NUMBER; |
27 | column = 0; |
28 | is_start_of_statement = 0; |
29 | is_start_of_basic_block = 0; |
30 | is_prologue_end = 0; |
31 | is_epilogue_begin = 0; |
32 | is_terminal_entry = 0; |
33 | } |
34 | |
35 | bool LineEntry::IsValid() const { |
36 | return range.GetBaseAddress().IsValid() && line != LLDB_INVALID_LINE_NUMBER; |
37 | } |
38 | |
39 | bool LineEntry::DumpStopContext(Stream *s, bool show_fullpaths) const { |
40 | const FileSpec &file = file_sp->GetSpecOnly(); |
41 | if (file) { |
42 | if (show_fullpaths) |
43 | file.Dump(s&: s->AsRawOstream()); |
44 | else |
45 | file.GetFilename().Dump(s); |
46 | |
47 | if (line) |
48 | s->PutChar(ch: ':'); |
49 | } |
50 | if (line) { |
51 | s->Printf(format: "%u" , line); |
52 | if (column) { |
53 | s->PutChar(ch: ':'); |
54 | s->Printf(format: "%u" , column); |
55 | } |
56 | } |
57 | return file || line; |
58 | } |
59 | |
60 | bool LineEntry::Dump(Stream *s, Target *target, bool show_file, |
61 | Address::DumpStyle style, |
62 | Address::DumpStyle fallback_style, bool show_range) const { |
63 | if (show_range) { |
64 | // Show address range |
65 | if (!range.Dump(s, target, style, fallback_style)) |
66 | return false; |
67 | } else { |
68 | // Show address only |
69 | if (!range.GetBaseAddress().Dump(s, exe_scope: target, style, fallback_style)) |
70 | return false; |
71 | } |
72 | if (show_file) |
73 | *s << ", file = " << GetFile(); |
74 | if (line) |
75 | s->Printf(format: ", line = %u" , line); |
76 | if (column) |
77 | s->Printf(format: ", column = %u" , column); |
78 | if (is_start_of_statement) |
79 | *s << ", is_start_of_statement = TRUE" ; |
80 | |
81 | if (is_start_of_basic_block) |
82 | *s << ", is_start_of_basic_block = TRUE" ; |
83 | |
84 | if (is_prologue_end) |
85 | *s << ", is_prologue_end = TRUE" ; |
86 | |
87 | if (is_epilogue_begin) |
88 | *s << ", is_epilogue_begin = TRUE" ; |
89 | |
90 | if (is_terminal_entry) |
91 | *s << ", is_terminal_entry = TRUE" ; |
92 | return true; |
93 | } |
94 | |
95 | bool LineEntry::GetDescription(Stream *s, lldb::DescriptionLevel level, |
96 | CompileUnit *cu, Target *target, |
97 | bool show_address_only) const { |
98 | |
99 | if (level == lldb::eDescriptionLevelBrief || |
100 | level == lldb::eDescriptionLevelFull) { |
101 | if (show_address_only) { |
102 | range.GetBaseAddress().Dump(s, exe_scope: target, style: Address::DumpStyleLoadAddress, |
103 | fallback_style: Address::DumpStyleFileAddress); |
104 | } else { |
105 | range.Dump(s, target, style: Address::DumpStyleLoadAddress, |
106 | fallback_style: Address::DumpStyleFileAddress); |
107 | } |
108 | |
109 | *s << ": " << GetFile(); |
110 | |
111 | if (line) { |
112 | s->Printf(format: ":%u" , line); |
113 | if (column) |
114 | s->Printf(format: ":%u" , column); |
115 | } |
116 | |
117 | if (level == lldb::eDescriptionLevelFull) { |
118 | if (is_start_of_statement) |
119 | *s << ", is_start_of_statement = TRUE" ; |
120 | |
121 | if (is_start_of_basic_block) |
122 | *s << ", is_start_of_basic_block = TRUE" ; |
123 | |
124 | if (is_prologue_end) |
125 | *s << ", is_prologue_end = TRUE" ; |
126 | |
127 | if (is_epilogue_begin) |
128 | *s << ", is_epilogue_begin = TRUE" ; |
129 | |
130 | if (is_terminal_entry) |
131 | *s << ", is_terminal_entry = TRUE" ; |
132 | } else { |
133 | if (is_terminal_entry) |
134 | s->EOL(); |
135 | } |
136 | } else { |
137 | return Dump(s, target, show_file: true, style: Address::DumpStyleLoadAddress, |
138 | fallback_style: Address::DumpStyleModuleWithFileAddress, show_range: true); |
139 | } |
140 | return true; |
141 | } |
142 | |
143 | bool lldb_private::operator<(const LineEntry &a, const LineEntry &b) { |
144 | return LineEntry::Compare(lhs: a, rhs: b) < 0; |
145 | } |
146 | |
147 | int LineEntry::Compare(const LineEntry &a, const LineEntry &b) { |
148 | int result = Address::CompareFileAddress(lhs: a.range.GetBaseAddress(), |
149 | rhs: b.range.GetBaseAddress()); |
150 | if (result != 0) |
151 | return result; |
152 | |
153 | const lldb::addr_t a_byte_size = a.range.GetByteSize(); |
154 | const lldb::addr_t b_byte_size = b.range.GetByteSize(); |
155 | |
156 | if (a_byte_size < b_byte_size) |
157 | return -1; |
158 | if (a_byte_size > b_byte_size) |
159 | return +1; |
160 | |
161 | // Check for an end sequence entry mismatch after we have determined that the |
162 | // address values are equal. If one of the items is an end sequence, we don't |
163 | // care about the line, file, or column info. |
164 | if (a.is_terminal_entry > b.is_terminal_entry) |
165 | return -1; |
166 | if (a.is_terminal_entry < b.is_terminal_entry) |
167 | return +1; |
168 | |
169 | if (a.line < b.line) |
170 | return -1; |
171 | if (a.line > b.line) |
172 | return +1; |
173 | |
174 | if (a.column < b.column) |
175 | return -1; |
176 | if (a.column > b.column) |
177 | return +1; |
178 | |
179 | return FileSpec::Compare(lhs: a.GetFile(), rhs: b.GetFile(), full: true); |
180 | } |
181 | |
182 | AddressRange LineEntry::GetSameLineContiguousAddressRange( |
183 | bool include_inlined_functions) const { |
184 | // Add each LineEntry's range to complete_line_range until we find a |
185 | // different file / line number. |
186 | AddressRange complete_line_range = range; |
187 | auto symbol_context_scope = lldb::eSymbolContextLineEntry; |
188 | Declaration start_call_site(original_file_sp->GetSpecOnly(), line); |
189 | if (include_inlined_functions) |
190 | symbol_context_scope |= lldb::eSymbolContextBlock; |
191 | |
192 | while (true) { |
193 | SymbolContext next_line_sc; |
194 | Address range_end(complete_line_range.GetBaseAddress()); |
195 | range_end.Slide(offset: complete_line_range.GetByteSize()); |
196 | range_end.CalculateSymbolContext(sc: &next_line_sc, resolve_scope: symbol_context_scope); |
197 | |
198 | if (!next_line_sc.line_entry.IsValid() || |
199 | next_line_sc.line_entry.range.GetByteSize() == 0) |
200 | break; |
201 | |
202 | if (*original_file_sp == *next_line_sc.line_entry.original_file_sp && |
203 | (next_line_sc.line_entry.line == 0 || |
204 | line == next_line_sc.line_entry.line)) { |
205 | // Include any line 0 entries - they indicate that this is compiler- |
206 | // generated code that does not correspond to user source code. |
207 | // next_line_sc is the same file & line as this LineEntry, so extend |
208 | // our AddressRange by its size and continue to see if there are more |
209 | // LineEntries that we can combine. However, if there was nothing to |
210 | // extend we're done. |
211 | if (!complete_line_range.Extend(rhs_range: next_line_sc.line_entry.range)) |
212 | break; |
213 | continue; |
214 | } |
215 | |
216 | if (include_inlined_functions && next_line_sc.block && |
217 | next_line_sc.block->GetContainingInlinedBlock() != nullptr) { |
218 | // The next_line_sc might be in a different file if it's an inlined |
219 | // function. If this is the case then we still want to expand our line |
220 | // range to include them if the inlined function is at the same call site |
221 | // as this line entry. The current block could represent a nested inline |
222 | // function call so we need to need to check up the block tree to see if |
223 | // we find one. |
224 | auto inlined_parent_block = |
225 | next_line_sc.block->GetContainingInlinedBlockWithCallSite( |
226 | find_call_site: start_call_site); |
227 | if (!inlined_parent_block) |
228 | // We didn't find any parent inlined block with a call site at this line |
229 | // entry so this inlined function is probably at another line. |
230 | break; |
231 | // Extend our AddressRange by the size of the inlined block, but if there |
232 | // was nothing to add then we're done. |
233 | if (!complete_line_range.Extend(rhs_range: next_line_sc.line_entry.range)) |
234 | break; |
235 | continue; |
236 | } |
237 | |
238 | break; |
239 | } |
240 | return complete_line_range; |
241 | } |
242 | |
243 | void LineEntry::ApplyFileMappings(lldb::TargetSP target_sp) { |
244 | if (target_sp) { |
245 | // Apply any file remappings to our file. |
246 | if (auto new_file_spec = target_sp->GetSourcePathMap().FindFile( |
247 | orig_spec: original_file_sp->GetSpecOnly())) |
248 | file_sp->Update(file_spec: *new_file_spec); |
249 | } |
250 | } |
251 | |