1 /*
2 * Copyright (c) 2021 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 "ecmascript/jspandafile/debug_info_extractor.h"
17
18 #include "libpandafile/debug_data_accessor-inl.h"
19 #include "libpandafile/line_number_program.h"
20
21 namespace panda::ecmascript {
22 using panda::panda_file::ClassDataAccessor;
23 using panda::panda_file::DebugInfoDataAccessor;
24 using panda::panda_file::File;
25 using panda::panda_file::LineNumberProgramItem;
26 using panda::panda_file::LineProgramState;
27 using panda::panda_file::LineNumberProgramProcessor;
28 using panda::panda_file::MethodDataAccessor;
29 using panda::panda_file::ProtoDataAccessor;
30
GetStringFromConstantPool(const panda_file::File &pf, uint32_t offset)31 static const char *GetStringFromConstantPool(const panda_file::File &pf, uint32_t offset)
32 {
33 return reinterpret_cast<const char *>(pf.GetStringData(panda_file::File::EntityId(offset)).data);
34 }
35
36 class LineNumberProgramHandler {
37 public:
LineNumberProgramHandler(LineProgramState *state)38 explicit LineNumberProgramHandler(LineProgramState *state) : state_(state) {}
39 ~LineNumberProgramHandler() = default;
40
41 NO_COPY_SEMANTIC(LineNumberProgramHandler);
42 NO_MOVE_SEMANTIC(LineNumberProgramHandler);
43
GetState() const44 LineProgramState *GetState() const
45 {
46 return state_;
47 }
48
ProcessBegin()49 void ProcessBegin()
50 {
51 lnt_.push_back({state_->GetAddress(), static_cast<int32_t>(state_->GetLine())});
52 }
53
ProcessEnd()54 void ProcessEnd()
55 {
56 // When process ends, update any variableInfo
57 // with end_offset = 0, set it to the state address.
58 for (auto iter = lvt_.begin(); iter != lvt_.end(); iter++) {
59 if (iter->endOffset == 0) {
60 iter->endOffset = state_->GetAddress();
61 }
62 }
63 }
64
HandleAdvanceLine(int32_t lineDiff) const65 bool HandleAdvanceLine(int32_t lineDiff) const
66 {
67 state_->AdvanceLine(lineDiff);
68 return true;
69 }
70
HandleAdvancePc(uint32_t pcDiff) const71 bool HandleAdvancePc(uint32_t pcDiff) const
72 {
73 state_->AdvancePc(pcDiff);
74 return true;
75 }
76
HandleSetFile(uint32_t sourceFileId) const77 bool HandleSetFile(uint32_t sourceFileId) const
78 {
79 state_->SetFile(sourceFileId);
80 return true;
81 }
82
HandleSetSourceCode(uint32_t sourceCodeId) const83 bool HandleSetSourceCode(uint32_t sourceCodeId) const
84 {
85 state_->SetSourceCode(sourceCodeId);
86 return true;
87 }
88
HandleSetPrologueEnd() const89 bool HandleSetPrologueEnd() const
90 {
91 return true;
92 }
93
HandleSetEpilogueBegin() const94 bool HandleSetEpilogueBegin() const
95 {
96 return true;
97 }
98
HandleStartLocal(int32_t regNumber, uint32_t nameId, [[maybe_unused]] uint32_t typeId)99 bool HandleStartLocal(int32_t regNumber, uint32_t nameId, [[maybe_unused]] uint32_t typeId)
100 {
101 // start_offset is the current state address, end_offset will temporarily be 0 here,
102 // then being updated inside the HandleEndLocal method.
103 uint32_t startOffset = state_->GetAddress();
104 uint32_t endOffset = 0;
105 const char *name = GetStringFromConstantPool(state_->GetPandaFile(), nameId);
106 lvt_.push_back({name, regNumber, startOffset, endOffset});
107 return true;
108 }
109
HandleStartLocalExtended(int32_t regNumber, uint32_t nameId, [[maybe_unused]] uint32_t typeId, [[maybe_unused]] uint32_t typeSignatureId)110 bool HandleStartLocalExtended(int32_t regNumber, uint32_t nameId, [[maybe_unused]] uint32_t typeId,
111 [[maybe_unused]] uint32_t typeSignatureId)
112 {
113 uint32_t startOffset = state_->GetAddress();
114 uint32_t endOffset = 0;
115 const char *name = GetStringFromConstantPool(state_->GetPandaFile(), nameId);
116 lvt_.push_back({name, regNumber, startOffset, endOffset});
117 return true;
118 }
119
HandleEndLocal([[maybe_unused]] int32_t regNumber)120 bool HandleEndLocal([[maybe_unused]] int32_t regNumber)
121 {
122 for (auto iter = lvt_.rbegin(); iter != lvt_.rend(); iter++) {
123 // reversely finds the variable and updates its end_offset to be state address
124 if (iter->regNumber == regNumber && iter->endOffset == 0) {
125 iter->endOffset = state_->GetAddress();
126 break;
127 }
128 }
129 return true;
130 }
131
HandleSetColumn(int32_t columnNumber)132 bool HandleSetColumn(int32_t columnNumber)
133 {
134 state_->SetColumn(columnNumber);
135 cnt_.push_back({state_->GetAddress(), static_cast<int32_t>(state_->GetColumn())});
136 return true;
137 }
138
HandleSpecialOpcode(uint32_t pcOffset, int32_t lineOffset)139 bool HandleSpecialOpcode(uint32_t pcOffset, int32_t lineOffset)
140 {
141 state_->AdvancePc(pcOffset);
142 state_->AdvanceLine(lineOffset);
143 lnt_.push_back({state_->GetAddress(), static_cast<int32_t>(state_->GetLine())});
144 return true;
145 }
146
GetLineNumberTable() const147 LineNumberTable GetLineNumberTable() const
148 {
149 return lnt_;
150 }
151
GetLocalVariableTable() const152 LocalVariableTable GetLocalVariableTable() const
153 {
154 return lvt_;
155 }
156
GetColumnNumberTable() const157 ColumnNumberTable GetColumnNumberTable() const
158 {
159 return cnt_;
160 }
161
GetFile() const162 const uint8_t *GetFile() const
163 {
164 return state_->GetFile();
165 }
166
GetSourceCode() const167 const uint8_t *GetSourceCode() const
168 {
169 return state_->GetSourceCode();
170 }
171
172 private:
173 using Opcode = LineNumberProgramItem::Opcode;
174
175 LineProgramState *state_;
176 LineNumberTable lnt_;
177 LocalVariableTable lvt_;
178 ColumnNumberTable cnt_;
179 };
180
GetLineNumberTable(const panda_file::File::EntityId methodId)181 const LineNumberTable &DebugInfoExtractor::GetLineNumberTable(const panda_file::File::EntityId methodId)
182 {
183 std::lock_guard<std::recursive_mutex> lock(mutex_);
184 static const LineNumberTable EMPTY_LINE_TABLE {};
185
186 auto iter = methods_.find(methodId.GetOffset());
187 if (iter == methods_.end()) {
188 if (!ExtractorMethodDebugInfo(methodId)) {
189 return EMPTY_LINE_TABLE;
190 }
191 return methods_[methodId.GetOffset()].lineNumberTable;
192 }
193 ASSERT(iter != methods_.end());
194 return iter->second.lineNumberTable;
195 }
196
GetColumnNumberTable(const panda_file::File::EntityId methodId)197 const ColumnNumberTable &DebugInfoExtractor::GetColumnNumberTable(const panda_file::File::EntityId methodId)
198 {
199 std::lock_guard<std::recursive_mutex> lock(mutex_);
200 static const ColumnNumberTable EMPTY_COLUMN_TABLE {};
201
202 auto iter = methods_.find(methodId.GetOffset());
203 if (iter == methods_.end()) {
204 if (!ExtractorMethodDebugInfo(methodId)) {
205 return EMPTY_COLUMN_TABLE;
206 }
207 return methods_[methodId.GetOffset()].columnNumberTable;
208 }
209 ASSERT(iter != methods_.end());
210 return iter->second.columnNumberTable;
211 }
212
GetLocalVariableTable(const panda_file::File::EntityId methodId)213 const LocalVariableTable &DebugInfoExtractor::GetLocalVariableTable(const panda_file::File::EntityId methodId)
214 {
215 std::lock_guard<std::recursive_mutex> lock(mutex_);
216 static const LocalVariableTable EMPTY_VARIABLE_TABLE {};
217
218 auto iter = methods_.find(methodId.GetOffset());
219 if (iter == methods_.end()) {
220 if (!ExtractorMethodDebugInfo(methodId)) {
221 return EMPTY_VARIABLE_TABLE;
222 }
223 return methods_[methodId.GetOffset()].localVariableTable;
224 }
225 ASSERT(iter != methods_.end());
226 return iter->second.localVariableTable;
227 }
228
GetSourceFile(const panda_file::File::EntityId methodId)229 const std::string &DebugInfoExtractor::GetSourceFile(const panda_file::File::EntityId methodId)
230 {
231 std::lock_guard<std::recursive_mutex> lock(mutex_);
232 static const std::string sourceFile = "";
233
234 auto iter = methods_.find(methodId.GetOffset());
235 if (iter == methods_.end()) {
236 if (!ExtractorMethodDebugInfo(methodId)) {
237 return sourceFile;
238 }
239 return methods_[methodId.GetOffset()].sourceFile;
240 }
241 ASSERT(iter != methods_.end());
242 return iter->second.sourceFile;
243 }
244
GetSourceCode(const panda_file::File::EntityId methodId)245 const std::string &DebugInfoExtractor::GetSourceCode(const panda_file::File::EntityId methodId)
246 {
247 std::lock_guard<std::recursive_mutex> lock(mutex_);
248 static const std::string sourceCode = "";
249
250 auto iter = methods_.find(methodId.GetOffset());
251 if (iter == methods_.end()) {
252 if (!ExtractorMethodDebugInfo(methodId)) {
253 return sourceCode;
254 }
255 return methods_[methodId.GetOffset()].sourceCode;
256 }
257 ASSERT(iter != methods_.end());
258 return iter->second.sourceCode;
259 }
260
ExtractorMethodDebugInfo(const panda_file::File::EntityId methodId)261 bool DebugInfoExtractor::ExtractorMethodDebugInfo(const panda_file::File::EntityId methodId)
262 {
263 if (!methodId.IsValid()) {
264 return false;
265 }
266
267 uint32_t offset = methodId.GetOffset();
268 if (offset >= jsPandaFile_->GetFileSize()) {
269 return false;
270 }
271
272 auto &pandaFile = *jsPandaFile_->GetPandaFile();
273 MethodDataAccessor mda(pandaFile, methodId);
274 auto classId = mda.GetClassId();
275 ASSERT(classId.IsValid() && !pandaFile.IsExternal(classId));
276 ClassDataAccessor cda(pandaFile, classId);
277 auto sourceFileId = cda.GetSourceFileId();
278 auto debugInfoId = mda.GetDebugInfoId();
279 if (!debugInfoId) {
280 return false;
281 }
282
283 ExtractorMethodDebugInfo(pandaFile, sourceFileId, debugInfoId, offset);
284 return true;
285 }
286
ExtractorMethodDebugInfo(const panda_file::File &pandaFile, const std::optional<panda_file::File::EntityId> sourceFileId, const std::optional<panda_file::File::EntityId> debugInfoId, uint32_t offset)287 void DebugInfoExtractor::ExtractorMethodDebugInfo(const panda_file::File &pandaFile,
288 const std::optional<panda_file::File::EntityId> sourceFileId,
289 const std::optional<panda_file::File::EntityId> debugInfoId,
290 uint32_t offset)
291 {
292 DebugInfoDataAccessor dda(pandaFile, debugInfoId.value());
293 const uint8_t *program = dda.GetLineNumberProgram();
294 LineProgramState state(pandaFile, sourceFileId.value_or(panda_file::File::EntityId(0)), dda.GetLineStart(),
295 dda.GetConstantPool());
296
297 LineNumberProgramHandler handler(&state);
298 LineNumberProgramProcessor<LineNumberProgramHandler> programProcessor(program, &handler);
299 programProcessor.Process();
300
301 const char *sourceFile = "";
302 if (state.HasFile()) {
303 sourceFile = reinterpret_cast<const char *>(handler.GetFile());
304 }
305
306 const char *sourceCode = "";
307 if (state.HasSourceCode()) {
308 sourceCode = reinterpret_cast<const char *>(handler.GetSourceCode());
309 }
310
311 MethodDebugInfo methodDebugInfo = {sourceFile, sourceCode, handler.GetLineNumberTable(),
312 handler.GetColumnNumberTable(), handler.GetLocalVariableTable()};
313 methods_.emplace(offset, std::move(methodDebugInfo));
314 }
315
Extract()316 void DebugInfoExtractor::Extract()
317 {
318 std::lock_guard<std::recursive_mutex> lock(mutex_);
319 auto &pandaFile = *jsPandaFile_->GetPandaFile();
320 auto classes = jsPandaFile_->GetClasses();
321
322 for (size_t i = 0; i < classes.Size(); i++) {
323 panda_file::File::EntityId classId(classes[i]);
324 if (!classId.IsValid() || jsPandaFile_->IsExternal(classId)) {
325 continue;
326 }
327
328 ClassDataAccessor cda(pandaFile, classId);
329 auto sourceFileId = cda.GetSourceFileId();
330
331 cda.EnumerateMethods([&](MethodDataAccessor &mda) {
332 panda_file::File::EntityId methodId = mda.GetMethodId();
333 if (!methodId.IsValid()) {
334 return;
335 }
336
337 uint32_t offset = methodId.GetOffset();
338 if (offset >= jsPandaFile_->GetFileSize()) {
339 return;
340 }
341
342 auto iter = methods_.find(offset);
343 if (iter != methods_.end()) {
344 return;
345 }
346
347 auto debugInfoId = mda.GetDebugInfoId();
348 if (!debugInfoId) {
349 return;
350 }
351
352 ExtractorMethodDebugInfo(pandaFile, sourceFileId, debugInfoId, offset);
353 });
354 }
355 }
356 } // namespace panda::ecmascript
357