• 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 #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 &param_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