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 <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 panda::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_ = panda::MTManagedThread::GetCurrent();
41 thread_->ManagedCodeBegin();
42 }
43
~DebuggerTest()44 ~DebuggerTest()
45 {
46 thread_->ManagedCodeEnd();
47 Runtime::Destroy();
48 }
49
50 protected:
51 panda::MTManagedThread *thread_;
52 };
53
ToPtr(uint64_t v)54 static ObjectHeader *ToPtr(uint64_t v)
55 {
56 return reinterpret_cast<ObjectHeader *>(static_cast<object_pointer_type>(v));
57 }
58
FromPtr(ObjectHeader * ptr)59 static uint64_t FromPtr(ObjectHeader *ptr)
60 {
61 return static_cast<object_pointer_type>(reinterpret_cast<uint64_t>(ptr));
62 }
63
64 template <bool is_dynamic = false>
CreateFrame(size_t nregs,Method * method,Frame * prev)65 static Frame *CreateFrame(size_t nregs, Method *method, Frame *prev)
66 {
67 uint32_t ext_sz = EmptyExtFrameDataSize;
68 void *mem = aligned_alloc(8, Frame::GetAllocSize(Frame::GetActualSize<is_dynamic>(nregs), ext_sz));
69 return new (Frame::FromExt(mem, ext_sz)) Frame(mem, method, prev, nregs);
70 }
71
FreeFrame(Frame * frame)72 static void FreeFrame(Frame *frame)
73 {
74 std::free(frame->GetExt());
75 }
76
TEST_F(DebuggerTest,Frame)77 TEST_F(DebuggerTest, Frame)
78 {
79 pandasm::Parser p;
80
81 auto source = R"(
82 .function void foo(i32 a0, i32 a1) {
83 movi v0, 1
84 movi v1, 2
85 return.void
86 }
87 )";
88
89 std::string src_filename = "src.pa";
90 auto res = p.Parse(source, src_filename);
91 ASSERT(p.ShowError().err == pandasm::Error::ErrorType::ERR_NONE);
92
93 auto file_ptr = pandasm::AsmEmitter::Emit(res.Value());
94 ASSERT(file_ptr != nullptr);
95
96 PandaString descriptor;
97 auto class_id = file_ptr->GetClassId(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
98 ASSERT_TRUE(class_id.IsValid());
99
100 panda_file::ClassDataAccessor cda(*file_ptr, class_id);
101 panda_file::File::EntityId method_id;
102 panda_file::File::EntityId code_id;
103
104 cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
105 method_id = mda.GetMethodId();
106 ASSERT_TRUE(mda.GetCodeId());
107 code_id = mda.GetCodeId().value();
108 });
109
110 panda_file::CodeDataAccessor code_data_accessor(*file_ptr, code_id);
111 auto nargs = code_data_accessor.GetNumArgs();
112 auto nregs = code_data_accessor.GetNumVregs();
113
114 constexpr size_t BYTECODE_OFFSET = 0xeeff;
115
116 Method method(nullptr, file_ptr.get(), method_id, code_id, 0, nargs, nullptr);
117 panda::Frame *frame = test::CreateFrame(nregs + nargs, &method, nullptr);
118 frame->SetBytecodeOffset(BYTECODE_OFFSET);
119
120 struct VRegValue {
121 uint64_t value {};
122 bool is_ref {};
123 };
124
125 std::vector<VRegValue> regs {{0x1111111122222222, false},
126 {FromPtr(panda::mem::AllocateNullifiedPayloadString(1)), true},
127 {0x3333333344444444, false},
128 {FromPtr(panda::mem::AllocateNullifiedPayloadString(1)), true}};
129
130 auto frame_handler = StaticFrameHandler(frame);
131 for (size_t i = 0; i < regs.size(); i++) {
132 if (regs[i].is_ref) {
133 frame_handler.GetVReg(i).SetReference(ToPtr(regs[i].value));
134 } else {
135 frame_handler.GetVReg(i).SetPrimitive(static_cast<int64_t>(regs[i].value));
136 }
137 }
138
139 {
140 VRegValue acc {0xaaaaaaaabbbbbbbb, false};
141 frame->GetAccAsVReg().SetPrimitive(static_cast<int64_t>(acc.value));
142 tooling::PtDebugFrame debug_frame(frame->GetMethod(), frame);
143
144 EXPECT_EQ(debug_frame.GetVRegNum(), nregs);
145 EXPECT_EQ(debug_frame.GetArgumentNum(), nargs);
146 EXPECT_EQ(debug_frame.GetMethodId(), method_id);
147 EXPECT_EQ(debug_frame.GetBytecodeOffset(), BYTECODE_OFFSET);
148 EXPECT_EQ(debug_frame.GetAccumulator(), acc.value);
149
150 for (size_t i = 0; i < debug_frame.GetVRegNum(); i++) {
151 EXPECT_EQ(debug_frame.GetVReg(i), regs[i].value);
152 }
153
154 for (size_t i = 0; i < debug_frame.GetArgumentNum(); i++) {
155 EXPECT_EQ(debug_frame.GetArgument(i), regs[i + nregs].value);
156 }
157 }
158
159 {
160 VRegValue acc {FromPtr(panda::mem::AllocateNullifiedPayloadString(1)), true};
161 frame->GetAccAsVReg().SetReference(ToPtr(acc.value));
162 tooling::PtDebugFrame debug_frame(frame->GetMethod(), frame);
163
164 EXPECT_EQ(debug_frame.GetVRegNum(), nregs);
165 EXPECT_EQ(debug_frame.GetArgumentNum(), nargs);
166 EXPECT_EQ(debug_frame.GetMethodId(), method_id);
167 EXPECT_EQ(debug_frame.GetBytecodeOffset(), BYTECODE_OFFSET);
168 EXPECT_EQ(debug_frame.GetAccumulator(), acc.value);
169
170 for (size_t i = 0; i < debug_frame.GetVRegNum(); i++) {
171 EXPECT_EQ(debug_frame.GetVReg(i), regs[i].value);
172 }
173
174 for (size_t i = 0; i < debug_frame.GetArgumentNum(); i++) {
175 EXPECT_EQ(debug_frame.GetArgument(i), regs[i + nregs].value);
176 }
177 }
178
179 test::FreeFrame(frame);
180 }
181
182 } // namespace panda::debugger::test
183