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> &param_info)183 void ExtractMethodParams(const File *pf, DebugInfoDataAccessor &dda, ProtoDataAccessor &pda, MethodDataAccessor &mda,
184                          ClassDataAccessor &cda, std::vector<DebugInfoExtractor::ParamInfo> &param_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 &param_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