1 /*
2  * Copyright (c) 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 "thread_sampler_test.h"
17 
18 #include <csignal>
19 #include <fstream>
20 #include <sstream>
21 #include <dlfcn.h>
22 
23 #include "watchdog.h"
24 
25 using namespace testing::ext;
26 namespace OHOS {
27 namespace HiviewDFX {
28 const char* LIB_THREAD_SAMPLER_PATH = "libthread_sampler.z.so";
29 typedef int (*ThreadSamplerInitFunc)(int);
30 typedef int32_t (*ThreadSamplerSampleFunc)();
31 typedef int (*ThreadSamplerCollectFunc)(char*, size_t, int);
32 typedef void (*ThreadSamplerDeinitFunc)();
33 
34 constexpr int SAMPLE_CNT = 10;
35 constexpr int INTERVAL = 100;
36 constexpr size_t STACK_LENGTH = 32 * 1024;
37 constexpr int MILLSEC_TO_MICROSEC = 1000;
38 
SetUpTestCase(void)39 void ThreadSamplerTest::SetUpTestCase(void)
40 {
41     printf("SetUpTestCase.\n");
42 }
43 
TearDownTestCase(void)44 void ThreadSamplerTest::TearDownTestCase(void)
45 {
46     printf("TearDownTestCase.\n");
47     Watchdog::GetInstance().StopWatchdog();
48 }
49 
SetUp(void)50 void ThreadSamplerTest::SetUp(void)
51 {
52     printf("SetUp.\n");
53 }
54 
TearDown(void)55 void ThreadSamplerTest::TearDown(void)
56 {
57     printf("TearDown.\n");
58 }
59 
WaitSomeTime()60 void WaitSomeTime()
61 {
62     int waitDelay = 3;
63     int32_t left = (INTERVAL * SAMPLE_CNT + INTERVAL) / MILLSEC_TO_MICROSEC + waitDelay;
64     int32_t end = time(nullptr) + left;
65     while (left > 0) {
66         left = end - time(nullptr);
67     }
68     sleep((INTERVAL * SAMPLE_CNT + INTERVAL) / MILLSEC_TO_MICROSEC + waitDelay);
69 }
70 
GetMMapSizeAndName(const std::string& checkName, std::string& mmapName)71 uint32_t GetMMapSizeAndName(const std::string& checkName, std::string& mmapName)
72 {
73     uint64_t size = 0;
74     mmapName = "";
75     std::ifstream mapsFile("/proc/self/maps");
76     std::string line;
77     int base = 16;
78     while (getline(mapsFile, line)) {
79         std::istringstream iss(line);
80         std::string addrs;
81         std::string permissions;
82         std::string offset;
83         std::string devices;
84         std::string inode;
85         std::string pathname;
86         iss >> addrs >> permissions >> offset >> devices >> inode >> pathname;
87         if (pathname.find(checkName) != std::string::npos) {
88             std::string start = addrs.substr(0, addrs.find('-'));
89             std::string end = addrs.substr(addrs.find('-') + 1);
90             size = std::stoul(end, nullptr, base) - std::stoul(start, nullptr, base);
91             mmapName = pathname;
92         }
93     }
94     return static_cast<uint32_t>(size);
95 }
96 
FunctionOpen(void* funcHandler, const char* funcName)97 void* FunctionOpen(void* funcHandler, const char* funcName)
98 {
99     if (funcHandler == nullptr) {
100         printf("open library failed.");
101         return nullptr;
102     }
103     dlerror();
104     char* err = nullptr;
105     void* func = dlsym(funcHandler, funcName);
106     if ((err = dlerror()) != nullptr) {
107         printf("dlopen %s failed. %s", funcName, err);
108         dlclose(funcHandler);
109         funcHandler = nullptr;
110         return nullptr;
111     }
112     return func;
113 }
114 
115 /**
116  * @tc.name: ThreadSamplerTest_001
117  * @tc.desc: sample thread SAMPLE_CNT times and check the stacktrace
118  * @tc.type: FUNC
119  * @tc.require
120  */
HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_001, TestSize.Level3)121 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_001, TestSize.Level3)
122 {
123     printf("ThreadSamplerTest_001\n");
124     printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
125 
126     void* funcHandler = dlopen(LIB_THREAD_SAMPLER_PATH, RTLD_LAZY);
127     if (funcHandler == nullptr) {
128         printf("open library failed.");
129     }
130     dlerror();
131     auto initFunc = reinterpret_cast<ThreadSamplerInitFunc>(FunctionOpen(funcHandler, "ThreadSamplerInit"));
132     auto sampleFunc = reinterpret_cast<ThreadSamplerSampleFunc>(FunctionOpen(funcHandler, "ThreadSamplerSample"));
133     auto collectFunc = reinterpret_cast<ThreadSamplerCollectFunc>(FunctionOpen(funcHandler, "ThreadSamplerCollect"));
134     auto deinitFunc = reinterpret_cast<ThreadSamplerDeinitFunc>(FunctionOpen(funcHandler, "ThreadSamplerDeinit"));
135 
136     initFunc(SAMPLE_CNT);
137     auto sampleHandler = [sampleFunc]() {
138         sampleFunc();
139     };
140 
141     char* stack = new char[STACK_LENGTH];
142     auto collectHandler = [&stack, collectFunc]() {
143         int treeFormat = 0;
144         collectFunc(stack, STACK_LENGTH, treeFormat);
145     };
146 
147     for (int i = 0; i < SAMPLE_CNT; i++) {
148         uint64_t delay = INTERVAL * i + INTERVAL;
149         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, delay);
150     }
151     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
152 
153     WaitSomeTime();
154 
155     ASSERT_NE(stack, "");
156     printf("stack:\n%s", stack);
157     delete[] stack;
158     deinitFunc();
159     dlclose(funcHandler);
160 }
161 
162 /**
163  * @tc.name: ThreadSamplerTest_002
164  * @tc.desc: sample thread SAMPLE_CNT times and check the stacktrace in tree format
165  * @tc.type: FUNC
166  * @tc.require
167  */
HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_002, TestSize.Level3)168 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_002, TestSize.Level3)
169 {
170     printf("ThreadSamplerTest_002\n");
171     printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
172     void* funcHandler = dlopen(LIB_THREAD_SAMPLER_PATH, RTLD_LAZY);
173     if (funcHandler == nullptr) {
174         printf("open library failed.");
175     }
176     dlerror();
177     auto initFunc = reinterpret_cast<ThreadSamplerInitFunc>(FunctionOpen(funcHandler, "ThreadSamplerInit"));
178     auto sampleFunc = reinterpret_cast<ThreadSamplerSampleFunc>(FunctionOpen(funcHandler, "ThreadSamplerSample"));
179     auto collectFunc = reinterpret_cast<ThreadSamplerCollectFunc>(FunctionOpen(funcHandler, "ThreadSamplerCollect"));
180     auto deinitFunc = reinterpret_cast<ThreadSamplerDeinitFunc>(FunctionOpen(funcHandler, "ThreadSamplerDeinit"));
181 
182     initFunc(SAMPLE_CNT);
183     auto sampleHandler = [sampleFunc]() {
184         sampleFunc();
185     };
186 
187     char* stack = new char[STACK_LENGTH];
188     auto collectHandler = [&stack, collectFunc]() {
189         int treeFormat = 1;
190         collectFunc(stack, STACK_LENGTH, treeFormat);
191     };
192 
193     for (int i = 0; i < SAMPLE_CNT; i++) {
194         uint64_t delay = INTERVAL * i + INTERVAL;
195         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, delay);
196     }
197     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
198 
199     WaitSomeTime();
200     ASSERT_NE(stack, "");
201     printf("stack:\n%s", stack);
202     delete[] stack;
203     deinitFunc();
204     dlclose(funcHandler);
205 }
206 
207 /**
208  * @tc.name: ThreadSamplerTest_003
209  * @tc.desc: sample thread SAMPLE_CNT times and deinit sampler send SAMPLE_CNT sample requestion and restart sampler.
210  * @tc.type: FUNC
211  * @tc.require
212  */
HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_003, TestSize.Level3)213 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_003, TestSize.Level3)
214 {
215     printf("ThreadSamplerTest_003\n");
216     printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
217     void* funcHandler = dlopen(LIB_THREAD_SAMPLER_PATH, RTLD_LAZY);
218     if (funcHandler == nullptr) {
219         printf("open library failed.");
220     }
221     dlerror();
222     auto initFunc = reinterpret_cast<ThreadSamplerInitFunc>(FunctionOpen(funcHandler, "ThreadSamplerInit"));
223     auto sampleFunc = reinterpret_cast<ThreadSamplerSampleFunc>(FunctionOpen(funcHandler, "ThreadSamplerSample"));
224     auto collectFunc = reinterpret_cast<ThreadSamplerCollectFunc>(FunctionOpen(funcHandler, "ThreadSamplerCollect"));
225     auto deinitFunc = reinterpret_cast<ThreadSamplerDeinitFunc>(FunctionOpen(funcHandler, "ThreadSamplerDeinit"));
226 
227     initFunc(SAMPLE_CNT);
228     auto sampleHandler = [sampleFunc]() {
229         sampleFunc();
230     };
231 
232     char* stack = new char[STACK_LENGTH];
233     auto collectHandler = [&stack, collectFunc]() {
234         int treeFormat = 1;
235         collectFunc(stack, STACK_LENGTH, treeFormat);
236     };
237 
238     for (int i = 0; i < SAMPLE_CNT; i++) {
239         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, INTERVAL * i + INTERVAL);
240     }
241     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
242 
243     WaitSomeTime();
244     ASSERT_NE(stack, "");
245     printf("stack:\n%s", stack);
246     deinitFunc();
247 
248     for (int i = 0; i < SAMPLE_CNT; i++) {
249         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, INTERVAL * i + INTERVAL);
250     }
251     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
252 
253     WaitSomeTime();
254     ASSERT_NE(stack, "");
255     printf("stack:\n%s", stack);
256 
257     initFunc(SAMPLE_CNT);
258     for (int i = 0; i < SAMPLE_CNT; i++) {
259         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, INTERVAL * i + INTERVAL);
260     }
261     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
262 
263     WaitSomeTime();
264     ASSERT_NE(stack, "");
265     printf("stack:\n%s", stack);
266     delete[] stack;
267     deinitFunc();
268     dlclose(funcHandler);
269 }
270 
271 /**
272  * @tc.name: ThreadSamplerTest_004
273  * @tc.desc: sample thread several times but signal is blocked.
274  * @tc.type: FUNC
275  * @tc.require
276  */
HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_004, TestSize.Level3)277 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_004, TestSize.Level3)
278 {
279     printf("ThreadSamplerTest_004\n");
280     printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
281     void* funcHandler = dlopen(LIB_THREAD_SAMPLER_PATH, RTLD_LAZY);
282     if (funcHandler == nullptr) {
283         printf("open library failed.");
284     }
285     dlerror();
286     auto initFunc = reinterpret_cast<ThreadSamplerInitFunc>(FunctionOpen(funcHandler, "ThreadSamplerInit"));
287     auto sampleFunc = reinterpret_cast<ThreadSamplerSampleFunc>(FunctionOpen(funcHandler, "ThreadSamplerSample"));
288     auto collectFunc = reinterpret_cast<ThreadSamplerCollectFunc>(FunctionOpen(funcHandler, "ThreadSamplerCollect"));
289     auto deinitFunc = reinterpret_cast<ThreadSamplerDeinitFunc>(FunctionOpen(funcHandler, "ThreadSamplerDeinit"));
290     initFunc(SAMPLE_CNT);
291     auto sampleHandler = [sampleFunc]() {
292         sampleFunc();
293     };
294 
295     char* stack = new char[STACK_LENGTH];
296     auto collectHandler = [&stack, collectFunc]() {
297         int treeFormat = 1;
298         collectFunc(stack, STACK_LENGTH, treeFormat);
299     };
300 
301     sigset_t sigset;
302     sigemptyset(&sigset);
303     sigaddset(&sigset, MUSL_SIGNAL_SAMPLE_STACK);
304     sigprocmask(SIG_BLOCK, &sigset, nullptr);
305 
306     for (int i = 0; i < SAMPLE_CNT; i++) {
307         uint64_t delay = INTERVAL * i + INTERVAL;
308         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, delay);
309     }
310     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
311 
312     WaitSomeTime();
313     printf("stack:\n%s", stack);
314     ASSERT_NE(stack, "");
315     sigprocmask(SIG_UNBLOCK, &sigset, nullptr);
316     sigdelset(&sigset, MUSL_SIGNAL_SAMPLE_STACK);
317     delete[] stack;
318     deinitFunc();
319     dlclose(funcHandler);
320 }
321 
322 /**
323  * @tc.name: ThreadSamplerTest_005
324  * @tc.desc: Check the size and name of uniqueStackTable mmap.
325  * @tc.type: FUNC
326  * @tc.require
327  */
HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_005, TestSize.Level3)328 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_005, TestSize.Level3)
329 {
330     printf("ThreadSamplerTest_005\n");
331 
332     auto isSubStr = [](const std::string& str, const std::string& sub) {
333         return str.find(sub) != std::string::npos;
334     };
335 
336     uint32_t uniTableSize = 0;
337     std::string uniStackTableMMapName = "";
338 
339     void* funcHandler = dlopen(LIB_THREAD_SAMPLER_PATH, RTLD_LAZY);
340     if (funcHandler == nullptr) {
341         printf("open library failed.");
342     }
343     dlerror();
344     auto initFunc = reinterpret_cast<ThreadSamplerInitFunc>(FunctionOpen(funcHandler, "ThreadSamplerInit"));
345     auto deinitFunc = reinterpret_cast<ThreadSamplerDeinitFunc>(FunctionOpen(funcHandler, "ThreadSamplerDeinit"));
346 
347     initFunc(SAMPLE_CNT);
348     uniTableSize = GetMMapSizeAndName("hicollie_buf", uniStackTableMMapName);
349 
350     uint32_t bufSize = 128 * 1024;
351     ASSERT_EQ(uniTableSize, bufSize);
352     ASSERT_EQ(isSubStr(uniStackTableMMapName, "hicollie_buf"), true);
353     printf("mmap name: %s, size: %u KB\n", uniStackTableMMapName.c_str(), uniTableSize);
354 
355     deinitFunc();
356     dlclose(funcHandler);
357 }
358 } // end of namespace HiviewDFX
359 } // end of namespace OHOS
360