• 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 "annotation_data_accessor.h"
18 #include "class_data_accessor-inl.h"
19 #include "code_data_accessor-inl.h"
20 #include "debug_data_accessor-inl.h"
21 #include "field_data_accessor-inl.h"
22 #include "file.h"
23 #include "file_item_container.h"
24 #include "file_writer.h"
25 #include "helpers.h"
26 #include "method_data_accessor-inl.h"
27 #include "modifiers.h"
28 #include "proto_data_accessor-inl.h"
29 
30 #include <cstdio>
31 
32 #include <memory>
33 #include <vector>
34 
35 #include <gtest/gtest.h>
36 
37 namespace panda::panda_file::test {
38 
39 static const char SOURCE_FILE[] = "asm.pa";
40 
PreparePandaFile(ItemContainer * container)41 void PreparePandaFile(ItemContainer *container)
42 {
43     ClassItem *class_item = container->GetOrCreateClassItem("A");
44     class_item->SetAccessFlags(ACC_PUBLIC);
45 
46     StringItem *method_name = container->GetOrCreateStringItem("foo");
47 
48     PrimitiveTypeItem *ret_type = container->GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
49     std::vector<MethodParamItem> params;
50     params.emplace_back(container->GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
51     ProtoItem *proto_item = container->GetOrCreateProtoItem(ret_type, params);
52     MethodItem *method_item = class_item->AddMethod(method_name, proto_item, ACC_PUBLIC | ACC_STATIC, params);
53 
54     std::vector<uint8_t> instructions {1, 2, 3, 4};
55     CodeItem *code_item = container->CreateItem<CodeItem>(4, 1, instructions);
56 
57     method_item->SetCode(code_item);
58 
59     StringItem *source_file_item = container->GetOrCreateStringItem(SOURCE_FILE);
60     StringItem *param_string_item = container->GetOrCreateStringItem("arg0");
61     StringItem *local_variable_name_0 = container->GetOrCreateStringItem("local_0");
62     StringItem *local_variable_name_1 = container->GetOrCreateStringItem("local_1");
63     StringItem *local_variable_name_2 = container->GetOrCreateStringItem("local_2");
64     StringItem *local_variable_type_i32 = container->GetOrCreateStringItem("I");
65     StringItem *local_variable_sig_type_i32 = container->GetOrCreateStringItem("type_i32");
66 
67     LineNumberProgramItem *line_number_program_item = container->CreateLineNumberProgramItem();
68     DebugInfoItem *debug_info_item = container->CreateItem<DebugInfoItem>(line_number_program_item);
69     method_item->SetDebugInfo(debug_info_item);
70 
71     // Add static method with ref arg
72 
73     StringItem *method_name_bar = container->GetOrCreateStringItem("bar");
74 
75     PrimitiveTypeItem *ret_type_bar = container->GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
76     std::vector<MethodParamItem> params_bar;
77     params_bar.emplace_back(container->GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
78     params_bar.emplace_back(container->GetOrCreateClassItem("RefArg"));
79     ProtoItem *proto_item_bar = container->GetOrCreateProtoItem(ret_type_bar, params_bar);
80     MethodItem *method_item_bar =
81         class_item->AddMethod(method_name_bar, proto_item_bar, ACC_PUBLIC | ACC_STATIC, params_bar);
82 
83     CodeItem *code_item_bar = container->CreateItem<CodeItem>(0, 2, instructions);
84 
85     method_item_bar->SetCode(code_item_bar);
86 
87     StringItem *param_string_item_bar1 = container->GetOrCreateStringItem("arg0");
88     StringItem *param_string_item_bar2 = container->GetOrCreateStringItem("arg1");
89 
90     LineNumberProgramItem *line_number_program_item_bar = container->CreateLineNumberProgramItem();
91     DebugInfoItem *debug_info_item_bar = container->CreateItem<DebugInfoItem>(line_number_program_item_bar);
92     method_item_bar->SetDebugInfo(debug_info_item_bar);
93 
94     // Add non static method with ref arg
95 
96     StringItem *method_name_baz = container->GetOrCreateStringItem("baz");
97 
98     PrimitiveTypeItem *ret_type_baz = container->GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
99     std::vector<MethodParamItem> params_baz;
100     params_baz.emplace_back(container->GetOrCreateClassItem("RefArg"));
101     params_baz.emplace_back(container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U1));
102     ProtoItem *proto_item_baz = container->GetOrCreateProtoItem(ret_type_baz, params_baz);
103     MethodItem *method_item_baz = class_item->AddMethod(method_name_baz, proto_item_baz, ACC_PUBLIC, params_baz);
104 
105     CodeItem *code_item_baz = container->CreateItem<CodeItem>(0, 2, instructions);
106 
107     method_item_baz->SetCode(code_item_baz);
108 
109     StringItem *param_string_item_baz1 = container->GetOrCreateStringItem("arg0");
110     StringItem *param_string_item_baz2 = container->GetOrCreateStringItem("arg1");
111 
112     LineNumberProgramItem *line_number_program_item_baz = container->CreateLineNumberProgramItem();
113     DebugInfoItem *debug_info_item_baz = container->CreateItem<DebugInfoItem>(line_number_program_item_baz);
114     method_item_baz->SetDebugInfo(debug_info_item_baz);
115 
116     // Add debug info for the following source file;
117 
118     //  1 # file: asm.pa
119     //  2 .function foo(i32 arg0) {
120     //  3   ldai arg0
121     //  4   stai v1     // START_LOCAL: reg=1, name="local_0", type="i32"
122     //  5   ldai 2
123     //  6   stai v2     // START_LOCAL_EXTENDED: reg=2, name="local_1",
124     //  type="i32", type_signature="type_i32" 7               // END_LOCAL: reg=1
125     //  8   stai v3     // START_LOCAL: reg=3, name="local_2", type="i32"
126     //  9
127     // 10   return.void
128     // 11 }
129     // 12 .function bar(i32 arg0, B arg1) { // static
130     // 13   ldai arg0
131     // 13   return.void
132     // 14 }
133     // 15 .function baz(B arg0, u1 arg1) { // non static
134     // 17   ldai arg0
135     // 16   return.void
136     // 17 }
137 
138     container->ComputeLayout();
139 
140     // foo line number program
141     auto *constant_pool = debug_info_item->GetConstantPool();
142     // Line 3
143     debug_info_item->SetLineNumber(3);
144     line_number_program_item->EmitSetFile(constant_pool, source_file_item);
145     line_number_program_item->EmitAdvancePc(constant_pool, 1);
146     line_number_program_item->EmitAdvanceLine(constant_pool, 1);
147     line_number_program_item->EmitSpecialOpcode(0, 0);
148     line_number_program_item->EmitColumn(constant_pool, 0, 7);
149     // Line 4
150     line_number_program_item->EmitStartLocal(constant_pool, 1, local_variable_name_0, local_variable_type_i32);
151     line_number_program_item->EmitSpecialOpcode(1, 1);
152     line_number_program_item->EmitColumn(constant_pool, 0, 8);
153     // Line 5
154     line_number_program_item->EmitSpecialOpcode(1, 1);
155     line_number_program_item->EmitColumn(constant_pool, 0, 9);
156     // Line 6
157     line_number_program_item->EmitStartLocalExtended(constant_pool, 2, local_variable_name_1, local_variable_type_i32,
158                                                      local_variable_sig_type_i32);
159     line_number_program_item->EmitEndLocal(1);
160     line_number_program_item->EmitSpecialOpcode(1, 2);
161     line_number_program_item->EmitColumn(constant_pool, 0, 10);
162     // Line 8
163     line_number_program_item->EmitStartLocal(constant_pool, 3, local_variable_name_2, local_variable_type_i32);
164     line_number_program_item->EmitAdvanceLine(constant_pool, 2);
165     line_number_program_item->EmitSpecialOpcode(0, 0);
166     line_number_program_item->EmitColumn(constant_pool, 0, 11);
167     // Line 10
168     line_number_program_item->EmitEnd();
169 
170     debug_info_item->AddParameter(param_string_item);
171 
172     method_item->SetDebugInfo(debug_info_item);
173 
174     // bar line number program
175     auto *constant_pool_bar = debug_info_item_bar->GetConstantPool();
176     debug_info_item_bar->SetLineNumber(13);
177     line_number_program_item_bar->EmitSetFile(constant_pool_bar, source_file_item);
178     line_number_program_item_bar->EmitAdvancePc(constant_pool_bar, 1);
179     line_number_program_item_bar->EmitAdvanceLine(constant_pool_bar, 1);
180     line_number_program_item_bar->EmitSpecialOpcode(0, 0);
181     line_number_program_item_bar->EmitColumn(constant_pool_bar, 0, 5);
182     line_number_program_item_bar->EmitEnd();
183 
184     debug_info_item_bar->AddParameter(param_string_item_bar1);
185     debug_info_item_bar->AddParameter(param_string_item_bar2);
186 
187     method_item_bar->SetDebugInfo(debug_info_item_bar);
188 
189     // baz line number program
190     auto *constant_pool_baz = debug_info_item_baz->GetConstantPool();
191     debug_info_item_baz->SetLineNumber(15);
192     line_number_program_item_baz->EmitSetFile(constant_pool_baz, source_file_item);
193     line_number_program_item_baz->EmitAdvancePc(constant_pool_baz, 1);
194     line_number_program_item_baz->EmitAdvanceLine(constant_pool_baz, 1);
195     line_number_program_item_baz->EmitSpecialOpcode(0, 0);
196     line_number_program_item_baz->EmitColumn(constant_pool_baz, 0, 6);
197     line_number_program_item_baz->EmitEnd();
198 
199     debug_info_item_baz->AddParameter(param_string_item_baz1);
200     debug_info_item_baz->AddParameter(param_string_item_baz2);
201 
202     method_item_baz->SetDebugInfo(debug_info_item_baz);
203 }
204 
205 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
206 struct SourcePairLocation {
207     std::string path;  // NOLINT(misc-non-private-member-variables-in-classes)
208     size_t line;       // NOLINT(misc-non-private-member-variables-in-classes)
209 
operator ==panda::panda_file::test::SourcePairLocation210     bool operator==(const SourcePairLocation &other) const
211     {
212         return path == other.path && line == other.line;
213     }
214 
IsValidpanda::panda_file::test::SourcePairLocation215     bool IsValid() const
216     {
217         return !path.empty();
218     }
219 };
220 
GetLineNumberByTableOffsetWrapper(const panda_file::LineNumberTable & table,uint32_t offset)221 static std::optional<size_t> GetLineNumberByTableOffsetWrapper(const panda_file::LineNumberTable &table,
222                                                                uint32_t offset)
223 {
224     for (const auto &value : table) {
225         if (value.offset == offset) {
226             return value.line;
227         }
228     }
229     return std::nullopt;
230 }
231 
GetOffsetByTableLineNumberWrapper(const panda_file::LineNumberTable & table,size_t line)232 static std::optional<uint32_t> GetOffsetByTableLineNumberWrapper(const panda_file::LineNumberTable &table, size_t line)
233 {
234     for (const auto &value : table) {
235         if (value.line == line) {
236             return value.offset;
237         }
238     }
239     return std::nullopt;
240 }
241 
GetBreakpointAddressWrapper(DebugInfoExtractor extractor,const SourcePairLocation & source_location)242 static std::pair<File::EntityId, uint32_t> GetBreakpointAddressWrapper(DebugInfoExtractor extractor,
243                                                                        const SourcePairLocation &source_location)
244 {
245     auto pos = source_location.path.find_last_of("/\\");
246     auto name = source_location.path;
247 
248     if (pos != std::string::npos) {
249         name = name.substr(pos + 1);
250     }
251 
252     std::vector<panda_file::File::EntityId> methods = extractor.GetMethodIdList();
253     for (const auto &method : methods) {
254         if (extractor.GetSourceFile(method) == source_location.path || extractor.GetSourceFile(method) == name) {
255             panda_file::LineNumberTable line_table = extractor.GetLineNumberTable(method);
256             if (line_table.empty()) {
257                 continue;
258             }
259 
260             std::optional<size_t> offset = GetOffsetByTableLineNumberWrapper(line_table, source_location.line);
261             if (offset == std::nullopt) {
262                 continue;
263             }
264             return {method, offset.value()};
265         }
266     }
267     return {File::EntityId(), 0};
268 }
269 
GetLocalVariableInfoWrapper(DebugInfoExtractor extractor,File::EntityId method_id,size_t offset)270 static std::vector<panda_file::LocalVariableInfo> GetLocalVariableInfoWrapper(DebugInfoExtractor extractor,
271                                                                               File::EntityId method_id, size_t offset)
272 {
273     std::vector<panda_file::LocalVariableInfo> variables = extractor.GetLocalVariableTable(method_id);
274     std::vector<panda_file::LocalVariableInfo> result;
275 
276     for (const auto &variable : variables) {
277         if (variable.start_offset <= offset && offset <= variable.end_offset) {
278             result.push_back(variable);
279         }
280     }
281     return result;
282 }
283 
GetSourcePairLocationWrapper(DebugInfoExtractor extractor,File::EntityId method_id,uint32_t bytecode_offset)284 static SourcePairLocation GetSourcePairLocationWrapper(DebugInfoExtractor extractor, File::EntityId method_id,
285                                                        uint32_t bytecode_offset)
286 {
287     panda_file::LineNumberTable line_table = extractor.GetLineNumberTable(method_id);
288     if (line_table.empty()) {
289         return SourcePairLocation();
290     }
291 
292     std::optional<size_t> line = GetLineNumberByTableOffsetWrapper(line_table, bytecode_offset);
293     if (line == std::nullopt) {
294         return SourcePairLocation();
295     }
296 
297     return SourcePairLocation {extractor.GetSourceFile(method_id), line.value()};
298 }
299 
GetPandaFile(std::vector<uint8_t> & data)300 static std::unique_ptr<const File> GetPandaFile(std::vector<uint8_t> &data)
301 {
302     os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(data.data()), data.size(),
303                               [](std::byte *, size_t) noexcept {});
304     return File::OpenFromMemory(std::move(ptr));
305 }
306 
307 class ExtractorTest : public testing::Test {
308 public:
SetUpTestSuite()309     static void SetUpTestSuite()
310     {
311         ItemContainer container;
312         PreparePandaFile(&container);
313         MemoryWriter writer;
314         ASSERT_TRUE(container.Write(&writer));
315 
316         file_data = writer.GetData();
317         u_file = GetPandaFile(file_data);
318     }
319 
320     static std::unique_ptr<const panda_file::File> u_file;
321     static std::vector<uint8_t> file_data;
322 };
323 
324 std::unique_ptr<const panda_file::File> ExtractorTest::u_file {nullptr};
325 std::vector<uint8_t> ExtractorTest::file_data;
326 
327 HWTEST_F(ExtractorTest, DebugInfoTest, testing::ext::TestSize.Level0)
328 {
329     const panda_file::File *pf = u_file.get();
330     ASSERT_TRUE(pf != nullptr);
331     DebugInfoExtractor extractor(pf);
332 
333     auto breakpoint1_address = GetBreakpointAddressWrapper(extractor, SourcePairLocation {SOURCE_FILE, 1});
334     ASSERT_FALSE(breakpoint1_address.first.IsValid());
335     auto [method_id, bytecode_offset] = GetBreakpointAddressWrapper(extractor, SourcePairLocation {SOURCE_FILE, 6});
336     ASSERT_TRUE(method_id.IsValid());
337     ASSERT_EQ(bytecode_offset, 3U);
338 
339     auto source_location = GetSourcePairLocationWrapper(extractor, method_id, 2);
340     ASSERT_EQ(source_location.path, SOURCE_FILE);
341     ASSERT_EQ(source_location.line, 5U);
342 
343     auto vars = GetLocalVariableInfoWrapper(extractor, method_id, 4);
344     EXPECT_EQ(vars.size(), 2U);
345     ASSERT_EQ(vars[0].name, "local_1");
346     ASSERT_EQ(vars[0].type, "I");
347     ASSERT_EQ(vars[1].name, "local_2");
348     ASSERT_EQ(vars[1].type, "I");
349 }
350 
351 HWTEST_F(ExtractorTest, DebugInfoTestStaticWithRefArg, testing::ext::TestSize.Level0)
352 {
353     const panda_file::File *pf = u_file.get();
354     ASSERT_TRUE(pf != nullptr);
355     DebugInfoExtractor extractor(pf);
356 
357     auto breakpoint1_address = GetBreakpointAddressWrapper(extractor, SourcePairLocation {SOURCE_FILE, 1});
358     ASSERT_FALSE(breakpoint1_address.first.IsValid());
359     auto method_id = GetBreakpointAddressWrapper(extractor, SourcePairLocation {SOURCE_FILE, 14}).first;
360     ASSERT_TRUE(method_id.IsValid());
361 
362     auto vars = GetLocalVariableInfoWrapper(extractor, method_id, 14);
363     EXPECT_EQ(vars.size(), 0U);
364 }
365 
366 HWTEST_F(ExtractorTest, DebugInfoTestNonStaticWithRefArg, testing::ext::TestSize.Level0)
367 {
368     const panda_file::File *pf = u_file.get();
369     ASSERT_TRUE(pf != nullptr);
370     DebugInfoExtractor extractor(pf);
371 
372     auto breakpoint1_address = GetBreakpointAddressWrapper(extractor, SourcePairLocation {SOURCE_FILE, 1});
373     ASSERT_FALSE(breakpoint1_address.first.IsValid());
374     auto method_id = GetBreakpointAddressWrapper(extractor, SourcePairLocation {SOURCE_FILE, 16}).first;
375     ASSERT_TRUE(method_id.IsValid());
376 
377     auto vars = GetLocalVariableInfoWrapper(extractor, method_id, 16);
378     EXPECT_EQ(vars.size(), 0U);
379 }
380 
381 HWTEST_F(ExtractorTest, DebugInfoTestColumnNumber, testing::ext::TestSize.Level0)
382 {
383     const panda_file::File *pf = u_file.get();
384     ASSERT_TRUE(pf != nullptr);
385     DebugInfoExtractor extractor(pf);
386 
387     auto methods = extractor.GetMethodIdList();
388     constexpr uint32_t column_start = 5;
389     uint32_t i = column_start;
390     for (auto const &method_id : methods) {
391         auto &cnt = extractor.GetColumnNumberTable(method_id);
392         for (auto const &col : cnt) {
393             EXPECT_EQ(col.column, i++);
394         }
395     }
396 }
397 }  // namespace panda::panda_file::test
398