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