1 /**
2 * Copyright (c) 2021-2024 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 <gtest/gtest.h>
17
18 #include <vector>
19
20 #include "runtime/entrypoints/entrypoints.h"
21 #include "runtime/include/method.h"
22 #include "runtime/tooling/debugger.h"
23 #include "runtime/tests/test_utils.h"
24
25 #include "assembly-emitter.h"
26 #include "assembly-parser.h"
27
28 namespace ark::debugger::test {
29
30 class DebuggerTest : public testing::Test {
31 public:
DebuggerTest()32 DebuggerTest()
33 {
34 RuntimeOptions options;
35 options.SetShouldLoadBootPandaFiles(false);
36 options.SetShouldInitializeIntrinsics(false);
37 // this test doesn't check GC logic - just to make test easier without any handles
38 options.SetGcType("epsilon");
39 Runtime::Create(options);
40 thread_ = ark::MTManagedThread::GetCurrent();
41 thread_->ManagedCodeBegin();
42 }
43
~DebuggerTest()44 ~DebuggerTest() override
45 {
46 thread_->ManagedCodeEnd();
47 Runtime::Destroy();
48 }
49
50 NO_COPY_SEMANTIC(DebuggerTest);
51 NO_MOVE_SEMANTIC(DebuggerTest);
52
53 private:
54 ark::MTManagedThread *thread_;
55 };
56
ToPtr(uint64_t v)57 static ObjectHeader *ToPtr(uint64_t v)
58 {
59 return reinterpret_cast<ObjectHeader *>(static_cast<ObjectPointerType>(v));
60 }
61
FromPtr(ObjectHeader * ptr)62 static uint64_t FromPtr(ObjectHeader *ptr)
63 {
64 return static_cast<ObjectPointerType>(reinterpret_cast<uint64_t>(ptr));
65 }
66
67 template <bool IS_DYNAMIC = false>
CreateFrame(size_t nregs,Method * method,Frame * prev)68 static Frame *CreateFrame(size_t nregs, Method *method, Frame *prev)
69 {
70 uint32_t extSz = EMPTY_EXT_FRAME_DATA_SIZE;
71 void *mem = aligned_alloc(8, Frame::GetAllocSize(Frame::GetActualSize<IS_DYNAMIC>(nregs), extSz));
72 return new (Frame::FromExt(mem, extSz)) Frame(mem, method, prev, nregs);
73 }
74
FreeFrame(Frame * frame)75 static void FreeFrame(Frame *frame)
76 {
77 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
78 std::free(frame->GetExt());
79 }
80
81 struct VRegValue {
82 uint64_t value {};
83 bool isRef {};
84 };
85
SetVRegs(Frame * frame,std::vector<VRegValue> & regs)86 static void SetVRegs(Frame *frame, std::vector<VRegValue> ®s)
87 {
88 auto frameHandler = StaticFrameHandler(frame);
89 for (size_t i = 0; i < regs.size(); i++) {
90 if (regs[i].isRef) {
91 frameHandler.GetVReg(i).SetReference(ToPtr(regs[i].value));
92 } else {
93 frameHandler.GetVReg(i).SetPrimitive(static_cast<int64_t>(regs[i].value));
94 }
95 }
96 }
97
98 static constexpr size_t BYTECODE_OFFSET = 0xeeff;
99
100 struct MethodInfo {
101 uint32_t nregs;
102 uint32_t nargs;
103 panda_file::File::EntityId methodId;
104 };
105
CheckFrame(Frame * frame,std::vector<VRegValue> & regs,const MethodInfo & methodInfo)106 static void CheckFrame(Frame *frame, std::vector<VRegValue> ®s, const MethodInfo &methodInfo)
107 {
108 SetVRegs(frame, regs);
109 auto nregs = methodInfo.nregs;
110 auto nargs = methodInfo.nargs;
111 auto methodId = methodInfo.methodId;
112
113 {
114 // NOLINTNEXTLINE(readability-magic-numbers)
115 VRegValue acc {0xaaaaaaaabbbbbbbb, false};
116 frame->GetAccAsVReg().SetPrimitive(static_cast<int64_t>(acc.value));
117 tooling::PtDebugFrame debugFrame(frame->GetMethod(), frame);
118
119 EXPECT_EQ(debugFrame.GetVRegNum(), nregs);
120 EXPECT_EQ(debugFrame.GetArgumentNum(), nargs);
121 EXPECT_EQ(debugFrame.GetMethodId(), methodId);
122 EXPECT_EQ(debugFrame.GetBytecodeOffset(), BYTECODE_OFFSET);
123 EXPECT_EQ(debugFrame.GetAccumulator(), acc.value);
124 EXPECT_EQ(debugFrame.GetAccumulatorKind(), tooling::PtFrame::RegisterKind::PRIMITIVE);
125
126 for (size_t i = 0; i < debugFrame.GetVRegNum(); i++) {
127 EXPECT_EQ(debugFrame.GetVReg(i), regs[i].value);
128 EXPECT_EQ(debugFrame.GetVRegKind(i), regs[i].isRef ? tooling::PtFrame::RegisterKind::REFERENCE
129 : tooling::PtFrame::RegisterKind::PRIMITIVE);
130 }
131
132 for (size_t i = 0; i < debugFrame.GetArgumentNum(); i++) {
133 EXPECT_EQ(debugFrame.GetArgument(i), regs[i + nregs].value);
134 EXPECT_EQ(debugFrame.GetArgumentKind(i), regs[i + nregs].isRef ? tooling::PtFrame::RegisterKind::REFERENCE
135 : tooling::PtFrame::RegisterKind::PRIMITIVE);
136 }
137 }
138
139 {
140 VRegValue acc {FromPtr(ark::mem::AllocateNullifiedPayloadString(1)), true};
141 frame->GetAccAsVReg().SetReference(ToPtr(acc.value));
142 tooling::PtDebugFrame debugFrame(frame->GetMethod(), frame);
143
144 EXPECT_EQ(debugFrame.GetVRegNum(), nregs);
145 EXPECT_EQ(debugFrame.GetArgumentNum(), nargs);
146 EXPECT_EQ(debugFrame.GetMethodId(), methodId);
147 EXPECT_EQ(debugFrame.GetBytecodeOffset(), BYTECODE_OFFSET);
148 EXPECT_EQ(debugFrame.GetAccumulator(), acc.value);
149 EXPECT_EQ(debugFrame.GetAccumulatorKind(), tooling::PtFrame::RegisterKind::REFERENCE);
150
151 for (size_t i = 0; i < debugFrame.GetVRegNum(); i++) {
152 EXPECT_EQ(debugFrame.GetVReg(i), regs[i].value);
153 EXPECT_EQ(debugFrame.GetVRegKind(i), regs[i].isRef ? tooling::PtFrame::RegisterKind::REFERENCE
154 : tooling::PtFrame::RegisterKind::PRIMITIVE);
155 }
156
157 for (size_t i = 0; i < debugFrame.GetArgumentNum(); i++) {
158 EXPECT_EQ(debugFrame.GetArgument(i), regs[i + nregs].value);
159 EXPECT_EQ(debugFrame.GetArgumentKind(i), regs[i + nregs].isRef ? tooling::PtFrame::RegisterKind::REFERENCE
160 : tooling::PtFrame::RegisterKind::PRIMITIVE);
161 }
162 }
163 }
164
TEST_F(DebuggerTest,Frame)165 TEST_F(DebuggerTest, Frame)
166 {
167 pandasm::Parser p;
168
169 auto source = R"(
170 .function void foo(i32 a0, i32 a1) {
171 movi v0, 1
172 movi v1, 2
173 return.void
174 }
175 )";
176
177 std::string srcFilename = "src.pa";
178 auto res = p.Parse(source, srcFilename);
179 ASSERT(p.ShowError().err == pandasm::Error::ErrorType::ERR_NONE);
180
181 auto filePtr = pandasm::AsmEmitter::Emit(res.Value());
182 ASSERT(filePtr != nullptr);
183
184 PandaString descriptor;
185 auto classId = filePtr->GetClassId(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
186 ASSERT_TRUE(classId.IsValid());
187
188 panda_file::ClassDataAccessor cda(*filePtr, classId);
189 panda_file::File::EntityId methodId;
190 panda_file::File::EntityId codeId;
191
192 cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
193 methodId = mda.GetMethodId();
194 ASSERT_TRUE(mda.GetCodeId());
195 codeId = mda.GetCodeId().value();
196 });
197
198 panda_file::CodeDataAccessor codeDataAccessor(*filePtr, codeId);
199 auto nargs = codeDataAccessor.GetNumArgs();
200 auto nregs = codeDataAccessor.GetNumVregs();
201
202 Method method(nullptr, filePtr.get(), methodId, codeId, 0, nargs, nullptr);
203 ark::Frame *frame = test::CreateFrame(nregs + nargs, &method, nullptr);
204 frame->SetBytecodeOffset(BYTECODE_OFFSET);
205
206 // NOLINTBEGIN(readability-magic-numbers)
207 std::vector<VRegValue> regs {{0x1111111122222222, false},
208 {FromPtr(ark::mem::AllocateNullifiedPayloadString(1)), true},
209 {0x3333333344444444, false},
210 {FromPtr(ark::mem::AllocateNullifiedPayloadString(1)), true}};
211 // NOLINTEND(readability-magic-numbers)
212 MethodInfo methodInfo {nregs, nargs, methodId};
213 CheckFrame(frame, regs, methodInfo);
214
215 test::FreeFrame(frame);
216 }
217
218 } // namespace ark::debugger::test
219