• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &parameters = debugInfo.GetParameterInfo(methodId);
300     for (auto i = 0U; i < parameters.size(); i++) {
301         auto &parameter = 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