1 /*
2 * Copyright (c) 2025 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 <gtest/gtest.h>
17 #include <string>
18 #include <unistd.h>
19 #include <vector>
20
21 #include "dfx_buffer_writer.h"
22 #include "decorative_dump_info.h"
23 #include "dfx_process.h"
24 #include "dfx_define.h"
25 #include "dfx_test_util.h"
26 #include "dfx_util.h"
27 #include "key_thread_dump_info.h"
28 #include "procinfo.h"
29 #include "multithread_constructor.h"
30
31
32 using namespace OHOS::HiviewDFX;
33 using namespace testing::ext;
34 using namespace std;
35
36 namespace OHOS {
37 namespace HiviewDFX {
38 class ThreadDumpInfoTest : public testing::Test {
39 public:
40 static void SetUpTestCase(void);
TearDownTestCase(void)41 static void TearDownTestCase(void) {}
42 void SetUp();
TearDown()43 void TearDown() {}
44 static int WriteLogFunc(int32_t fd, const char *buf, size_t len);
45 static std::string result;
46 };
47 } // namespace HiviewDFX
48 } // namespace OHOS
49
50 std::string ThreadDumpInfoTest::result = "";
51
SetUpTestCase(void)52 void ThreadDumpInfoTest::SetUpTestCase(void)
53 {
54 result = "";
55 }
56
SetUp(void)57 void ThreadDumpInfoTest::SetUp(void)
58 {
59 DfxBufferWriter::GetInstance().SetWriteFunc(ThreadDumpInfoTest::WriteLogFunc);
60 }
61
WriteLogFunc(int32_t fd,const char * buf,size_t len)62 int ThreadDumpInfoTest::WriteLogFunc(int32_t fd, const char *buf, size_t len)
63 {
64 ThreadDumpInfoTest::result.append(std::string(buf, len));
65 return 0;
66 }
67
68 namespace {
69 pid_t g_childTid = 0;
SleepThread(void * argv)70 void *SleepThread(void *argv)
71 {
72 int threadID = *(int*)argv;
73 printf("create MultiThread %d", threadID);
74
75 const int sleepTime = 10;
76 g_childTid = gettid();
77 sleep(sleepTime);
78 return nullptr;
79 }
80
81 /**
82 * @tc.name: ThreadDumpInfoTest001
83 * @tc.desc: test KeyThreadDumpInfo dumpcatch
84 * @tc.type: FUNC
85 */
86 HWTEST_F(ThreadDumpInfoTest, ThreadDumpInfoTest001, TestSize.Level2)
87 {
88 GTEST_LOG_(INFO) << "ThreadDumpInfoTest001: start.";
89 // dumpcatch -p
90 pid_t pid = fork();
91 if (pid < 0) {
92 GTEST_LOG_(ERROR) << "Failed to fork new test process.";
93 } else if (pid == 0) {
94 sleep(3); // 3 : sleep 3 seconds
95 exit(0);
96 }
97 pid_t tid = pid;
98 pid_t nsPid = pid;
99 ProcessDumpRequest request = {
100 .type = ProcessDumpType::DUMP_TYPE_DUMP_CATCH,
101 .tid = tid,
102 .pid = pid,
103 .nsPid = pid,
104 };
105 DfxProcess process;
106 process.InitProcessInfo(pid, nsPid, getuid(), "");
107 process.SetVmPid(pid);
108 process.InitKeyThread(request);
109 process.GetKeyThread()->SetThreadRegs(DfxRegs::CreateRemoteRegs(pid)); // can not get context, so create regs self
110 Unwinder unwinder(pid, nsPid, request.type == ProcessDumpType::DUMP_TYPE_CPP_CRASH);
111 unwinder.EnableFillFrames(false);
112 KeyThreadDumpInfo dumpInfo;
113 EXPECT_GT(dumpInfo.UnwindStack(process, request, unwinder), 0);
114 dumpInfo.Symbolize(process, unwinder);
115 dumpInfo.Print(process, request, unwinder);
116 std::vector<std::string> keyWords = {
117 "Tid:",
118 to_string(tid),
119 "Name:",
120 "#00",
121 "#01",
122 };
123 for (const std::string& keyWord : keyWords) {
124 EXPECT_TRUE(CheckContent(result, keyWord, true));
125 }
126 process.Detach();
127 GTEST_LOG_(INFO) << "ThreadDumpInfoTest001: end.";
128 }
129
130 /**
131 * @tc.name: ThreadDumpInfoTest002
132 * @tc.desc: test KeyThreadDumpInfo dumpcatch single thread
133 * @tc.type: FUNC
134 */
135 HWTEST_F(ThreadDumpInfoTest, ThreadDumpInfoTest002, TestSize.Level2)
136 {
137 GTEST_LOG_(INFO) << "ThreadDumpInfoTest002: start.";
138 // dumpcatch -p -t
139 pid_t pid = fork();
140 if (pid < 0) {
141 GTEST_LOG_(ERROR) << "Failed to fork new test process.";
142 } else if (pid == 0) {
143 pthread_t childThread;
144 int threadID[1] = {1};
145 pthread_create(&childThread, NULL, SleepThread, &threadID[0]);
146 pthread_detach(childThread);
147 sleep(3); // 3 : sleep 3 seconds
148 exit(0);
149 }
150 sleep(1);
151 pid_t tid = pid;
152 pid_t nsPid = pid;
153 ProcessDumpRequest request = {
154 .type = ProcessDumpType::DUMP_TYPE_DUMP_CATCH,
155 .tid = tid,
156 .pid = pid,
157 .nsPid = pid,
158 };
159 std::vector<int> tids;
160 std::vector<int> nstids;
161 EXPECT_TRUE(GetTidsByPid(pid, tids, nstids));
162 request.siginfo.si_value.sival_int = nstids[nstids.size() - 1];
163 DfxProcess process;
164 process.InitProcessInfo(pid, nsPid, getuid(), "");
165 process.SetVmPid(pid);
166 process.InitKeyThread(request);
167 Unwinder unwinder(pid, nsPid, request.type == ProcessDumpType::DUMP_TYPE_CPP_CRASH);
168 unwinder.EnableFillFrames(false);
169 KeyThreadDumpInfo dumpInfo;
170 EXPECT_GT(dumpInfo.UnwindStack(process, request, unwinder), 0);
171 dumpInfo.Symbolize(process, unwinder);
172 dumpInfo.Print(process, request, unwinder);
173 std::vector<std::string> keyWords = {
174 "Tid:",
175 to_string(g_childTid),
176 "Name:",
177 "#00",
178 "#01",
179 };
180 for (const std::string& keyWord : keyWords) {
181 EXPECT_TRUE(CheckContent(result, keyWord, true));
182 }
183 process.Detach();
184 GTEST_LOG_(INFO) << "ThreadDumpInfoTest002: end.";
185 }
186
187 /**
188 * @tc.name: ThreadDumpInfoTest003
189 * @tc.desc: test KeyThreadDumpInfo cppcrash
190 * @tc.type: FUNC
191 */
192 HWTEST_F(ThreadDumpInfoTest, ThreadDumpInfoTest003, TestSize.Level2)
193 {
194 GTEST_LOG_(INFO) << "ThreadDumpInfoTest003: start.";
195 pid_t pid = fork();
196 if (pid < 0) {
197 GTEST_LOG_(ERROR) << "Failed to fork new test process.";
198 } else if (pid == 0) {
199 sleep(3); // 3 : sleep 3 seconds
200 exit(0);
201 }
202 pid_t tid = pid;
203 pid_t nsPid = pid;
204 ProcessDumpRequest request = {
205 .type = ProcessDumpType::DUMP_TYPE_CPP_CRASH,
206 .tid = tid,
207 .pid = pid,
208 .nsPid = pid,
209 };
210 DfxProcess process;
211 process.InitProcessInfo(pid, nsPid, getuid(), "");
212 process.SetVmPid(pid);
213 process.InitKeyThread(request);
214 process.GetKeyThread()->SetThreadRegs(DfxRegs::CreateRemoteRegs(pid)); // can not get context, so create regs self
215 Unwinder unwinder(pid, nsPid, request.type == ProcessDumpType::DUMP_TYPE_CPP_CRASH);
216 unwinder.EnableFillFrames(false);
217 KeyThreadDumpInfo dumpInfo;
218 EXPECT_GT(dumpInfo.UnwindStack(process, request, unwinder), 0);
219 dumpInfo.Symbolize(process, unwinder);
220 dumpInfo.Print(process, request, unwinder);
221 std::vector<std::string> keyWords = {
222 "Fault thread info:",
223 "Tid:",
224 to_string(tid),
225 "#00",
226 "#01",
227 };
228 for (const std::string& keyWord : keyWords) {
229 EXPECT_TRUE(CheckContent(result, keyWord, true));
230 }
231 GTEST_LOG_(INFO) << "ThreadDumpInfoTest003: end.";
232 }
233
234 /**
235 * @tc.name: ThreadDumpInfoTest004
236 * @tc.desc: test other thread dump info in cppcrash type
237 * @tc.type: FUNC
238 */
239 HWTEST_F(ThreadDumpInfoTest, ThreadDumpInfoTest004, TestSize.Level2)
240 {
241 GTEST_LOG_(INFO) << "ThreadDumpInfoTest004: start.";
242 pid_t testProcess = CreateMultiThreadProcess(10); // 10 : create a process with ten threads
243 sleep(1);
244 pid_t pid = testProcess;
245 pid_t tid = testProcess;
246 pid_t nsPid = testProcess;
247 ProcessDumpRequest request = {
248 .type = ProcessDumpType::DUMP_TYPE_CPP_CRASH,
249 .tid = tid,
250 .pid = pid,
251 .nsPid = pid,
252 };
253 DfxProcess process;
254 process.InitProcessInfo(pid, nsPid, getuid(), "");
255 process.SetVmPid(pid);
256 process.InitOtherThreads(0);
257 Unwinder unwinder(pid, nsPid, request.type == ProcessDumpType::DUMP_TYPE_CPP_CRASH);
258 unwinder.EnableFillFrames(false);
259 OtherThreadDumpInfo dumpInfo;
260 EXPECT_GT(dumpInfo.UnwindStack(process, request, unwinder), 0);
261 dumpInfo.Symbolize(process, unwinder);
262 dumpInfo.Print(process, request, unwinder);
263 std::vector<std::string> keyWords = {
264 "Other thread info:",
265 "Tid:",
266 "#00",
267 "#01",
268 };
269 for (const std::string& keyWord : keyWords) {
270 EXPECT_TRUE(CheckContent(result, keyWord, true));
271 }
272 process.Detach();
273 kill(pid, SIGKILL);
274 GTEST_LOG_(INFO) << "ThreadDumpInfoTest004: end.";
275 }
276
277 /**
278 * @tc.name: ThreadDumpInfoTest005
279 * @tc.desc: test other thread dump info in dumpcatch type
280 * @tc.type: FUNC
281 */
282 HWTEST_F(ThreadDumpInfoTest, ThreadDumpInfoTest005, TestSize.Level2)
283 {
284 GTEST_LOG_(INFO) << "ThreadDumpInfoTest005: start.";
285 pid_t testProcess = CreateMultiThreadProcess(10); // 10 : create a process with ten threads
286 sleep(1);
287 pid_t pid = testProcess;
288 pid_t tid = testProcess;
289 pid_t nsPid = testProcess;
290 ProcessDumpRequest request = {
291 .type = ProcessDumpType::DUMP_TYPE_DUMP_CATCH,
292 .tid = tid,
293 .pid = pid,
294 .nsPid = pid,
295 };
296 DfxProcess process;
297 process.InitProcessInfo(pid, nsPid, getuid(), "");
298 process.SetVmPid(pid);
299 process.InitOtherThreads(0);
300 Unwinder unwinder(pid, nsPid, request.type == ProcessDumpType::DUMP_TYPE_CPP_CRASH);
301 unwinder.EnableFillFrames(false);
302 OtherThreadDumpInfo dumpInfo;
303 EXPECT_GT(dumpInfo.UnwindStack(process, request, unwinder), 0);
304 dumpInfo.Symbolize(process, unwinder);
305 dumpInfo.Print(process, request, unwinder);
306 std::vector<std::string> keyWords = {
307 "Tid:",
308 "#00",
309 "#01",
310 };
311 for (const std::string& keyWord : keyWords) {
312 EXPECT_TRUE(CheckContent(result, keyWord, true));
313 }
314 process.Detach();
315 kill(pid, SIGKILL);
316 GTEST_LOG_(INFO) << "ThreadDumpInfoTest005: end.";
317 }
318
319 /**
320 * @tc.name: ThreadDumpInfoTest006
321 * @tc.desc: test all thread dump info
322 * @tc.type: FUNC
323 */
324 HWTEST_F(ThreadDumpInfoTest, ThreadDumpInfoTest006, TestSize.Level2)
325 {
326 GTEST_LOG_(INFO) << "ThreadDumpInfoTest006: start.";
327 int threadCount = 10;
328 pid_t pid = CreateMultiThreadProcess(threadCount);
329 sleep(1);
330 pid_t nsPid = pid;
331 ProcessDumpRequest request = {
332 .type = ProcessDumpType::DUMP_TYPE_DUMP_CATCH,
333 .tid = pid,
334 .pid = pid,
335 .nsPid = pid,
336 };
337 DfxProcess process;
338 process.InitProcessInfo(pid, nsPid, getuid(), "");
339 process.InitKeyThread(request);
340 process.SetVmPid(pid);
341 process.GetKeyThread()->SetThreadRegs(DfxRegs::CreateRemoteRegs(pid)); // can not get context, so create regs self
342 process.InitOtherThreads(pid);
343 Unwinder unwinder(pid, nsPid, request.type == ProcessDumpType::DUMP_TYPE_CPP_CRASH);
344 unwinder.EnableFillFrames(false);
345 std::shared_ptr<DumpInfo> keyThreadDumpInfo = std::make_shared<KeyThreadDumpInfo>();
346 OtherThreadDumpInfo dumpInfo;
347 dumpInfo.SetDumpInfo(keyThreadDumpInfo);
348 EXPECT_GT(dumpInfo.UnwindStack(process, request, unwinder), threadCount);
349 dumpInfo.Symbolize(process, unwinder);
350 dumpInfo.Print(process, request, unwinder);
351 std::vector<std::string> keyWords = {
352 "Tid:",
353 to_string(pid),
354 "#00",
355 "#01",
356 };
357 for (const std::string& keyWord : keyWords) {
358 EXPECT_TRUE(CheckContent(result, keyWord, true));
359 }
360 process.Detach();
361 kill(pid, SIGKILL);
362 GTEST_LOG_(INFO) << "ThreadDumpInfoTest006: end.";
363 }
364 }
365