• 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 <compiler/compiler_options.h>
17 #include <gtest/gtest.h>
18 
19 #include <bitset>
20 #include <vector>
21 
22 #include "assembly-emitter.h"
23 #include "assembly-parser.h"
24 #include "code_info/code_info_builder.h"
25 #include "libpandabase/utils/cframe_layout.h"
26 #include "libpandabase/utils/utils.h"
27 #include "runtime/bridge/bridge.h"
28 #include "runtime/include/runtime.h"
29 #include "runtime/include/stack_walker-inl.h"
30 #include "runtime/include/thread_scopes.h"
31 #include "runtime/mem/refstorage/global_object_storage.h"
32 #include "runtime/tests/test_utils.h"
33 #include "compiler/tests/panda_runner.h"
34 
35 namespace panda::test {
36 
37 // NOLINTNEXTLINE(google-build-using-namespace)
38 using namespace compiler;
39 
40 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
41 #define HOOK_ASSERT(cond, action)                                                     \
42     do {                                                                              \
43         if (!(cond)) {                                                                \
44             std::cerr << "ASSERT FAILED(" << __LINE__ << "): " << #cond << std::endl; \
45             action;                                                                   \
46         }                                                                             \
47     } while (0)
48 
49 class StackWalkerTest : public testing::Test {
50 public:
51     using Callback = int (*)(uintptr_t, uintptr_t);
52 
StackWalkerTest()53     StackWalkerTest()
54         : defaultCompilerNonOptimizing_(g_options.IsCompilerNonOptimizing()),
55           defaultCompilerRegex_(g_options.GetCompilerRegex())
56     {
57     }
58 
~StackWalkerTest()59     ~StackWalkerTest() override
60     {
61         g_options.SetCompilerNonOptimizing(defaultCompilerNonOptimizing_);
62         g_options.SetCompilerRegex(defaultCompilerRegex_);
63     }
64 
65     NO_COPY_SEMANTIC(StackWalkerTest);
66     NO_MOVE_SEMANTIC(StackWalkerTest);
67 
SetUp()68     void SetUp() override
69     {
70 #ifndef PANDA_COMPILER_ENABLE
71         GTEST_SKIP();
72 #endif
73     }
74 
75     void TestModifyManyVregs(bool isCompiled);
76 
GetMethod(std::string_view methodName)77     static Method *GetMethod(std::string_view methodName)
78     {
79         PandaString descriptor;
80         auto *thread = MTManagedThread::GetCurrent();
81         thread->ManagedCodeBegin();
82         auto *extension = Runtime::GetCurrent()->GetClassLinker()->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
83         auto cls = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
84         thread->ManagedCodeEnd();
85         ASSERT(cls);
86         return cls->GetDirectMethod(utf::CStringAsMutf8(methodName.data()));
87     }
88 
89     static mem::Reference *globalObj_;
90 
91 private:
92     bool defaultCompilerNonOptimizing_;
93     std::string defaultCompilerRegex_;
94 };
95 
96 mem::Reference *StackWalkerTest::globalObj_;
97 
98 template <typename T>
ConvertToU64(T val)99 uint64_t ConvertToU64(T val)
100 {
101     if constexpr (sizeof(T) == sizeof(uint32_t)) {
102         return bit_cast<uint32_t>(val);
103     } else if constexpr (sizeof(T) == sizeof(uint64_t)) {
104         return bit_cast<uint64_t>(val);
105     } else {
106         return static_cast<uint64_t>(val);
107     }
108 }
109 
110 template <Arch ARCH>
ToCalleeRegister(size_t reg)111 int32_t ToCalleeRegister(size_t reg)
112 {
113     return reg + GetFirstCalleeReg(ARCH, false);
114 }
115 
116 template <Arch ARCH>
ToCalleeFpRegister(size_t reg)117 int32_t ToCalleeFpRegister(size_t reg)
118 {
119     return reg + GetFirstCalleeReg(ARCH, true);
120 }
121 
TEST_F(StackWalkerTest,ModifyVreg)122 TEST_F(StackWalkerTest, ModifyVreg)
123 {
124     auto source = R"(
125         .function i32 main() {
126             movi.64 v0, 27
127         try_begin:
128             call.short testa, v0
129             jmp try_end
130         try_end:
131             return
132         .catchall try_begin, try_end, try_end
133         }
134         .function i32 testa(i64 a0) {
135             call.short testb, a0
136             return
137         }
138         .function i32 testb(i64 a0) {
139             call.short testc, a0
140             return
141         }
142         .function i32 testc(i64 a0) {
143             lda a0
144         try_begin:
145             call.short hook  # change vregs in all frames
146             jnez exit
147             call.short hook  # verify vregs in all frames
148             jmp exit
149         exit:
150             return
151         .catchall try_begin, exit, exit
152         }
153         .function i32 hook() {
154             ldai 1
155             return
156         }
157     )";
158 
159     PandaRunner runner;
160     runner.GetRuntimeOptions().SetCompilerHotnessThreshold(0);
161     runner.GetCompilerOptions().SetCompilerNonOptimizing(true);
162     runner.GetCompilerOptions().SetCompilerRematConst(false);
163     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::testb|_GLOBAL::hook).*");
164     [[maybe_unused]] static constexpr std::array<uint64_t, 3> FRAME_VALUES = {0x123456789abcdef, 0xaaaabbbbccccdddd,
165                                                                               0xabcdef20};
166     static int runCount = 0;
167     runner.Run(source, [](uintptr_t lr, [[maybe_unused]] uintptr_t fp) -> int {
168         StackWalker walker(reinterpret_cast<void *>(fp), true, lr);
169         bool success = false;
170         walker.NextFrame();
171         if (runCount == 0) {
172             bool wasSet = false;
173             HOOK_ASSERT(!walker.IsCFrame(), return 1);
174             success = walker.IterateVRegsWithInfo([&wasSet, &walker](const auto &regInfo, const auto &reg) {
175                 if (!regInfo.IsAccumulator()) {
176                     HOOK_ASSERT(reg.GetLong() == 27L, return false);
177                     walker.SetVRegValue(regInfo, FRAME_VALUES[0]);
178                     wasSet = true;
179                 }
180                 return true;
181             });
182             HOOK_ASSERT(success, return 1);
183             HOOK_ASSERT(wasSet, return 1);
184 
185             walker.NextFrame();
186             HOOK_ASSERT(walker.IsCFrame(), return 1);
187             success = walker.IterateVRegsWithInfo([&walker](const auto &regInfo, const auto &reg) {
188                 if (!regInfo.IsAccumulator()) {
189                     HOOK_ASSERT(reg.GetLong() == 27L, return false);
190                     walker.SetVRegValue(regInfo, FRAME_VALUES[1]);
191                 }
192                 return true;
193             });
194             HOOK_ASSERT(success, return 1);
195 
196             walker.NextFrame();
197             HOOK_ASSERT(walker.IsCFrame(), return 1);
198             success = walker.IterateVRegsWithInfo([&walker](const auto &regInfo, const auto &reg) {
199                 if (!regInfo.IsAccumulator()) {
200                     HOOK_ASSERT(reg.GetLong() == 27L, return true);
201                     walker.SetVRegValue(regInfo, FRAME_VALUES[2U]);
202                 }
203                 return true;
204             });
205             HOOK_ASSERT(success, return 1);
206         } else if (runCount == 1) {
207             HOOK_ASSERT(!walker.IsCFrame(), return 1);
208             success = walker.IterateVRegsWithInfo([](const auto &regInfo, const auto &reg) {
209                 if (!regInfo.IsAccumulator()) {
210                     HOOK_ASSERT(reg.GetLong() == bit_cast<int64_t>(FRAME_VALUES[0]), return true);
211                 }
212                 return true;
213             });
214             HOOK_ASSERT(success, return 1);
215 
216             walker.NextFrame();
217             HOOK_ASSERT(walker.IsCFrame(), return 1);
218             success = walker.IterateVRegsWithInfo([](const auto &regInfo, const auto &reg) {
219                 if (!regInfo.IsAccumulator()) {
220                     HOOK_ASSERT(reg.GetLong() == bit_cast<int64_t>(FRAME_VALUES[1]), return true);
221                 }
222                 return true;
223             });
224             HOOK_ASSERT(success, return 1);
225 
226             walker.NextFrame();
227             HOOK_ASSERT(walker.IsCFrame(), return 1);
228             success = walker.IterateVRegsWithInfo([](const auto &regInfo, const auto &reg) {
229                 if (!regInfo.IsAccumulator()) {
230                     HOOK_ASSERT(reg.GetLong() == bit_cast<int64_t>(FRAME_VALUES[2U]), return true);
231                 }
232                 return true;
233             });
234             HOOK_ASSERT(success, return 1);
235         } else {
236             return 1;
237         }
238         runCount++;
239         return 0;
240     });
241     ASSERT_EQ(runCount, 2_I);
242 }
243 
TestModifyManyVregs(bool isCompiled)244 void StackWalkerTest::TestModifyManyVregs(bool isCompiled)
245 {
246     auto source = R"(
247         .function i32 main() {
248             movi.64 v0, 5
249         try_begin:
250             call.short test
251             jmp try_end
252         try_end:
253             return
254 
255         .catchall try_begin, try_end, try_end
256         }
257 
258         .function i32 test() {
259             movi.64 v1, 1
260             movi.64 v2, 2
261             movi.64 v3, 3
262             movi.64 v4, 4
263             movi.64 v5, 5
264             movi.64 v6, 6
265             movi.64 v7, 7
266             movi.64 v8, 8
267             movi.64 v9, 9
268             movi.64 v10, 10
269             movi.64 v11, 11
270             movi.64 v12, 12
271             movi.64 v13, 13
272             movi.64 v14, 14
273             movi.64 v15, 15
274             movi.64 v16, 16
275             movi.64 v17, 17
276             movi.64 v18, 18
277             movi.64 v19, 19
278             movi.64 v20, 20
279             movi.64 v21, 21
280             movi.64 v22, 22
281             movi.64 v23, 23
282             movi.64 v24, 24
283             movi.64 v25, 25
284             movi.64 v26, 26
285             movi.64 v27, 27
286             movi.64 v28, 28
287             movi.64 v29, 29
288             movi.64 v30, 30
289             movi.64 v31, 31
290         try_begin:
291             mov v0, v31
292             newarr v0, v0, i32[]
293             call.short stub
294             jmp try_end
295         try_end:
296             return
297 
298         .catchall try_begin, try_end, try_end
299         }
300 
301         .function i32 stub() {
302         try_begin:
303             call.short hook  # change vregs in all frames
304             jnez exit
305             call.short hook  # verify vregs in all frames
306             jmp exit
307         exit:
308             return
309 
310         .catchall try_begin, exit, exit
311         }
312 
313         .function i32 hook() {
314             ldai 1
315             return
316         }
317     )";
318 
319     static bool firstRun;
320     static bool compiled;
321 
322     PandaRunner runner;
323     runner.GetRuntimeOptions().SetCompilerHotnessThreshold(0);
324     runner.GetCompilerOptions().SetCompilerNonOptimizing(true);
325 
326     if (!isCompiled) {
327         runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main)(?!_GLOBAL::test)(?!_GLOBAL::hook).*");
328     }
329 
330     firstRun = true;
331     compiled = isCompiled;
332     runner.Run(source, [](uintptr_t lr, [[maybe_unused]] uintptr_t fp) -> int {
333         StackWalker walker(reinterpret_cast<void *>(fp), true, lr);
334 
335         HOOK_ASSERT(walker.GetMethod()->GetFullName() == "_GLOBAL::stub", return 1);
336         walker.NextFrame();
337         HOOK_ASSERT(walker.GetMethod()->GetFullName() == "_GLOBAL::test", return 1);
338         HOOK_ASSERT(walker.IsCFrame() == compiled, return 1);
339 
340         int regIndex = 1;
341         bool success = false;
342         if (firstRun) {
343             auto storage = Runtime::GetCurrent()->GetPandaVM()->GetGlobalObjectStorage();
344             StackWalkerTest::globalObj_ =
345                 storage->Add(panda::mem::AllocateNullifiedPayloadString(1), mem::Reference::ObjectType::GLOBAL);
346         }
347         auto obj = Runtime::GetCurrent()->GetPandaVM()->GetGlobalObjectStorage()->Get(StackWalkerTest::globalObj_);
348         if (firstRun) {
349             success = walker.IterateVRegsWithInfo([&regIndex, &walker, &obj](const auto &regInfo, const auto &reg) {
350                 if (!regInfo.IsAccumulator()) {
351                     if (reg.HasObject()) {
352                         HOOK_ASSERT(reg.GetReference() != nullptr, return false);
353                         walker.SetVRegValue(regInfo, obj);
354                     } else if (regInfo.GetLocation() != VRegInfo::Location::CONSTANT) {
355                         // frame_size is more than nregs_ now for inst `V4_V4_ID16` and `V4_V4_V4_V4_ID16`
356                         auto regIdx = regInfo.GetIndex();
357                         if (regIdx < walker.GetMethod()->GetNumVregs()) {
358                             HOOK_ASSERT(regIdx == reg.GetLong(), return false);
359                         }
360                         // NOLINTNEXTLINE(readability-magic-numbers)
361                         walker.SetVRegValue(regInfo, regIdx + 100000000000L);
362                     }
363                     regIndex++;
364                 }
365                 return true;
366             });
367             HOOK_ASSERT(success, return 1);
368             HOOK_ASSERT(regIndex >= 32_I, return 1);
369             firstRun = false;
370         } else {
371             success = walker.IterateVRegsWithInfo([&regIndex, &obj](const auto &regInfo, const auto &reg) {
372                 if (!regInfo.IsAccumulator()) {
373                     if (reg.HasObject()) {
374                         HOOK_ASSERT((reg.GetReference() == reinterpret_cast<ObjectHeader *>(Low32Bits(obj))),
375                                     return false);
376                     } else {
377                         if (regInfo.GetLocation() != VRegInfo::Location::CONSTANT) {
378                             HOOK_ASSERT(reg.GetLong() == (regInfo.GetIndex() + 100000000000), return false);
379                         }
380                         regIndex++;
381                     }
382                 }
383                 return true;
384             });
385             HOOK_ASSERT(success, return 1);
386             HOOK_ASSERT(regIndex >= 32_I, return 1);
387         }
388 
389         HOOK_ASSERT(success, return 1);
390         return 0;
391     });
392 }
393 
TEST_F(StackWalkerTest,ModifyMultipleVregs)394 TEST_F(StackWalkerTest, ModifyMultipleVregs)
395 {
396     if constexpr (ArchTraits<RUNTIME_ARCH>::SUPPORT_DEOPTIMIZATION) {
397         TestModifyManyVregs(true);
398         TestModifyManyVregs(false);
399     }
400 }
401 
TEST_F(StackWalkerTest,ThrowExceptionThroughMultipleFrames)402 TEST_F(StackWalkerTest, ThrowExceptionThroughMultipleFrames)
403 {
404     auto source = R"(
405         .record E {}
406 
407         .function u1 f4() {
408             newobj v0, E
409             throw v0
410             return
411         }
412 
413         .function u1 f3() {
414             call f4
415             return
416         }
417 
418         .function u1 f2() {
419             call f3
420             return
421         }
422 
423         .function u1 f1() {
424             call f2
425             return
426         }
427 
428         .function u1 main() {
429         try_begin:
430             movi v0, 123
431             call f1
432             ldai 1
433             return
434         try_end:
435 
436         catch_block1_begin:
437             movi v1, 123
438             lda v0
439             jne v1, exit_1
440             ldai 0
441             return
442             exit_1:
443             ldai 1
444             return
445 
446         .catch E, try_begin, try_end, catch_block1_begin
447         }
448     )";
449 
450     PandaRunner runner;
451     runner.GetRuntimeOptions().SetCompilerHotnessThreshold(0);
452     runner.GetCompilerOptions().SetCompilerNonOptimizing(true);
453 
454     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f4|_GLOBAL::f1|_GLOBAL::f2|_GLOBAL::f3).*");
455     runner.Run(source, nullptr);
456 
457     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f4|_GLOBAL::f1|_GLOBAL::f2).*");
458     runner.Run(source, nullptr);
459 
460     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f4|_GLOBAL::f2).*");
461     runner.Run(source, nullptr);
462 
463     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f4).*");
464     runner.Run(source, nullptr);
465 
466     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f1|_GLOBAL::f2|_GLOBAL::f3).*");
467     runner.Run(source, nullptr);
468 
469     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main).*");
470     runner.Run(source, nullptr);
471 
472     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f4|_GLOBAL::f1|_GLOBAL::f2|_GLOBAL::f3).*");
473     runner.Run(source, nullptr);
474 
475     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f4|_GLOBAL::f1|_GLOBAL::f2).*");
476     runner.Run(source, nullptr);
477 
478     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f4|_GLOBAL::f2).*");
479     runner.Run(source, nullptr);
480 
481     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f4).*");
482     runner.Run(source, nullptr);
483 
484     runner.GetCompilerOptions().SetCompilerRegex(".*");
485     runner.Run(source, nullptr);
486 
487     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f1|_GLOBAL::f2|_GLOBAL::f3).*");
488     runner.Run(source, nullptr);
489 
490     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f1|_GLOBAL::f2).*");
491     runner.Run(source, nullptr);
492 
493     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f2).*");
494     runner.Run(source, nullptr);
495 
496     runner.GetCompilerOptions().SetCompilerRegex(".*");
497     runner.Run(source, nullptr);
498 }
499 
TEST_F(StackWalkerTest,CatchInCompiledCode)500 TEST_F(StackWalkerTest, CatchInCompiledCode)
501 {
502     auto source = R"(
503         .record panda.String <external>
504         .record panda.ArrayIndexOutOfBoundsException <external>
505 
506         .function i32 f2() {
507             movi v0, 4
508             newarr v1, v0, i64[]
509             ldai 42
510             ldarr v1 # BoundsException
511             return
512         }
513 
514         .function i32 f1() {
515         try_begin:
516             call f2
517             ldai 1
518             return
519         try_end:
520 
521         catch_block1_begin:
522             ldai 123
523             return
524 
525         .catch panda.ArrayIndexOutOfBoundsException, try_begin, try_end, catch_block1_begin
526         }
527 
528         .function u1 main() {
529             call f1
530             movi v0, 123
531             jne v0, exit_1
532             ldai 0
533             return
534             exit_1:
535             ldai 1
536             return
537         }
538     )";
539 
540     PandaRunner runner;
541     runner.GetRuntimeOptions().SetCompilerHotnessThreshold(0);
542     runner.GetCompilerOptions().SetCompilerNonOptimizing(true);
543 
544     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f2).*");
545     runner.Run(source, nullptr);
546 
547     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f1).*");
548     runner.Run(source, nullptr);
549 
550     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main).*");
551     runner.Run(source, nullptr);
552 
553     runner.GetCompilerOptions().SetCompilerRegex(".*");
554     runner.Run(source, nullptr);
555 }
556 
557 }  // namespace panda::test
558