1 /*
2 * Copyright (c) 2022 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
18 #include <cstdio>
19 #include <cstdlib>
20 #include <memory>
21 #include <mutex>
22 #include <thread>
23
24 #include <dlfcn.h>
25 #include <fcntl.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28
29 #include "file_util.h"
30 #include <libunwind_i-ohos.h>
31 #include <libunwind.h>
32 #include <securec.h>
33
34 #include "dfx_frame_format.h"
35 #include "backtrace_local.h"
36 #include "backtrace_local_context.h"
37 #include "backtrace_local_thread.h"
38 #include "dfx_symbols.h"
39 #include "elapsed_time.h"
40 #include "dfx_test_util.h"
41
42 using namespace testing;
43 using namespace testing::ext;
44
45 namespace OHOS {
46 namespace HiviewDFX {
47 #undef LOG_DOMAIN
48 #undef LOG_TAG
49 #define LOG_TAG "BacktraceLocalTest"
50 #define LOG_DOMAIN 0xD002D11
51 namespace {
CheckResourceUsage(uint32_t fdCount,uint32_t mapsCount,uint64_t memCount)52 static void CheckResourceUsage(uint32_t fdCount, uint32_t mapsCount, uint64_t memCount)
53 {
54 // check memory/fd/maps
55 auto curFdCount = GetSelfFdCount();
56 constexpr uint32_t extraVal = 10;
57 GTEST_LOG_(INFO) << "AfterTest Fd New:" << std::to_string(curFdCount);
58 GTEST_LOG_(INFO) << "Fd Old:" << std::to_string(fdCount) << "\n";
59
60 auto curMapsCount = GetSelfMapsCount();
61 GTEST_LOG_(INFO) << "AfterTest Maps New:" << std::to_string(curMapsCount);
62 GTEST_LOG_(INFO) << "Maps Old:" << std::to_string(mapsCount) << "\n";
63
64 auto curMemSize = GetSelfMemoryCount();
65 constexpr double ratio = 1.5;
66 GTEST_LOG_(INFO) << "AfterTest Memory New(KB):" << std::to_string(curMemSize);
67 GTEST_LOG_(INFO) << "Memory Old(KB):" << std::to_string(memCount) << "\n";
68 }
69 }
70 class BacktraceLocalTest : public testing::Test {
71 public:
72 static void SetUpTestCase();
73 static void TearDownTestCase();
74 void SetUp();
75 void TearDown();
76
77 uint32_t fdCount;
78 uint32_t mapsCount;
79 uint64_t memCount;
80
81 static uint32_t fdCountTotal;
82 static uint32_t mapsCountTotal;
83 static uint64_t memCountTotal;
84 };
85
86 uint32_t BacktraceLocalTest::fdCountTotal = 0;
87 uint32_t BacktraceLocalTest::mapsCountTotal = 0;
88 uint64_t BacktraceLocalTest::memCountTotal = 0;
89
SetUpTestCase()90 void BacktraceLocalTest::SetUpTestCase()
91 {
92 BacktraceLocalTest::fdCountTotal = GetSelfFdCount();
93 BacktraceLocalTest::mapsCountTotal = GetSelfMapsCount();
94 BacktraceLocalTest::memCountTotal = GetSelfMemoryCount();
95 }
96
TearDownTestCase()97 void BacktraceLocalTest::TearDownTestCase()
98 {
99 CheckResourceUsage(fdCountTotal, mapsCountTotal, memCountTotal);
100 }
101
SetUp()102 void BacktraceLocalTest::SetUp()
103 {
104 fdCount = GetSelfFdCount();
105 mapsCount = GetSelfMapsCount();
106 memCount = GetSelfMemoryCount();
107 }
108
TearDown()109 void BacktraceLocalTest::TearDown()
110 {
111 CheckResourceUsage(fdCount, mapsCount, memCount);
112 }
113
114 /**
115 * @tc.name: BacktraceLocalTest001
116 * @tc.desc: test get backtrace of current thread
117 * @tc.type: FUNC
118 */
119 HWTEST_F(BacktraceLocalTest, BacktraceLocalTest001, TestSize.Level2)
120 {
121 GTEST_LOG_(INFO) << "BacktraceLocalTest001: start.";
122 unw_addr_space_t as;
123 unw_init_local_address_space(&as);
124 if (as == nullptr) {
125 FAIL() << "Failed to init address space.\n";
126 return;
127 }
128
129 auto symbol = std::make_shared<DfxSymbols>();
130 ElapsedTime counter;
131 BacktraceLocalThread thread(BACKTRACE_CURRENT_THREAD);
132 ASSERT_EQ(true, thread.Unwind(as, symbol, 0));
133
134 GTEST_LOG_(INFO) << "UnwindCurrentCost:" << counter.Elapsed();
135 const auto& frames = thread.GetFrames();
136 ASSERT_GT(frames.size(), 0);
137 for (const auto& frame : frames) {
138 GTEST_LOG_(INFO) << DfxFrameFormat::GetFrameStr(frame);
139 }
140
141 unw_destroy_local_address_space(as);
142 GTEST_LOG_(INFO) << "BacktraceLocalTest001: end.";
143 }
144
145 int32_t g_tid = 0;
146 std::mutex g_mutex;
Test002()147 __attribute__((noinline)) void Test002()
148 {
149 printf("Test002\n");
150 g_mutex.lock();
151 g_mutex.unlock();
152 }
153
Test001()154 __attribute__((noinline)) void Test001()
155 {
156 g_tid = gettid();
157 printf("Test001:%d\n", g_tid);
158 Test002();
159 }
160
161 /**
162 * @tc.name: BacktraceLocalTest003
163 * @tc.desc: test get backtrace of a child thread
164 * @tc.type: FUNC
165 */
166 HWTEST_F(BacktraceLocalTest, BacktraceLocalTest003, TestSize.Level2)
167 {
168 GTEST_LOG_(INFO) << "BacktraceLocalTest003: start.";
169 unw_addr_space_t as;
170 unw_init_local_address_space(&as);
171 if (as == nullptr) {
172 FAIL() << "Failed to init address space.\n";
173 return;
174 }
175 g_mutex.lock();
176 std::thread backtraceThread(Test001);
177 sleep(1);
178 if (g_tid <= 0) {
179 FAIL() << "Failed to create child thread.\n";
180 }
181
182 auto symbol = std::make_shared<DfxSymbols>();
183 ElapsedTime counter;
184 BacktraceLocalThread thread(g_tid);
185 ASSERT_EQ(true, thread.Unwind(as, symbol, 0));
186 GTEST_LOG_(INFO) << "UnwindCurrentCost:" << counter.Elapsed();
187 BacktraceLocalContext::GetInstance().CleanUp();
188 const auto& frames = thread.GetFrames();
189 ASSERT_GT(frames.size(), 0);
190 auto backtraceStr = thread.GetFormatedStr(false);
191 ASSERT_GT(backtraceStr.size(), 0);
192 GTEST_LOG_(INFO) << backtraceStr;
193 g_mutex.unlock();
194 unw_destroy_local_address_space(as);
195 g_tid = 0;
196 if (backtraceThread.joinable()) {
197 backtraceThread.join();
198 }
199 GTEST_LOG_(INFO) << "BacktraceLocalTest003: end.";
200 }
201
202 using GetMap = void (*)(void);
203 /**
204 * @tc.name: BacktraceLocalTest004
205 * @tc.desc: test whether crash log is generated if we call a dlsym func after dlclose
206 * @tc.type: FUNC
207 */
208 HWTEST_F(BacktraceLocalTest, BacktraceLocalTest004, TestSize.Level2)
209 {
210 GTEST_LOG_(INFO) << "BacktraceLocalTest004: start.";
211 pid_t childPid = fork();
212 if (childPid == 0) {
213 void* handle = dlopen("libunwind.z.so", RTLD_LAZY);
214 if (handle == nullptr) {
215 FAIL();
216 }
217
218 auto getMap = reinterpret_cast<GetMap>(dlsym(handle, "unw_get_map"));
219 if (getMap == nullptr) {
220 dlclose(handle);
221 FAIL();
222 }
223 // close before call functrion
224 dlclose(handle);
225 getMap();
226 _exit(0);
227 }
228
229 int status;
230 int ret = wait(&status);
231 sleep(1);
232 std::string path = GetCppCrashFileName(childPid);
233 if (path.empty()) {
234 FAIL();
235 }
236 GTEST_LOG_(INFO) << "LogFile: " << path;
237
238 std::string content;
239 if (!OHOS::HiviewDFX::LoadStringFromFile(path, content)) {
240 FAIL();
241 }
242
243 // both dlclose debug enabled and disabled cases
244 if (content.find("Not mapped pc") == std::string::npos &&
245 content.find("libunwind") == std::string::npos) {
246 FAIL();
247 }
248
249 GTEST_LOG_(INFO) << "BacktraceLocalTest004: end.";
250 }
251
252 /**
253 * @tc.name: BacktraceLocalTest005
254 * @tc.desc: test get backtrace of current process
255 * @tc.type: FUNC
256 */
257 HWTEST_F(BacktraceLocalTest, BacktraceLocalTest005, TestSize.Level2)
258 {
259 GTEST_LOG_(INFO) << "BacktraceLocalTest005: start.";
260 g_mutex.lock();
261 std::thread backtraceThread(Test001);
262 sleep(1);
263 if (g_tid <= 0) {
264 FAIL() << "Failed to create child thread.\n";
265 }
266
267 std::string stacktrace = GetProcessStacktrace();
268 ASSERT_GT(stacktrace.size(), 0);
269 GTEST_LOG_(INFO) << stacktrace;
270
271 if (stacktrace.find("backtrace_local_test") == std::string::npos) {
272 FAIL() << "Failed to find pid key word.\n";
273 }
274
275 g_mutex.unlock();
276 g_tid = 0;
277 if (backtraceThread.joinable()) {
278 backtraceThread.join();
279 }
280 GTEST_LOG_(INFO) << "BacktraceLocalTest005: end.";
281 }
282
283 /**
284 * @tc.name: BacktraceLocalTest006
285 * @tc.desc: test GetTrace C interface
286 * @tc.type: FUNC
287 */
288 HWTEST_F(BacktraceLocalTest, BacktraceLocalTest006, TestSize.Level2)
289 {
290 GTEST_LOG_(INFO) << "BacktraceLocalTest006: start.";
291 const char* trace = GetTrace();
292 std::string stacktrace(trace);
293 GTEST_LOG_(INFO) << stacktrace;
294 ASSERT_TRUE(stacktrace.find("#00") != std::string::npos);
295 ASSERT_TRUE(stacktrace.find("pc") != std::string::npos);
296 ASSERT_TRUE(stacktrace.find("backtrace_local_test") != std::string::npos);
297 GTEST_LOG_(INFO) << "BacktraceLocalTest006: end.";
298 }
299 } // namespace HiviewDFX
300 } // namepsace OHOS
301