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