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 <gmock/gmock.h>
17 #include <gtest/gtest.h>
18
19 #include "assembler/assembly-parser.h"
20 #include "bytecode_instruction.h"
21 #include "include/thread_scopes.h"
22 #include "libpandafile/bytecode_instruction-inl.h"
23 #include "runtime/include/managed_thread.h"
24 #include "runtime/include/runtime.h"
25 #include "runtime/include/runtime_notification.h"
26 #include "runtime/interpreter/runtime_interface.h"
27
28 namespace ark::interpreter::test {
29
30 class InterpreterTestSwitch : public testing::Test {
31 public:
InterpreterTestSwitch()32 InterpreterTestSwitch()
33 {
34 RuntimeOptions options;
35 options.SetShouldLoadBootPandaFiles(false);
36 options.SetShouldInitializeIntrinsics(false);
37 options.SetRunGcInPlace(true);
38 options.SetVerifyCallStack(false);
39 options.SetInterpreterType("cpp");
40 Runtime::Create(options);
41 }
42
~InterpreterTestSwitch()43 ~InterpreterTestSwitch() override
44 {
45 Runtime::Destroy();
46 }
47
48 NO_COPY_SEMANTIC(InterpreterTestSwitch);
49 NO_MOVE_SEMANTIC(InterpreterTestSwitch);
50 };
51
52 constexpr int32_t RET = 10;
53
EntryPoint(Method *)54 static int32_t EntryPoint(Method * /* unused */)
55 {
56 auto *thread = ManagedThread::GetCurrent();
57 thread->SetCurrentDispatchTable<false>(thread->GetDebugDispatchTable());
58 return RET;
59 }
60
61 class SwitchToDebugListener : public RuntimeListener {
62 public:
63 struct Event {
64 ManagedThread *thread;
65 Method *method;
66 uint32_t bcOffset;
67 };
68
BytecodePcChanged(ManagedThread * thread,Method * method,uint32_t bcOffset)69 void BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset) override
70 {
71 events_.push_back({thread, method, bcOffset});
72 }
73
GetEvents() const74 auto &GetEvents() const
75 {
76 return events_;
77 }
78
79 private:
80 std::vector<Event> events_ {};
81 };
82
83 static auto g_switchToDebugSource = R"(
84 .function i32 f() {
85 ldai 10
86 return
87 }
88
89 .function i32 g() {
90 call f
91 return
92 }
93
94 .function i32 main() {
95 call g
96 return
97 }
98 )";
99
TEST_F(InterpreterTestSwitch,SwitchToDebug)100 TEST_F(InterpreterTestSwitch, SwitchToDebug)
101 {
102 pandasm::Parser p;
103
104 auto source = g_switchToDebugSource;
105
106 auto res = p.Parse(source);
107 auto pf = pandasm::AsmEmitter::Emit(res.Value());
108 ASSERT_NE(pf, nullptr);
109
110 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
111 classLinker->AddPandaFile(std::move(pf));
112 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
113
114 SwitchToDebugListener listener {};
115
116 auto *notificationManager = Runtime::GetCurrent()->GetNotificationManager();
117 notificationManager->AddListener(&listener, RuntimeNotificationManager::BYTECODE_PC_CHANGED);
118
119 std::vector<Value> args;
120 Value v;
121 Method *mainMethod;
122
123 auto *thread = ManagedThread::GetCurrent();
124
125 {
126 ScopedManagedCodeThread smc(thread);
127 PandaString descriptor;
128
129 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
130 ASSERT_NE(klass, nullptr);
131
132 mainMethod = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
133 ASSERT_NE(mainMethod, nullptr);
134
135 Method *fMethod = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
136 ASSERT_NE(fMethod, nullptr);
137
138 fMethod->SetCompiledEntryPoint(reinterpret_cast<const void *>(EntryPoint));
139
140 v = mainMethod->Invoke(thread, args.data());
141 }
142
143 notificationManager->RemoveListener(&listener, RuntimeNotificationManager::BYTECODE_PC_CHANGED);
144
145 ASSERT_EQ(v.GetAs<int32_t>(), RET);
146 ASSERT_EQ(listener.GetEvents().size(), 1U);
147
148 auto &event = listener.GetEvents()[0];
149 EXPECT_EQ(event.thread, thread);
150 EXPECT_EQ(event.method, mainMethod);
151 EXPECT_EQ(event.bcOffset, BytecodeInstruction::Size(BytecodeInstruction::Format::V4_V4_ID16));
152 }
153
154 } // namespace ark::interpreter::test
155