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 
30 constexpr int SAMPLE_CNT = 10;
31 constexpr int INTERVAL = 100;
32 constexpr size_t STACK_LENGTH = 32 * 1024;
33 constexpr int MILLSEC_TO_MICROSEC = 1000;
34 
35 static std::mutex threadSamplerSignalMutex_;
36 SigActionType ThreadSamplerTest::threadSamplerSigHandler_ = nullptr;
37 
SetUpTestCase(void)38 void ThreadSamplerTest::SetUpTestCase(void)
39 {
40     printf("SetUpTestCase.\n");
41 }
42 
TearDownTestCase(void)43 void ThreadSamplerTest::TearDownTestCase(void)
44 {
45     printf("TearDownTestCase.\n");
46     Watchdog::GetInstance().StopWatchdog();
47 }
48 
SetUp(void)49 void ThreadSamplerTest::SetUp(void)
50 {
51     printf("SetUp.\n");
52 }
53 
TearDown(void)54 void ThreadSamplerTest::TearDown(void)
55 {
56     printf("TearDown.\n");
57 }
58 
WaitSomeTime()59 void WaitSomeTime()
60 {
61     int waitDelay = 3;
62     int32_t left = (INTERVAL * SAMPLE_CNT + INTERVAL) / MILLSEC_TO_MICROSEC + waitDelay;
63     int32_t end = time(nullptr) + left;
64     while (left > 0) {
65         left = end - time(nullptr);
66     }
67     sleep((INTERVAL * SAMPLE_CNT + INTERVAL) / MILLSEC_TO_MICROSEC + waitDelay);
68 }
69 
GetMMapSizeAndName(const std::string & checkName,std::string & mmapName)70 uint32_t GetMMapSizeAndName(const std::string& checkName, std::string& mmapName)
71 {
72     uint64_t size = 0;
73     mmapName = "";
74     std::ifstream mapsFile("/proc/self/maps");
75     std::string line;
76     int base = 16;
77     while (getline(mapsFile, line)) {
78         std::istringstream iss(line);
79         std::string addrs;
80         std::string permissions;
81         std::string offset;
82         std::string devices;
83         std::string inode;
84         std::string pathname;
85         iss >> addrs >> permissions >> offset >> devices >> inode >> pathname;
86         if (pathname.find(checkName) != std::string::npos) {
87             std::string start = addrs.substr(0, addrs.find('-'));
88             std::string end = addrs.substr(addrs.find('-') + 1);
89             size = std::stoul(end, nullptr, base) - std::stoul(start, nullptr, base);
90             mmapName = pathname;
91         }
92     }
93     return static_cast<uint32_t>(size);
94 }
95 
FunctionOpen(void * funcHandler,const char * funcName)96 void* FunctionOpen(void* funcHandler, const char* funcName)
97 {
98     dlerror();
99     char* err = nullptr;
100     void* func = dlsym(funcHandler, funcName);
101     err = dlerror();
102     if (err != nullptr) {
103         return nullptr;
104     }
105     return func;
106 }
107 
ThreadSamplerSigHandler(int sig,siginfo_t * si,void * context)108 void ThreadSamplerTest::ThreadSamplerSigHandler(int sig, siginfo_t* si, void* context)
109 {
110     std::lock_guard<std::mutex> lock(threadSamplerSignalMutex_);
111     if (ThreadSamplerTest::threadSamplerSigHandler_ == nullptr) {
112         return;
113     }
114     ThreadSamplerTest::threadSamplerSigHandler_(sig, si, context);
115 }
116 
InstallThreadSamplerSignal()117 bool ThreadSamplerTest::InstallThreadSamplerSignal()
118 {
119     struct sigaction action {};
120     sigfillset(&action.sa_mask);
121     action.sa_sigaction = ThreadSamplerTest::ThreadSamplerSigHandler;
122     action.sa_flags = SA_RESTART | SA_SIGINFO;
123     if (sigaction(MUSL_SIGNAL_SAMPLE_STACK, &action, nullptr) != 0) {
124         return false;
125     }
126     return true;
127 }
128 
UninstallThreadSamplerSignal()129 void ThreadSamplerTest::UninstallThreadSamplerSignal()
130 {
131     std::lock_guard<std::mutex> lock(threadSamplerSignalMutex_);
132     threadSamplerSigHandler_ = nullptr;
133 }
134 
InitThreadSamplerFuncs()135 bool ThreadSamplerTest::InitThreadSamplerFuncs()
136 {
137     threadSamplerFuncHandler_ = dlopen(LIB_THREAD_SAMPLER_PATH, RTLD_LAZY);
138     if (threadSamplerFuncHandler_ == nullptr) {
139         return false;
140     }
141 
142     threadSamplerInitFunc_ =
143         reinterpret_cast<ThreadSamplerInitFunc>(FunctionOpen(threadSamplerFuncHandler_, "ThreadSamplerInit"));
144     threadSamplerSampleFunc_ =
145         reinterpret_cast<ThreadSamplerSampleFunc>(FunctionOpen(threadSamplerFuncHandler_, "ThreadSamplerSample"));
146     threadSamplerCollectFunc_ =
147         reinterpret_cast<ThreadSamplerCollectFunc>(FunctionOpen(threadSamplerFuncHandler_, "ThreadSamplerCollect"));
148     threadSamplerDeinitFunc_ =
149         reinterpret_cast<ThreadSamplerDeinitFunc>(FunctionOpen(threadSamplerFuncHandler_, "ThreadSamplerDeinit"));
150     threadSamplerSigHandler_ =
151         reinterpret_cast<SigActionType>(FunctionOpen(threadSamplerFuncHandler_, "ThreadSamplerSigHandler"));
152     if (threadSamplerInitFunc_ == nullptr || threadSamplerSampleFunc_ == nullptr ||
153         threadSamplerCollectFunc_ == nullptr || threadSamplerDeinitFunc_ == nullptr ||
154         threadSamplerSigHandler_ == nullptr) {
155         threadSamplerInitFunc_ = nullptr;
156         threadSamplerSampleFunc_ = nullptr;
157         threadSamplerCollectFunc_ = nullptr;
158         threadSamplerDeinitFunc_ = nullptr;
159         threadSamplerSigHandler_ = nullptr;
160         dlclose(threadSamplerFuncHandler_);
161         threadSamplerFuncHandler_ = nullptr;
162         return false;
163     }
164     return true;
165 }
166 
InitThreadSampler()167 bool ThreadSamplerTest::InitThreadSampler()
168 {
169     if (!InitThreadSamplerFuncs()) {
170         return false;
171     }
172 
173     if (!InstallThreadSamplerSignal()) {
174         return false;
175     }
176     return true;
177 }
178 
179 /**
180  * @tc.name: ThreadSamplerTest_001
181  * @tc.desc: sample thread SAMPLE_CNT times and check the stacktrace
182  * @tc.type: FUNC
183  * @tc.require
184  */
185 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_001, TestSize.Level3)
186 {
187     printf("ThreadSamplerTest_001\n");
188     printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
189 
190     bool flag = InitThreadSampler();
191     ASSERT_TRUE(flag);
192 
193     threadSamplerInitFunc_(SAMPLE_CNT);
__anon5be550370102() 194     auto sampleHandler = [this]() {
195         threadSamplerSampleFunc_();
196     };
197 
198     char* stk = new char[STACK_LENGTH];
__anon5be550370202() 199     auto collectHandler = [this, &stk]() {
200         int treeFormat = 0;
201         threadSamplerCollectFunc_(stk, STACK_LENGTH, treeFormat);
202     };
203 
204     for (int i = 0; i < SAMPLE_CNT; i++) {
205         uint64_t delay = INTERVAL * i + INTERVAL;
206         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, delay);
207     }
208     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
209 
210     WaitSomeTime();
211 
212     std::string stack = stk;
213     ASSERT_NE(stack, "");
214     printf("stack:\n%s", stack.c_str());
215     delete[] stk;
216     UninstallThreadSamplerSignal();
217     threadSamplerDeinitFunc_();
218     dlclose(threadSamplerFuncHandler_);
219 }
220 
221 /**
222  * @tc.name: ThreadSamplerTest_002
223  * @tc.desc: sample thread SAMPLE_CNT times and check the stacktrace in tree format
224  * @tc.type: FUNC
225  * @tc.require
226  */
227 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_002, TestSize.Level3)
228 {
229     printf("ThreadSamplerTest_002\n");
230     printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
231 
232     bool flag = InitThreadSampler();
233     ASSERT_TRUE(flag);
234 
235     threadSamplerInitFunc_(SAMPLE_CNT);
__anon5be550370302() 236     auto sampleHandler = [this]() {
237         threadSamplerSampleFunc_();
238     };
239 
240     char* stk = new char[STACK_LENGTH];
__anon5be550370402() 241     auto collectHandler = [this, &stk]() {
242         int treeFormat = 1;
243         threadSamplerCollectFunc_(stk, STACK_LENGTH, treeFormat);
244     };
245 
246     for (int i = 0; i < SAMPLE_CNT; i++) {
247         uint64_t delay = INTERVAL * i + INTERVAL;
248         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, delay);
249     }
250     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
251 
252     WaitSomeTime();
253 
254     std::string stack = stk;
255     ASSERT_NE(stack, "");
256     printf("stack:\n%s", stack.c_str());
257     delete[] stk;
258     UninstallThreadSamplerSignal();
259     threadSamplerDeinitFunc_();
260     dlclose(threadSamplerFuncHandler_);
261 }
262 
263 /**
264  * @tc.name: ThreadSamplerTest_003
265  * @tc.desc: sample thread SAMPLE_CNT times and deinit sampler send SAMPLE_CNT sample requestion and restart sampler.
266  * @tc.type: FUNC
267  * @tc.require
268  */
269 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_003, TestSize.Level3)
270 {
271     printf("ThreadSamplerTest_003\n");
272     printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
273 
274     bool flag = InitThreadSampler();
275     ASSERT_TRUE(flag);
276 
277     threadSamplerInitFunc_(SAMPLE_CNT);
__anon5be550370502() 278     auto sampleHandler = [this]() {
279         threadSamplerSampleFunc_();
280     };
281 
282     char* stk = new char[STACK_LENGTH];
__anon5be550370602() 283     auto collectHandler = [this, &stk]() {
284         int treeFormat = 1;
285         threadSamplerCollectFunc_(stk, STACK_LENGTH, treeFormat);
286     };
287 
288     for (int i = 0; i < SAMPLE_CNT; i++) {
289         uint64_t delay = INTERVAL * i + INTERVAL;
290         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, delay);
291     }
292     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
293 
294     WaitSomeTime();
295 
296     std::string stack = stk;
297     ASSERT_NE(stack, "");
298     printf("stack:\n%s", stack.c_str());
299     threadSamplerDeinitFunc_();
300 
301     for (int i = 0; i < SAMPLE_CNT; i++) {
302         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, INTERVAL * i + INTERVAL);
303     }
304     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
305 
306     WaitSomeTime();
307     stack = stk;
308     ASSERT_NE(stack, "");
309     printf("stack:\n%s", stack.c_str());
310 
311     threadSamplerInitFunc_(SAMPLE_CNT);
312 
313     flag = InstallThreadSamplerSignal();
314     ASSERT_TRUE(flag);
315 
316     for (int i = 0; i < SAMPLE_CNT; i++) {
317         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, INTERVAL * i + INTERVAL);
318     }
319     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
320 
321     WaitSomeTime();
322     stack = stk;
323     ASSERT_NE(stack, "");
324     printf("stack:\n%s", stack.c_str());
325     delete[] stk;
326     UninstallThreadSamplerSignal();
327     threadSamplerDeinitFunc_();
328     dlclose(threadSamplerFuncHandler_);
329 }
330 
331 /**
332  * @tc.name: ThreadSamplerTest_004
333  * @tc.desc: sample thread several times but signal is blocked.
334  * @tc.type: FUNC
335  * @tc.require
336  */
337 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_004, TestSize.Level3)
338 {
339     printf("ThreadSamplerTest_004\n");
340     printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
341 
342     bool flag = InitThreadSampler();
343     ASSERT_TRUE(flag);
344 
345     threadSamplerInitFunc_(SAMPLE_CNT);
__anon5be550370702() 346     auto sampleHandler = [this]() {
347         threadSamplerSampleFunc_();
348     };
349 
350     char* stk = new char[STACK_LENGTH];
__anon5be550370802() 351     auto collectHandler = [this, &stk]() {
352         int treeFormat = 1;
353         threadSamplerCollectFunc_(stk, STACK_LENGTH, treeFormat);
354     };
355 
356     sigset_t sigset;
357     sigemptyset(&sigset);
358     sigaddset(&sigset, MUSL_SIGNAL_SAMPLE_STACK);
359     sigprocmask(SIG_BLOCK, &sigset, nullptr);
360 
361     for (int i = 0; i < SAMPLE_CNT; i++) {
362         uint64_t delay = INTERVAL * i + INTERVAL;
363         Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, delay);
364     }
365     Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
366 
367     WaitSomeTime();
368     std::string stack = stk;
369     printf("stack:\n%s", stack.c_str());
370     ASSERT_NE(stack, "");
371     sigprocmask(SIG_UNBLOCK, &sigset, nullptr);
372     sigdelset(&sigset, MUSL_SIGNAL_SAMPLE_STACK);
373     delete[] stk;
374     UninstallThreadSamplerSignal();
375     threadSamplerDeinitFunc_();
376     dlclose(threadSamplerFuncHandler_);
377 }
378 
379 /**
380  * @tc.name: ThreadSamplerTest_005
381  * @tc.desc: Check the size and name of uniqueStackTable mmap.
382  * @tc.type: FUNC
383  * @tc.require
384  */
385 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_005, TestSize.Level3)
386 {
387     printf("ThreadSamplerTest_005\n");
388 
__anon5be550370902(const std::string& str, const std::string& sub) 389     auto isSubStr = [](const std::string& str, const std::string& sub) {
390         return str.find(sub) != std::string::npos;
391     };
392 
393     uint32_t uniTableSize = 0;
394     std::string uniStackTableMMapName = "";
395 
396     bool flag = InitThreadSamplerFuncs();
397     ASSERT_TRUE(flag);
398 
399     threadSamplerInitFunc_(SAMPLE_CNT);
400     uniTableSize = GetMMapSizeAndName("hicollie_buf", uniStackTableMMapName);
401 
402     uint32_t bufSize = 128 * 1024;
403     ASSERT_EQ(uniTableSize, bufSize);
404     ASSERT_EQ(isSubStr(uniStackTableMMapName, "hicollie_buf"), true);
405     printf("mmap name: %s, size: %u KB\n", uniStackTableMMapName.c_str(), uniTableSize);
406 
407     threadSamplerDeinitFunc_();
408     dlclose(threadSamplerFuncHandler_);
409 }
410 } // end of namespace HiviewDFX
411 } // end of namespace OHOS
412