1 /**
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "debug_info_cache.h"
17
18 #include "debug_info_extractor.h"
19 #include "optimizer/ir_builder/inst_builder.h"
20 #include "tooling/pt_location.h"
21
22 namespace panda::tooling::inspector {
AddPandaFile(const panda_file::File & file)23 void DebugInfoCache::AddPandaFile(const panda_file::File &file)
24 {
25 os::memory::LockHolder lock(debugInfosMutex_);
26 debugInfos_.emplace(std::piecewise_construct, std::forward_as_tuple(&file),
27 std::forward_as_tuple(file, [this, &file](auto methodId, auto sourceName) {
28 os::memory::LockHolder l(disassembliesMutex_);
29 disassemblies_.emplace(std::piecewise_construct, std::forward_as_tuple(sourceName),
30 std::forward_as_tuple(file, methodId));
31 }));
32 }
33
GetSourceLocation(const PtFrame & frame,std::string_view & sourceFile,std::string_view & methodName,size_t & lineNumber)34 void DebugInfoCache::GetSourceLocation(const PtFrame &frame, std::string_view &sourceFile, std::string_view &methodName,
35 size_t &lineNumber)
36 {
37 auto method = frame.GetMethod();
38 auto pandaFile = method->GetPandaFile();
39 auto &debugInfo = GetDebugInfo(pandaFile);
40 sourceFile = debugInfo.GetSourceFile(method->GetFileId());
41
42 methodName = utf::Mutf8AsCString(method->GetName().data);
43
44 // Line number entry corresponding to the bytecode location is
45 // the last such entry for which the bytecode offset is not greater than
46 // the given offset. Use `std::upper_bound` to find the first entry
47 // for which the condition is not true, and then step back.
48 auto &table = debugInfo.GetLineNumberTable(method->GetFileId());
49 auto lineNumberIter = std::upper_bound(table.begin(), table.end(), frame.GetBytecodeOffset(),
50 [](auto offset, auto &entry) { return offset < entry.offset; });
51 ASSERT(lineNumberIter != table.begin());
52 lineNumber = std::prev(lineNumberIter)->line;
53 }
54
GetCurrentLineLocations(const PtFrame & frame)55 std::unordered_set<PtLocation, HashLocation> DebugInfoCache::GetCurrentLineLocations(const PtFrame &frame)
56 {
57 std::unordered_set<PtLocation, HashLocation> locations;
58
59 auto method = frame.GetMethod();
60 auto pandaFile = method->GetPandaFile();
61 auto methodId = method->GetFileId();
62 auto &table = GetDebugInfo(pandaFile).GetLineNumberTable(methodId);
63 auto it = std::upper_bound(table.begin(), table.end(), frame.GetBytecodeOffset(),
64 [](auto offset, auto entry) { return offset < entry.offset; });
65 if (it == table.begin()) {
66 return locations;
67 }
68 auto lineNumber = (--it)->line;
69
70 for (it = table.begin(); it != table.end(); ++it) {
71 if (it->line != lineNumber) {
72 continue;
73 }
74
75 auto next = it + 1;
76 auto nextOffset = next != table.end() ? next->offset : method->GetCodeSize();
77 for (auto o = it->offset; o < nextOffset; o++) {
78 locations.emplace(pandaFile->GetFilename().c_str(), methodId, o);
79 }
80 }
81
82 return locations;
83 }
84
GetContinueToLocations(std::string_view sourceFile,size_t lineNumber)85 std::unordered_set<PtLocation, HashLocation> DebugInfoCache::GetContinueToLocations(std::string_view sourceFile,
86 size_t lineNumber)
87 {
88 std::unordered_set<PtLocation, HashLocation> locations;
89 EnumerateLineEntries(
90 [](auto, auto &) { return true; },
91 [sourceFile](auto, auto &debugInfo, auto methodId) { return debugInfo.GetSourceFile(methodId) == sourceFile; },
92 [lineNumber, &locations](auto pandaFile, auto &, auto methodId, auto &entry, auto next) {
93 if (entry.line != lineNumber) {
94 // continue enumeration
95 return true;
96 }
97
98 uint32_t nextOffset;
99 if (next == nullptr) {
100 panda_file::MethodDataAccessor mda(*pandaFile, methodId);
101 if (auto codeId = mda.GetCodeId()) {
102 nextOffset = panda_file::CodeDataAccessor(*pandaFile, *codeId).GetCodeSize();
103 } else {
104 nextOffset = 0;
105 }
106 } else {
107 nextOffset = next->offset;
108 }
109
110 for (auto o = entry.offset; o < nextOffset; o++) {
111 locations.emplace(pandaFile->GetFilename().data(), methodId, o);
112 }
113 return true;
114 });
115 return locations;
116 }
117
GetBreakpointLocations(const std::function<bool (std::string_view)> & sourceFileFilter,size_t lineNumber,std::set<std::string_view> & sourceFiles)118 std::vector<PtLocation> DebugInfoCache::GetBreakpointLocations(
119 const std::function<bool(std::string_view)> &sourceFileFilter, size_t lineNumber,
120 std::set<std::string_view> &sourceFiles)
121 {
122 std::vector<PtLocation> locations;
123 sourceFiles.clear();
124 // clang-format off
125 EnumerateLineEntries(
126 [](auto, auto &) { return true; },
127 [&sourceFileFilter](auto, auto &debugInfo, auto methodId) {
128 return sourceFileFilter(debugInfo.GetSourceFile(methodId));
129 },
130 [lineNumber, &sourceFiles, &locations](auto pandaFile, auto &debugInfo, auto methodId,
131 auto &entry, auto /* next */) {
132 if (entry.line == lineNumber) {
133 sourceFiles.insert(debugInfo.GetSourceFile(methodId));
134 locations.emplace_back(pandaFile->GetFilename().data(), methodId, entry.offset);
135 }
136
137 return true;
138 });
139 // clang-format on
140 return locations;
141 }
142
GetValidLineNumbers(std::string_view sourceFile,size_t startLine,size_t endLine,bool restrictToFunction)143 std::set<size_t> DebugInfoCache::GetValidLineNumbers(std::string_view sourceFile, size_t startLine, size_t endLine,
144 bool restrictToFunction)
145 {
146 std::set<size_t> lineNumbers;
147 auto lineHandler = [startLine, endLine, &lineNumbers](auto, auto &, auto, auto &entry, auto /* next */) {
148 if (entry.line >= startLine && entry.line < endLine) {
149 lineNumbers.insert(entry.line);
150 }
151
152 return true;
153 };
154
155 if (restrictToFunction) {
156 auto methodFilter = [sourceFile, startLine](auto, auto &debugInfo, auto methodId) {
157 if (debugInfo.GetSourceFile(methodId) != sourceFile) {
158 return false;
159 }
160
161 bool hasLess = false;
162 bool hasGreater = false;
163 for (auto &entry : debugInfo.GetLineNumberTable(methodId)) {
164 if (entry.line <= startLine) {
165 hasLess = true;
166 }
167
168 if (entry.line >= startLine) {
169 hasGreater = true;
170 }
171
172 if (hasLess && hasGreater) {
173 break;
174 }
175 }
176
177 return hasLess && hasGreater;
178 };
179 EnumerateLineEntries([](auto, auto &) { return true; }, methodFilter, lineHandler);
180 } else {
181 EnumerateLineEntries([](auto, auto &) { return true; },
182 [sourceFile](auto, auto &debugInfo, auto methodId) {
183 return (debugInfo.GetSourceFile(methodId) == sourceFile);
184 },
185 lineHandler);
186 }
187
188 return lineNumbers;
189 }
190
191 // NOLINTBEGIN(readability-magic-numbers)
CreateTypedValueFromReg(uint64_t reg,panda_file::Type::TypeId type)192 static TypedValue CreateTypedValueFromReg(uint64_t reg, panda_file::Type::TypeId type)
193 {
194 switch (type) {
195 case panda_file::Type::TypeId::INVALID:
196 return TypedValue::Invalid();
197 case panda_file::Type::TypeId::VOID:
198 return TypedValue::Void();
199 case panda_file::Type::TypeId::U1:
200 return TypedValue::U1((reg & 0x1ULL) != 0);
201 case panda_file::Type::TypeId::I8:
202 return TypedValue::I8(reg & 0xffULL);
203 case panda_file::Type::TypeId::U8:
204 return TypedValue::U8(reg & 0xffULL);
205 case panda_file::Type::TypeId::I16:
206 return TypedValue::I16(reg & 0xffffULL);
207 case panda_file::Type::TypeId::U16:
208 return TypedValue::U16(reg & 0xffffULL);
209 case panda_file::Type::TypeId::I32:
210 return TypedValue::I32(reg & 0xffffffffULL);
211 case panda_file::Type::TypeId::U32:
212 return TypedValue::U32(reg & 0xffffffffULL);
213 case panda_file::Type::TypeId::F32:
214 return TypedValue::F32(bit_cast<float>(static_cast<int32_t>(reg & 0xffffffffULL)));
215 case panda_file::Type::TypeId::F64:
216 return TypedValue::F64(bit_cast<double>(reg & 0xffffffffULL));
217 case panda_file::Type::TypeId::I64:
218 return TypedValue::I64(reg);
219 case panda_file::Type::TypeId::U64:
220 return TypedValue::U64(reg);
221 case panda_file::Type::TypeId::REFERENCE:
222 return TypedValue::Reference(reinterpret_cast<ObjectHeader *>(reg));
223 case panda_file::Type::TypeId::TAGGED:
224 return TypedValue::Tagged(coretypes::TaggedValue(static_cast<coretypes::TaggedType>(reg)));
225 default:
226 UNREACHABLE();
227 return TypedValue::Invalid();
228 }
229 }
230 // NOLINTEND(readability-magic-numbers)
231
GetTypeIdBySignature(char signature)232 static panda_file::Type::TypeId GetTypeIdBySignature(char signature)
233 {
234 switch (signature) {
235 case 'V':
236 return panda_file::Type::TypeId::VOID;
237 case 'Z':
238 return panda_file::Type::TypeId::U1;
239 case 'B':
240 return panda_file::Type::TypeId::I8;
241 case 'H':
242 return panda_file::Type::TypeId::U8;
243 case 'S':
244 return panda_file::Type::TypeId::I16;
245 case 'C':
246 return panda_file::Type::TypeId::U16;
247 case 'I':
248 return panda_file::Type::TypeId::I32;
249 case 'U':
250 return panda_file::Type::TypeId::U32;
251 case 'F':
252 return panda_file::Type::TypeId::F32;
253 case 'D':
254 return panda_file::Type::TypeId::F64;
255 case 'J':
256 return panda_file::Type::TypeId::I64;
257 case 'Q':
258 return panda_file::Type::TypeId::U64;
259 case 'A':
260 return panda_file::Type::TypeId::TAGGED;
261 case 'L':
262 case '[':
263 return panda_file::Type::TypeId::REFERENCE;
264 default:
265 return panda_file::Type::TypeId::INVALID;
266 }
267 }
268
GetLocals(const PtFrame & frame)269 std::map<std::string, TypedValue> DebugInfoCache::GetLocals(const PtFrame &frame)
270 {
271 std::map<std::string, TypedValue> result;
272
273 auto localHandler = [&result](const std::string &name, const std::string &signature, uint64_t reg,
274 PtFrame::RegisterKind kind) {
275 auto type = signature.empty() ? panda_file::Type::TypeId::INVALID : GetTypeIdBySignature(signature[0]);
276 if (type == panda_file::Type::TypeId::INVALID) {
277 switch (kind) {
278 case PtFrame::RegisterKind::PRIMITIVE:
279 type = panda_file::Type::TypeId::U64;
280 break;
281 case PtFrame::RegisterKind::REFERENCE:
282 type = panda_file::Type::TypeId::REFERENCE;
283 break;
284 case PtFrame::RegisterKind::TAGGED:
285 type = panda_file::Type::TypeId::TAGGED;
286 break;
287 default:
288 UNREACHABLE();
289 break;
290 }
291 }
292
293 result.emplace(name, CreateTypedValueFromReg(reg, type));
294 };
295
296 auto method = frame.GetMethod();
297 auto methodId = method->GetFileId();
298 auto &debugInfo = GetDebugInfo(method->GetPandaFile());
299 auto ¶meters = debugInfo.GetParameterInfo(methodId);
300 for (auto i = 0U; i < parameters.size(); i++) {
301 auto ¶meter = parameters[i];
302 localHandler(parameter.name, parameter.signature, frame.GetArgument(i), frame.GetArgumentKind(i));
303 }
304
305 auto &variables = debugInfo.GetLocalVariableTable(methodId);
306 for (auto &variable : variables) {
307 auto frameOffset = frame.GetBytecodeOffset();
308 if (variable.startOffset <= frameOffset && frameOffset < variable.endOffset) {
309 localHandler(variable.name, variable.typeSignature,
310 // We introduced a hack in DisasmBackedDebugInfoExtractor, assigning -1 to Accumulator
311 variable.regNumber == -1 ? frame.GetAccumulator() : frame.GetVReg(variable.regNumber),
312 variable.regNumber == -1 ? frame.GetAccumulatorKind() : frame.GetVRegKind(variable.regNumber));
313 }
314 }
315
316 return result;
317 }
318
GetSourceCode(std::string_view sourceFile)319 std::string DebugInfoCache::GetSourceCode(std::string_view sourceFile)
320 {
321 {
322 os::memory::LockHolder lock(disassembliesMutex_);
323
324 auto it = disassemblies_.find(sourceFile);
325 if (it != disassemblies_.end()) {
326 return GetDebugInfo(&it->second.first).GetSourceCode(it->second.second);
327 }
328 }
329
330 if (!os::file::File::IsRegularFile(sourceFile.data())) {
331 return {};
332 }
333
334 std::string result;
335
336 std::stringstream buffer;
337 buffer << std::ifstream(sourceFile.data()).rdbuf();
338
339 result = buffer.str();
340 if (!result.empty() && result.back() != '\n') {
341 result += "\n";
342 }
343
344 return result;
345 }
346
GetDebugInfo(const panda_file::File * file)347 const panda_file::DebugInfoExtractor &DebugInfoCache::GetDebugInfo(const panda_file::File *file)
348 {
349 os::memory::LockHolder lock(debugInfosMutex_);
350 auto it = debugInfos_.find(file);
351 ASSERT(it != debugInfos_.end());
352 return it->second;
353 }
354 } // namespace panda::tooling::inspector
355