• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-2025 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 "debugger/debug_info_cache.h"
17 
18 #include "gtest/gtest.h"
19 
20 #include "assembly-emitter.h"
21 #include "assembly-parser.h"
22 #include "runtime.h"
23 #include "runtime_options.h"
24 #include "thread_scopes.h"
25 
26 #include "test_frame.h"
27 
28 // NOLINTBEGIN
29 
30 namespace ark::tooling::inspector::test {
31 
32 static constexpr const char *g_source = R"(
33     .record Test {}
34 
35     .function i32 Test.foo(u64 a0, u64 a1) {
36         mov v0, v1         # line 2, offset 0, 1
37         mov v100, v101     # line 3, offset 2, 3, 4
38         movi v0, 4         # line 4, offset 5, 6
39         ldai 222           # line 5, offset 7, 8, 9
40         return             # line 6, offset 10
41     }
42 )";
43 
44 class DebugInfoCacheTest : public testing::Test {
45 protected:
SetUpTestSuite()46     static void SetUpTestSuite()
47     {
48         pandasm::Parser p;
49 
50         auto res = p.Parse(g_source, SOURCE_FILE_NAME.data());
51         ASSERT_TRUE(res.HasValue());
52         ASSERT_TRUE(pandasm::AsmEmitter::Emit(ASM_FILE_NAME.data(), res.Value()));
53         auto pf = panda_file::OpenPandaFile(ASM_FILE_NAME);
54         ASSERT_NE(pf, nullptr);
55 
56         cache.AddPandaFile(*pf, true);
57 
58         RuntimeOptions options;
59         options.SetShouldInitializeIntrinsics(false);
60         options.SetShouldLoadBootPandaFiles(false);
61         Runtime::Create(options);
62 
63         thread_ = ManagedThread::GetCurrent();
64         {
65             ScopedManagedCodeThread s(thread_);
66 
67             ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
68             classLinker->AddPandaFile(std::move(pf));
69 
70             PandaString descriptorHolder;
71             const auto *descriptor = ClassHelper::GetDescriptor(utf::CStringAsMutf8("Test"), &descriptorHolder);
72             auto *ext = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
73             Class *klass = ext->GetClass(descriptor, true, ext->GetBootContext());
74             ASSERT_NE(klass, nullptr);
75 
76             auto methods = klass->GetMethods();
77             ASSERT_EQ(methods.size(), 1);
78             methodFoo = &methods[0];
79         }
80     }
81 
TearDownTestSuite()82     static void TearDownTestSuite()
83     {
84         Runtime::Destroy();
85     }
86 
87     static constexpr std::string_view ASM_FILE_NAME = "source.abc";
88     // This test intentionally sets empty source file name to ensure that disassembly is used for debug info
89     static constexpr std::string_view SOURCE_FILE_NAME = "";
90     static DebugInfoCache cache;
91     static ManagedThread *thread_;
92     static Method *methodFoo;
93 };
94 
95 DebugInfoCache DebugInfoCacheTest::cache {};
96 ManagedThread *DebugInfoCacheTest::thread_ = nullptr;
97 Method *DebugInfoCacheTest::methodFoo = nullptr;
98 
TEST_F(DebugInfoCacheTest,GetCurrentLineLocations)99 TEST_F(DebugInfoCacheTest, GetCurrentLineLocations)
100 {
101     auto fr0 = TestFrame(methodFoo, 2U);   // offset 2, line 3 of function
102     auto fr1 = TestFrame(methodFoo, 6U);   // offset 6, line 4 of function
103     auto fr2 = TestFrame(methodFoo, 10U);  // offset 10, line 6 of function
104 
105     auto curr = cache.GetCurrentLineLocations(fr0);
106     ASSERT_EQ(curr.size(), 3U);
107     ASSERT_NE(curr.find(PtLocation(ASM_FILE_NAME.data(), methodFoo->GetFileId(), 2U)), curr.end());
108     ASSERT_NE(curr.find(PtLocation(ASM_FILE_NAME.data(), methodFoo->GetFileId(), 3U)), curr.end());
109     ASSERT_NE(curr.find(PtLocation(ASM_FILE_NAME.data(), methodFoo->GetFileId(), 4U)), curr.end());
110 
111     curr = cache.GetCurrentLineLocations(fr1);
112     ASSERT_EQ(curr.size(), 2U);
113     ASSERT_NE(curr.find(PtLocation(ASM_FILE_NAME.data(), methodFoo->GetFileId(), 5U)), curr.end());
114     ASSERT_NE(curr.find(PtLocation(ASM_FILE_NAME.data(), methodFoo->GetFileId(), 6U)), curr.end());
115 
116     curr = cache.GetCurrentLineLocations(fr2);
117     ASSERT_EQ(curr.size(), 1);
118     ASSERT_NE(curr.find(PtLocation(ASM_FILE_NAME.data(), methodFoo->GetFileId(), 10U)), curr.end());
119 }
120 
TEST_F(DebugInfoCacheTest,GetLocals)121 TEST_F(DebugInfoCacheTest, GetLocals)
122 {
123     static constexpr size_t ARGUMENTS_COUNT = 2U;
124     static constexpr size_t LOCALS_COUNT = 103U;
125 
126     auto fr0 = TestFrame(methodFoo, 2U);  // offset 2, line 3 of function
127 
128     for (size_t i = 0; i < ARGUMENTS_COUNT; i++) {
129         fr0.SetArgument(i, i + 1);
130         fr0.SetArgumentKind(i, PtFrame::RegisterKind::PRIMITIVE);
131     }
132     for (size_t i = 0; i < LOCALS_COUNT; i++) {
133         fr0.SetVReg(i, i);
134         fr0.SetVRegKind(i, PtFrame::RegisterKind::PRIMITIVE);
135     }
136     auto mapLocals = cache.GetLocals(fr0);
137     ASSERT_EQ(ARGUMENTS_COUNT + LOCALS_COUNT, mapLocals.size());
138 
139     EXPECT_NO_THROW(mapLocals.at("a0"));
140     EXPECT_NO_THROW(mapLocals.at("a1"));
141     EXPECT_NO_THROW(mapLocals.at("v101"));
142 
143     ASSERT_EQ(mapLocals.at("a0").GetAsU64(), 1U);
144     ASSERT_EQ(mapLocals.at("a1").GetAsU64(), 2U);
145     ASSERT_EQ(mapLocals.at("v101").GetAsU64(), 101U);
146 }
147 
TEST_F(DebugInfoCacheTest,GetSourceLocation)148 TEST_F(DebugInfoCacheTest, GetSourceLocation)
149 {
150     auto fr0 = TestFrame(methodFoo, 2U);  // offset 2, line 3 of function
151     auto fr1 = TestFrame(methodFoo, 6U);  // offset 6, line 4 of function
152 
153     std::string_view disasm_file;
154     std::string_view method_name;
155     size_t line_number = 0;
156 
157     cache.GetSourceLocation(fr0, disasm_file, method_name, line_number);
158     ASSERT_NE(disasm_file.find(ASM_FILE_NAME.data()), std::string::npos);
159     ASSERT_EQ(method_name, "foo");
160     ASSERT_EQ(line_number, 3U);
161 
162     cache.GetSourceLocation(fr1, disasm_file, method_name, line_number);
163     ASSERT_NE(disasm_file.find(ASM_FILE_NAME.data()), std::string::npos);
164     ASSERT_EQ(method_name, "foo");
165     ASSERT_EQ(line_number, 4U);
166 
167     auto set_locs = cache.GetContinueToLocations(disasm_file, 4U);
168     ASSERT_EQ(set_locs.size(), 2U);
169     ASSERT_NE(set_locs.find(PtLocation(ASM_FILE_NAME.data(), methodFoo->GetFileId(), 6U)), set_locs.end());
170     ASSERT_NE(set_locs.find(PtLocation(ASM_FILE_NAME.data(), methodFoo->GetFileId(), 5U)), set_locs.end());
171 
172     set_locs = cache.GetContinueToLocations(disasm_file, 6U);
173     ASSERT_EQ(set_locs.size(), 1);
174     ASSERT_NE(set_locs.find(PtLocation(ASM_FILE_NAME.data(), methodFoo->GetFileId(), 10U)), set_locs.end());
175 
176     set_locs = cache.GetContinueToLocations(disasm_file, 1);
177     ASSERT_EQ(set_locs.size(), 0);
178 
179     auto valid_locs = cache.GetValidLineNumbers(disasm_file, 0U, 100U, false);
180     ASSERT_EQ(valid_locs.size(), 5U);
181 
182     ASSERT_NE(valid_locs.find(2U), valid_locs.end());
183     ASSERT_NE(valid_locs.find(3U), valid_locs.end());
184     ASSERT_NE(valid_locs.find(4U), valid_locs.end());
185     ASSERT_NE(valid_locs.find(5U), valid_locs.end());
186     ASSERT_NE(valid_locs.find(6U), valid_locs.end());
187 
188     auto s = cache.GetSourceCode(disasm_file);
189     ASSERT_NE(s.find(".function i32 Test.foo(u64 a0, u64 a1)"), std::string::npos);
190 
191     s = cache.GetSourceCode("source.pa");
192     ASSERT_TRUE(s.empty());
193 
194     std::set<std::string_view> sets;
195     auto breaks = cache.GetBreakpointLocations([](auto) { return true; }, 4U, sets);
196     ASSERT_EQ(breaks.size(), 1);
197     ASSERT_EQ(sets.size(), 1);
198     ASSERT_EQ(*sets.begin(), disasm_file);
199 
200     ASSERT_NE(std::find(breaks.begin(), breaks.end(), PtLocation(ASM_FILE_NAME.data(), methodFoo->GetFileId(), 5U)),
201               breaks.end());
202 }
203 
204 }  // namespace ark::tooling::inspector::test
205 
206 // NOLINTEND
207