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.Level0)
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);
__anon8a91e8a10102() 194 auto sampleHandler = [this]() {
195 threadSamplerSampleFunc_();
196 };
197
198 char* stk = new char[STACK_LENGTH];
199 char* heaviestStk = new char[STACK_LENGTH];
__anon8a91e8a10202() 200 auto collectHandler = [this, &stk, &heaviestStk]() {
201 int treeFormat = 0;
202 threadSamplerCollectFunc_(stk, heaviestStk, STACK_LENGTH, STACK_LENGTH, treeFormat);
203 };
204
205 for (int i = 0; i < SAMPLE_CNT; i++) {
206 uint64_t delay = INTERVAL * i + INTERVAL;
207 Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, delay);
208 }
209 Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
210
211 WaitSomeTime();
212
213 std::string stack = stk;
214 std::string heaviestStack = heaviestStk;
215 ASSERT_NE(stack, "");
216 printf("stack:\n%s\nheaviestStack:\n%s", stack.c_str(), heaviestStack.c_str());
217 delete[] stk;
218 delete[] heaviestStk;
219 UninstallThreadSamplerSignal();
220 threadSamplerDeinitFunc_();
221 dlclose(threadSamplerFuncHandler_);
222 }
223
224 /**
225 * @tc.name: ThreadSamplerTest_002
226 * @tc.desc: sample thread SAMPLE_CNT times and check the stacktrace in tree format
227 * @tc.type: FUNC
228 * @tc.require
229 */
230 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_002, TestSize.Level3)
231 {
232 printf("ThreadSamplerTest_002\n");
233 printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
234
235 bool flag = InitThreadSampler();
236 ASSERT_TRUE(flag);
237
238 threadSamplerInitFunc_(SAMPLE_CNT);
__anon8a91e8a10302() 239 auto sampleHandler = [this]() {
240 threadSamplerSampleFunc_();
241 };
242
243 char* stk = new char[STACK_LENGTH];
244 char* heaviestStk = new char[STACK_LENGTH];
__anon8a91e8a10402() 245 auto collectHandler = [this, &stk, &heaviestStk]() {
246 int treeFormat = 1;
247 threadSamplerCollectFunc_(stk, heaviestStk, STACK_LENGTH, STACK_LENGTH, treeFormat);
248 };
249
250 for (int i = 0; i < SAMPLE_CNT; i++) {
251 uint64_t delay = INTERVAL * i + INTERVAL;
252 Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, delay);
253 }
254 Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
255
256 WaitSomeTime();
257
258 std::string stack = stk;
259 std::string heaviestStack = heaviestStk;
260 ASSERT_NE(stack, "");
261 printf("stack:\n%s\nheaviestStack:\n%s", stack.c_str(), heaviestStack.c_str());
262 delete[] stk;
263 delete[] heaviestStk;
264 UninstallThreadSamplerSignal();
265 threadSamplerDeinitFunc_();
266 dlclose(threadSamplerFuncHandler_);
267 }
268
269 /**
270 * @tc.name: ThreadSamplerTest_003
271 * @tc.desc: sample thread SAMPLE_CNT times and deinit sampler send SAMPLE_CNT sample requestion and restart sampler.
272 * @tc.type: FUNC
273 * @tc.require
274 */
275 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_003, TestSize.Level3)
276 {
277 printf("ThreadSamplerTest_003\n");
278 printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
279
280 InitThreadSampler();
281
282 threadSamplerInitFunc_(SAMPLE_CNT);
__anon8a91e8a10502() 283 auto sampleHandler = [this]() {
284 threadSamplerSampleFunc_();
285 };
286
287 char* stk = new char[STACK_LENGTH];
288 char* heaviestStk = new char[STACK_LENGTH];
__anon8a91e8a10602() 289 auto collectHandler = [this, &stk, &heaviestStk]() {
290 int treeFormat = 1;
291 threadSamplerCollectFunc_(stk, heaviestStk, STACK_LENGTH, STACK_LENGTH, treeFormat);
292 };
293
294 for (int i = 0; i < SAMPLE_CNT; i++) {
295 Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, INTERVAL * i + INTERVAL);
296 }
297 Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
298
299 WaitSomeTime();
300
301 std::string stack = stk;
302 std::string heaviestStack = heaviestStk;
303 ASSERT_NE(stack, "");
304 printf("stack:\n%s\nheaviestStack:\n%s", stack.c_str(), heaviestStack.c_str());
305 threadSamplerDeinitFunc_();
306
307 for (int i = 0; i < SAMPLE_CNT; i++) {
308 Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, INTERVAL * i + INTERVAL);
309 }
310 Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
311
312 WaitSomeTime();
313 stack = stk;
314 heaviestStack = heaviestStk;
315 ASSERT_NE(stack, "");
316 printf("stack:\n%s\nheaviestStack:\n%s", stack.c_str(), heaviestStack.c_str());
317
318 threadSamplerInitFunc_(SAMPLE_CNT);
319
320 for (int i = 0; i < SAMPLE_CNT; i++) {
321 Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, INTERVAL * i + INTERVAL);
322 }
323 Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
324
325 WaitSomeTime();
326 stack = stk;
327 heaviestStack = heaviestStk;
328 ASSERT_NE(stack, "");
329 printf("stack:\n%s\nheaviestStack:\n%s", stack.c_str(), heaviestStack.c_str());
330 delete[] stk;
331 delete[] heaviestStk;
332 UninstallThreadSamplerSignal();
333 threadSamplerDeinitFunc_();
334 dlclose(threadSamplerFuncHandler_);
335 }
336
337 /**
338 * @tc.name: ThreadSamplerTest_004
339 * @tc.desc: sample thread several times but signal is blocked.
340 * @tc.type: FUNC
341 * @tc.require
342 */
343 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_004, TestSize.Level3)
344 {
345 printf("ThreadSamplerTest_004\n");
346 printf("Total:%dMS Sample:%dMS \n", INTERVAL * SAMPLE_CNT + INTERVAL, INTERVAL);
347
348 bool flag = InitThreadSampler();
349 ASSERT_TRUE(flag);
350
351 threadSamplerInitFunc_(SAMPLE_CNT);
__anon8a91e8a10702() 352 auto sampleHandler = [this]() {
353 threadSamplerSampleFunc_();
354 };
355
356 char* stk = new char[STACK_LENGTH];
357 char* heaviestStk = new char[STACK_LENGTH];
__anon8a91e8a10802() 358 auto collectHandler = [this, &stk, &heaviestStk]() {
359 int treeFormat = 1;
360 threadSamplerCollectFunc_(stk, heaviestStk, STACK_LENGTH, STACK_LENGTH, treeFormat);
361 };
362
363 sigset_t sigset;
364 sigemptyset(&sigset);
365 sigaddset(&sigset, MUSL_SIGNAL_SAMPLE_STACK);
366 sigprocmask(SIG_BLOCK, &sigset, nullptr);
367
368 for (int i = 0; i < SAMPLE_CNT; i++) {
369 uint64_t delay = INTERVAL * i + INTERVAL;
370 Watchdog::GetInstance().RunOneShotTask("ThreadSamplerTest", sampleHandler, delay);
371 }
372 Watchdog::GetInstance().RunOneShotTask("CollectStackTest", collectHandler, INTERVAL * SAMPLE_CNT + INTERVAL);
373
374 WaitSomeTime();
375
376 std::string stack = stk;
377 std::string heaviestStack = heaviestStk;
378 ASSERT_NE(stack, "");
379 printf("stack:\n%s\nheaviestStack:\n%s", stack.c_str(), heaviestStack.c_str());
380 sigprocmask(SIG_UNBLOCK, &sigset, nullptr);
381 sigdelset(&sigset, MUSL_SIGNAL_SAMPLE_STACK);
382 delete[] stk;
383 delete[] heaviestStk;
384 UninstallThreadSamplerSignal();
385 threadSamplerDeinitFunc_();
386 dlclose(threadSamplerFuncHandler_);
387 }
388
389 /**
390 * @tc.name: ThreadSamplerTest_005
391 * @tc.desc: Check the size and name of uniqueStackTable mmap.
392 * @tc.type: FUNC
393 * @tc.require
394 */
395 HWTEST_F(ThreadSamplerTest, ThreadSamplerTest_005, TestSize.Level3)
396 {
397 printf("ThreadSamplerTest_005\n");
398
__anon8a91e8a10902(const std::string& str, const std::string& sub) 399 auto isSubStr = [](const std::string& str, const std::string& sub) {
400 return str.find(sub) != std::string::npos;
401 };
402
403 uint32_t uniTableSize = 0;
404 std::string uniStackTableMMapName = "";
405
406 bool flag = InitThreadSamplerFuncs();
407 ASSERT_TRUE(flag);
408
409 threadSamplerInitFunc_(SAMPLE_CNT);
410 uniTableSize = GetMMapSizeAndName("hicollie_buf", uniStackTableMMapName);
411
412 uint32_t bufSize = 128 * 1024;
413 ASSERT_EQ(uniTableSize, bufSize);
414 ASSERT_EQ(isSubStr(uniStackTableMMapName, "hicollie_buf"), true);
415 printf("mmap name: %s, size: %u KB\n", uniStackTableMMapName.c_str(), uniTableSize);
416
417 threadSamplerDeinitFunc_();
418 dlclose(threadSamplerFuncHandler_);
419 }
420 } // end of namespace HiviewDFX
421 } // end of namespace OHOS
422