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