1 /**
2 * Copyright (c) 2021-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_extractor.h"
17 #include "line_number_program.h"
18 #include "class_data_accessor-inl.h"
19 #include "debug_data_accessor-inl.h"
20 #include "proto_data_accessor-inl.h"
21 #include "utils/utf.h"
22
23 namespace panda::panda_file {
24
GetStringFromConstantPool(const File & pf,uint32_t offset)25 static const char *GetStringFromConstantPool(const File &pf, uint32_t offset)
26 {
27 return utf::Mutf8AsCString(pf.GetStringData(File::EntityId(offset)).data);
28 }
29
DebugInfoExtractor(const File * pf)30 DebugInfoExtractor::DebugInfoExtractor(const File *pf)
31 {
32 Extract(pf);
33 }
34
35 class LineNumberProgramHandler {
36 public:
LineNumberProgramHandler(LineProgramState * state)37 explicit LineNumberProgramHandler(LineProgramState *state) : state_(state) {}
38 ~LineNumberProgramHandler() = default;
39
40 NO_COPY_SEMANTIC(LineNumberProgramHandler);
41 NO_MOVE_SEMANTIC(LineNumberProgramHandler);
42
GetState() const43 LineProgramState *GetState() const
44 {
45 return state_;
46 }
47
ProcessBegin()48 void ProcessBegin()
49 {
50 lnt_.push_back({state_->GetAddress(), static_cast<size_t>(state_->GetLine())});
51 }
52
ProcessEnd()53 void ProcessEnd()
54 {
55 ProcessVars();
56 }
57
HandleAdvanceLine(int32_t line_diff) const58 bool HandleAdvanceLine(int32_t line_diff) const
59 {
60 state_->AdvanceLine(line_diff);
61 return true;
62 }
63
HandleAdvancePc(uint32_t pc_diff) const64 bool HandleAdvancePc(uint32_t pc_diff) const
65 {
66 state_->AdvancePc(pc_diff);
67 return true;
68 }
69
HandleSetFile(uint32_t source_file_id) const70 bool HandleSetFile(uint32_t source_file_id) const
71 {
72 state_->SetFile(source_file_id);
73 return true;
74 }
75
HandleSetSourceCode(uint32_t source_code_id) const76 bool HandleSetSourceCode(uint32_t source_code_id) const
77 {
78 state_->SetSourceCode(source_code_id);
79 return true;
80 }
81
HandleSetPrologueEnd() const82 bool HandleSetPrologueEnd() const
83 {
84 return true;
85 }
86
HandleSetEpilogueBegin() const87 bool HandleSetEpilogueBegin() const
88 {
89 return true;
90 }
91
HandleStartLocal(int32_t reg_number,uint32_t name_id,uint32_t type_id)92 bool HandleStartLocal(int32_t reg_number, uint32_t name_id, uint32_t type_id)
93 {
94 const char *name = GetStringFromConstantPool(state_->GetPandaFile(), name_id);
95 const char *type = GetStringFromConstantPool(state_->GetPandaFile(), type_id);
96 lvt_.push_back({name, type, type, reg_number, state_->GetAddress(), 0});
97 return true;
98 }
99
HandleStartLocalExtended(int32_t reg_number,uint32_t name_id,uint32_t type_id,uint32_t type_signature_id)100 bool HandleStartLocalExtended(int32_t reg_number, uint32_t name_id, uint32_t type_id, uint32_t type_signature_id)
101 {
102 const char *name = GetStringFromConstantPool(state_->GetPandaFile(), name_id);
103 const char *type = GetStringFromConstantPool(state_->GetPandaFile(), type_id);
104 const char *type_sign = GetStringFromConstantPool(state_->GetPandaFile(), type_signature_id);
105 lvt_.push_back({name, type, type_sign, reg_number, state_->GetAddress(), 0});
106 return true;
107 }
108
HandleEndLocal(int32_t reg_number)109 bool HandleEndLocal(int32_t reg_number)
110 {
111 bool found = false;
112 for (auto it = lvt_.rbegin(); it != lvt_.rend(); ++it) {
113 if (it->reg_number == reg_number) {
114 it->end_offset = state_->GetAddress();
115 found = true;
116 break;
117 }
118 }
119 if (!found) {
120 LOG(FATAL, PANDAFILE) << "Unknown variable";
121 }
122 return true;
123 }
124
HandleSetColumn(int32_t column_number)125 bool HandleSetColumn(int32_t column_number)
126 {
127 state_->SetColumn(column_number);
128 cnt_.push_back({state_->GetAddress(), state_->GetColumn()});
129 return true;
130 }
131
HandleSpecialOpcode(uint32_t pc_offset,int32_t line_offset)132 bool HandleSpecialOpcode(uint32_t pc_offset, int32_t line_offset)
133 {
134 state_->AdvancePc(pc_offset);
135 state_->AdvanceLine(line_offset);
136 lnt_.push_back({state_->GetAddress(), static_cast<size_t>(state_->GetLine())});
137 return true;
138 }
139
GetLineNumberTable() const140 LineNumberTable GetLineNumberTable() const
141 {
142 return lnt_;
143 }
144
GetLocalVariableTable() const145 LocalVariableTable GetLocalVariableTable() const
146 {
147 return lvt_;
148 }
149
GetColumnNumberTable() const150 ColumnNumberTable GetColumnNumberTable() const
151 {
152 return cnt_;
153 }
154
GetFile() const155 const uint8_t *GetFile() const
156 {
157 return state_->GetFile();
158 }
159
GetSourceCode() const160 const uint8_t *GetSourceCode() const
161 {
162 return state_->GetSourceCode();
163 }
164
165 private:
166 using Opcode = LineNumberProgramItem::Opcode;
167
ProcessVars()168 void ProcessVars()
169 {
170 for (auto &var : lvt_) {
171 if (var.end_offset == 0) {
172 var.end_offset = state_->GetAddress();
173 }
174 }
175 }
176
177 LineProgramState *state_;
178 LineNumberTable lnt_;
179 LocalVariableTable lvt_;
180 ColumnNumberTable cnt_;
181 };
182
Extract(const File * pf)183 void DebugInfoExtractor::Extract(const File *pf)
184 {
185 const auto &panda_file = *pf;
186 auto classes = pf->GetClasses();
187 for (size_t i = 0; i < classes.Size(); i++) {
188 File::EntityId id(classes[i]);
189 if (panda_file.IsExternal(id)) {
190 continue;
191 }
192
193 ClassDataAccessor cda(panda_file, id);
194
195 auto source_file_id = cda.GetSourceFileId();
196
197 cda.EnumerateMethods([&](MethodDataAccessor &mda) {
198 auto debug_info_id = mda.GetDebugInfoId();
199
200 if (!debug_info_id) {
201 return;
202 }
203
204 DebugInfoDataAccessor dda(panda_file, debug_info_id.value());
205 ProtoDataAccessor pda(panda_file, mda.GetProtoId());
206
207 std::vector<ParamInfo> param_info;
208
209 size_t idx = 0;
210 size_t idx_ref = pda.GetReturnType().IsReference() ? 1 : 0;
211 bool first_param = true;
212 const char *class_name = utf::Mutf8AsCString(pf->GetStringData(cda.GetClassId()).data);
213 dda.EnumerateParameters([&](File::EntityId ¶m_id) {
214 ParamInfo info;
215 if (param_id.IsValid()) {
216 info.name = utf::Mutf8AsCString(pf->GetStringData(param_id).data);
217 if (first_param && !mda.IsStatic()) {
218 info.signature = class_name;
219 } else {
220 Type param_type = pda.GetArgType(idx++);
221 if (param_type.IsPrimitive()) {
222 info.signature = Type::GetSignatureByTypeId(param_type);
223 } else {
224 auto ref_type = pda.GetReferenceType(idx_ref++);
225 info.signature = utf::Mutf8AsCString(pf->GetStringData(ref_type).data);
226 }
227 }
228 }
229 first_param = false;
230 param_info.emplace_back(info);
231 });
232
233 const uint8_t *program = dda.GetLineNumberProgram();
234
235 LineProgramState state(panda_file, source_file_id.value_or(File::EntityId(0)), dda.GetLineStart(),
236 dda.GetConstantPool());
237
238 LineNumberProgramHandler handler(&state);
239 LineNumberProgramProcessor<LineNumberProgramHandler> program_processor(program, &handler);
240 program_processor.Process();
241
242 File::EntityId method_id = mda.GetMethodId();
243 const char *source_file = utf::Mutf8AsCString(handler.GetFile());
244 const char *source_code = utf::Mutf8AsCString(handler.GetSourceCode());
245 if (UNLIKELY(source_code == nullptr)) {
246 source_code = "";
247 }
248 methods_.push_back({source_file, source_code, method_id, handler.GetLineNumberTable(),
249 handler.GetLocalVariableTable(), std::move(param_info),
250 handler.GetColumnNumberTable()});
251 });
252 }
253 }
254
GetLineNumberTable(File::EntityId method_id) const255 const LineNumberTable &DebugInfoExtractor::GetLineNumberTable(File::EntityId method_id) const
256 {
257 for (const auto &method : methods_) {
258 if (method.method_id == method_id) {
259 return method.line_number_table;
260 }
261 }
262
263 static const LineNumberTable EMPTY_LINE_TABLE {}; // NOLINT(fuchsia-statically-constructed-objects)
264 return EMPTY_LINE_TABLE;
265 }
266
GetColumnNumberTable(File::EntityId method_id) const267 const ColumnNumberTable &DebugInfoExtractor::GetColumnNumberTable(File::EntityId method_id) const
268 {
269 for (const auto &method : methods_) {
270 if (method.method_id == method_id) {
271 return method.column_number_table;
272 }
273 }
274
275 static const ColumnNumberTable EMPTY_COLUMN_TABLE {}; // NOLINT(fuchsia-statically-constructed-objects)
276 return EMPTY_COLUMN_TABLE;
277 }
278
GetLocalVariableTable(File::EntityId method_id) const279 const LocalVariableTable &DebugInfoExtractor::GetLocalVariableTable(File::EntityId method_id) const
280 {
281 for (const auto &method : methods_) {
282 if (method.method_id == method_id) {
283 return method.local_variable_table;
284 }
285 }
286
287 static const LocalVariableTable EMPTY_VARIABLE_TABLE {}; // NOLINT(fuchsia-statically-constructed-objects)
288 return EMPTY_VARIABLE_TABLE;
289 }
290
GetParameterInfo(File::EntityId method_id) const291 const std::vector<DebugInfoExtractor::ParamInfo> &DebugInfoExtractor::GetParameterInfo(File::EntityId method_id) const
292 {
293 for (const auto &method : methods_) {
294 if (method.method_id == method_id) {
295 return method.param_info;
296 }
297 }
298
299 static const std::vector<ParamInfo> EMPTY_PARAM_INFO {}; // NOLINT(fuchsia-statically-constructed-objects)
300 return EMPTY_PARAM_INFO;
301 }
302
GetSourceFile(File::EntityId method_id) const303 const char *DebugInfoExtractor::GetSourceFile(File::EntityId method_id) const
304 {
305 for (const auto &method : methods_) {
306 if (method.method_id == method_id) {
307 return method.source_file.c_str();
308 }
309 }
310 return "";
311 }
312
GetSourceCode(File::EntityId method_id) const313 const char *DebugInfoExtractor::GetSourceCode(File::EntityId method_id) const
314 {
315 for (const auto &method : methods_) {
316 if (method.method_id == method_id) {
317 return method.source_code.c_str();
318 }
319 }
320 return "";
321 }
322
GetMethodIdList() const323 std::vector<File::EntityId> DebugInfoExtractor::GetMethodIdList() const
324 {
325 std::vector<File::EntityId> list;
326
327 for (const auto &method : methods_) {
328 list.push_back(method.method_id);
329 }
330 return list;
331 }
332
333 } // namespace panda::panda_file
334