1 /*
2 * Copyright (c) 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 "ecmascript/dfx/hprof/heap_profiler.h"
17 #include "ecmascript/tests/test_helper.h"
18 #include "ecmascript/js_array.h"
19
20 using namespace panda::ecmascript;
21
22 namespace panda::test {
23 class GlobalHandleLeakDetectTest : public testing::Test {
24 public:
SetUpTestCase()25 static void SetUpTestCase()
26 {
27 GTEST_LOG_(INFO) << "SetUpTestCase";
28 }
29
TearDownTestCase()30 static void TearDownTestCase()
31 {
32 GTEST_LOG_(INFO) << "TearDownCase";
33 }
34
SetUp()35 void SetUp() override
36 {
37 JSRuntimeOptions options;
38 options.SetArkProperties(options.GetArkProperties() | GLOBAL_OBJECT_LEAK_CHECK | GLOBAL_PRIMITIVE_LEAK_CHECK);
39 ecmaVm_ = JSNApi::CreateEcmaVM(options);
40 ASSERT_TRUE(ecmaVm_ != nullptr) << "Cannot create EcmaVM";
41 thread_ = ecmaVm_->GetJSThread();
42 thread_->ManagedCodeBegin();
43 scope_ = new EcmaHandleScope(thread_);
44 ecmaVm_->SetEnableForceGC(false);
45 heapProfiler_ = reinterpret_cast<HeapProfiler *>(HeapProfilerInterface::GetInstance(ecmaVm_));
46 }
47
TearDown()48 void TearDown() override
49 {
50 TestHelper::DestroyEcmaVMWithScope(ecmaVm_, scope_);
51 }
52
DumpHeapSnapshot(const std::string & detectResultFilePath)53 void DumpHeapSnapshot(const std::string &detectResultFilePath)
54 {
55 DumpSnapShotOption dumpOption;
56 dumpOption.dumpFormat = DumpFormat::JSON;
57 dumpOption.captureNumericValue = true;
58 dumpOption.isSync = false;
59 FileDescriptorStream stream(-1);
60
61 JSRuntimeOptions &options = ecmaVm_->GetJSOptions();
62 options.SwitchStartGlobalLeakCheck();
63 if (options.EnableGlobalLeakCheck() && options.IsStartGlobalLeakCheck()) {
64 int fd = open(detectResultFilePath.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
65 ASSERT_NE(fd, -1) << strerror(errno);
66 thread_->SetStackTraceFd(fd);
67 }
68 heapProfiler_->DumpHeapSnapshot(&stream, dumpOption);
69 }
70
71 EcmaVM *ecmaVm_ {nullptr};
72 JSThread *thread_ {nullptr};
73 EcmaHandleScope *scope_ {nullptr};
74 HeapProfiler *heapProfiler_ {nullptr};
75 };
76
HWTEST_F_L0(GlobalHandleLeakDetectTest,TestDetectionResult)77 HWTEST_F_L0(GlobalHandleLeakDetectTest, TestDetectionResult)
78 {
79 std::string detectResultFilePath{"global_handle_leak_detect_result.txt"};
80 DumpHeapSnapshot(detectResultFilePath);
81
82 JSHandle<JSTaggedValue> arrayInRoot = JSArray::ArrayCreate(thread_, JSTaggedNumber(1));
83 // New a GlobalHandle without Dispose
84 thread_->NewGlobalHandle(arrayInRoot.GetTaggedType());
85
86 DumpHeapSnapshot(detectResultFilePath);
87
88 std::ifstream file {detectResultFilePath};
89 std::string line;
90 bool hasHandleNumber {false};
91 bool hasLeakObject {false};
92 bool hasLeakFooter {false};
93 while (std::getline(file, line)) {
94 if (!hasHandleNumber && line.find("Global Handle Number") != std::string::npos) {
95 hasHandleNumber = true;
96 continue;
97 }
98 if (!hasLeakObject && line.find("Global maybe leak object address") != std::string::npos) {
99 hasLeakObject = true;
100 continue;
101 }
102 if (!hasLeakFooter && line.find("Global leak check success!") != std::string::npos) {
103 hasLeakFooter = true;
104 continue;
105 }
106 }
107 ASSERT_TRUE(hasHandleNumber);
108 ASSERT_TRUE(hasLeakObject);
109 ASSERT_TRUE(hasLeakFooter);
110 }
111 } // namespace panda::test