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