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
ExtractMethodParams(const File *pf, DebugInfoDataAccessor &dda, ProtoDataAccessor &pda, MethodDataAccessor &mda, ClassDataAccessor &cda, std::vector<DebugInfoExtractor::ParamInfo> ¶m_info)183 void ExtractMethodParams(const File *pf, DebugInfoDataAccessor &dda, ProtoDataAccessor &pda, MethodDataAccessor &mda,
184 ClassDataAccessor &cda, std::vector<DebugInfoExtractor::ParamInfo> ¶m_info)
185 {
186 size_t idx = 0;
187 size_t idx_ref = (mda.HasValidProto() && pda.GetReturnType().IsReference()) ? 1 : 0;
188 bool first_param = true;
189 const char *class_name = utf::Mutf8AsCString(pf->GetStringData(cda.GetClassId()).data);
190 dda.EnumerateParameters([&](File::EntityId ¶m_id) {
191 DebugInfoExtractor::ParamInfo info;
192 if (param_id.IsValid() && !mda.HasValidProto()) {
193 info.name = utf::Mutf8AsCString(pf->GetStringData(param_id).data);
194 // get signature of <any> type
195 info.signature = Type::GetSignatureByTypeId(Type(Type::TypeId::TAGGED));
196 param_info.emplace_back(info);
197 return;
198 }
199
200 if (!param_id.IsValid()) {
201 first_param = false;
202 param_info.emplace_back(info);
203 return;
204 }
205
206 info.name = utf::Mutf8AsCString(pf->GetStringData(param_id).data);
207 if (first_param && !mda.IsStatic()) {
208 info.signature = class_name;
209 } else {
210 Type param_type = pda.GetArgType(idx++);
211 if (param_type.IsPrimitive()) {
212 info.signature = Type::GetSignatureByTypeId(param_type);
213 } else {
214 auto ref_type = pda.GetReferenceType(idx_ref++);
215 info.signature = utf::Mutf8AsCString(pf->GetStringData(ref_type).data);
216 }
217 }
218
219 first_param = false;
220 param_info.emplace_back(info);
221 });
222 }
223
Extract(const File *pf)224 void DebugInfoExtractor::Extract(const File *pf)
225 {
226 const auto &panda_file = *pf;
227 auto classes = pf->GetClasses();
228 for (size_t i = 0; i < classes.Size(); i++) {
229 File::EntityId id(classes[i]);
230 if (panda_file.IsExternal(id)) {
231 continue;
232 }
233
234 ClassDataAccessor cda(panda_file, id);
235
236 auto source_file_id = cda.GetSourceFileId();
237
238 cda.EnumerateMethods([&](MethodDataAccessor &mda) {
239 auto debug_info_id = mda.GetDebugInfoId();
240
241 if (!debug_info_id) {
242 return;
243 }
244
245 DebugInfoDataAccessor dda(panda_file, debug_info_id.value());
246 ProtoDataAccessor pda(panda_file, mda.GetProtoId());
247
248 std::vector<ParamInfo> param_info;
249 ExtractMethodParams(pf, dda, pda, mda, cda, param_info);
250
251 const uint8_t *program = dda.GetLineNumberProgram();
252
253 LineProgramState state(panda_file, source_file_id.value_or(File::EntityId(0)), dda.GetLineStart(),
254 dda.GetConstantPool());
255
256 LineNumberProgramHandler handler(&state);
257 LineNumberProgramProcessor<LineNumberProgramHandler> program_processor(program, &handler);
258 program_processor.Process();
259
260 File::EntityId method_id = mda.GetMethodId();
261 const char *source_file = utf::Mutf8AsCString(handler.GetFile());
262 const char *source_code = utf::Mutf8AsCString(handler.GetSourceCode());
263 if (UNLIKELY(source_code == nullptr)) {
264 source_code = "";
265 }
266 ASSERT(methods_.find(method_id.GetOffset()) == methods_.end());
267 methods_.emplace(method_id.GetOffset(),
268 MethodDebugInfo {source_file, source_code, method_id, handler.GetLineNumberTable(),
269 handler.GetLocalVariableTable(), std::move(param_info),
270 handler.GetColumnNumberTable()});
271 });
272 }
273 }
274
GetLineNumberTable(File::EntityId method_id) const275 const LineNumberTable &DebugInfoExtractor::GetLineNumberTable(File::EntityId method_id) const
276 {
277 auto it = methods_.find(method_id.GetOffset());
278 if (it != methods_.end()) {
279 return it->second.line_number_table;
280 }
281
282 static const LineNumberTable EMPTY_LINE_TABLE {}; // NOLINT(fuchsia-statically-constructed-objects)
283 return EMPTY_LINE_TABLE;
284 }
285
GetColumnNumberTable(File::EntityId method_id) const286 const ColumnNumberTable &DebugInfoExtractor::GetColumnNumberTable(File::EntityId method_id) const
287 {
288 auto it = methods_.find(method_id.GetOffset());
289 if (it != methods_.end()) {
290 return it->second.column_number_table;
291 }
292
293 static const ColumnNumberTable EMPTY_COLUMN_TABLE {}; // NOLINT(fuchsia-statically-constructed-objects)
294 return EMPTY_COLUMN_TABLE;
295 }
296
GetLocalVariableTable(File::EntityId method_id) const297 const LocalVariableTable &DebugInfoExtractor::GetLocalVariableTable(File::EntityId method_id) const
298 {
299 auto it = methods_.find(method_id.GetOffset());
300 if (it != methods_.end()) {
301 return it->second.local_variable_table;
302 }
303
304 static const LocalVariableTable EMPTY_VARIABLE_TABLE {}; // NOLINT(fuchsia-statically-constructed-objects)
305 return EMPTY_VARIABLE_TABLE;
306 }
307
GetParameterInfo(File::EntityId method_id) const308 const std::vector<DebugInfoExtractor::ParamInfo> &DebugInfoExtractor::GetParameterInfo(File::EntityId method_id) const
309 {
310 auto it = methods_.find(method_id.GetOffset());
311 if (it != methods_.end()) {
312 return it->second.param_info;
313 }
314
315 static const std::vector<ParamInfo> EMPTY_PARAM_INFO {}; // NOLINT(fuchsia-statically-constructed-objects)
316 return EMPTY_PARAM_INFO;
317 }
318
GetSourceFile(File::EntityId method_id) const319 const char *DebugInfoExtractor::GetSourceFile(File::EntityId method_id) const
320 {
321 auto it = methods_.find(method_id.GetOffset());
322 if (it != methods_.end()) {
323 return it->second.source_file.c_str();
324 }
325 return "";
326 }
327
GetSourceCode(File::EntityId method_id) const328 const char *DebugInfoExtractor::GetSourceCode(File::EntityId method_id) const
329 {
330 auto it = methods_.find(method_id.GetOffset());
331 if (it != methods_.end()) {
332 return it->second.source_code.c_str();
333 }
334 return "";
335 }
336
GetMethodIdList() const337 std::vector<File::EntityId> DebugInfoExtractor::GetMethodIdList() const
338 {
339 std::vector<File::EntityId> list;
340
341 for (const auto &method : methods_) {
342 list.push_back(method.second.method_id);
343 }
344 return list;
345 }
346
347 } // namespace panda::panda_file
348