• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> &param_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 &param_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