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