/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "callstack_test.h" using namespace testing::ext; using namespace testing; using namespace std; using namespace OHOS::HiviewDFX; namespace OHOS { namespace Developtools { namespace HiPerf { class CallStackTest : public testing::Test { public: static void SetUpTestCase(void); static void TearDownTestCase(void); void SetUp(); void TearDown(); default_random_engine rnd_; }; void CallStackTest::SetUpTestCase() { DebugLogger::GetInstance()->Reset(); DebugLogger::GetInstance()->OpenLog(DEFAULT_UT_LOG_DIR + "CallStackTest.txt"); } void CallStackTest::TearDownTestCase() { DebugLogger::GetInstance()->RestoreLog(); } void CallStackTest::SetUp() {} void CallStackTest::TearDown() {} /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackEmpty, TestSize.Level1) { /* 3 2 1 cache A -> B -> C new C expand A -> B -> C */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = {}; ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2), 0u); ASSERT_NE(stack1, stack2); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackC, TestSize.Level1) { /* 3 2 1 cache A -> B -> C new C expand A -> B -> C */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = { {0x1u, 0x1u}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2), 2u); ASSERT_EQ(stack1, stack2); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackBC, TestSize.Level1) { /* 3 2 1 cache A -> B -> C new B -> C expand A -> B -> C */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = { {0x1u, 0x1u}, {0x2u, 0x2u}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2), 1u); ASSERT_EQ(stack1, stack2); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackABC, TestSize.Level1) { /* 3 2 1 cache A -> B -> C new A -> B -> C expand A -> B -> C */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2), 0u); ASSERT_EQ(stack1, stack2); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackAB, TestSize.Level1) { /* 3 2 1 cache A -> B -> C new A -> B expand A -> B */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = { {0x2u, 0x2u}, {0x3u, 0x3u}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2), 0u); ASSERT_NE(stack1, stack2); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackA, TestSize.Level1) { /* 3 2 1 cache A -> B -> C new A expand A */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = { {0x3u, 0x3u}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2), 0u); ASSERT_NE(stack1, stack2); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackB, TestSize.Level1) { /* 3 2 1 cache A -> B -> C new B expand A -> B */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = { {0x2u, 0x2u}, }; std::vector<CallFrame> stack3 = { {0x2u, 0x2u}, {0x3u, 0x3u}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2), 1u); ASSERT_NE(stack1, stack2); ASSERT_EQ(stack3, stack2); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackB2, TestSize.Level1) { /* 3 2 1 cache A -> B -> C new B expand A -> B */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = { {0x2u, 0x2u}, }; std::vector<CallFrame> stack3 = { {0x2u, 0x2u}, {0x3u, 0x3u}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1, 2), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2, 2), 0u); ASSERT_NE(stack1, stack2); ASSERT_NE(stack3, stack2); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackB0, TestSize.Level1) { /* 3 2 1 cache A -> B -> C new B expand A -> B */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = { {0x2u, 0x2u}, }; std::vector<CallFrame> stack3 = { {0x2u, 0x2u}, {0x3u, 0x3u}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1, 0), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2, 0), 0u); ASSERT_NE(stack1, stack2); ASSERT_NE(stack3, stack2); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackBC2, TestSize.Level1) { /* 3 2 1 cache A -> B -> C new B -> C expand A -> B -> C */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH, true); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = { {0x1u, 0x1u}, {0x2u, 0x2u}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1, 2), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2, 2), 1u); ASSERT_EQ(stack1, stack2); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackABCDE, TestSize.Level1) { /* 0. A -> B -> C -> E -> F 1. C -> E -> F 2. B -> C 3. A -> B -> C 4. B -> F -> F */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stackFull = { {0xE, 0xE}, {0xD, 0xD}, {0xC, 0xC}, {0xB, 0xB}, {0xA, 0xA}, }; std::vector<CallFrame> stackBC = { {0xC, 0xC}, {0xB, 0xB}, }; std::vector<CallFrame> stackABC = { {0xC, 0xC}, {0xB, 0xB}, {0xA, 0xA}, }; std::vector<CallFrame> stackBFF = { {0xF, 0xF}, {0xF, 0xF}, {0xB, 0xB}, }; std::vector<CallFrame> stackBFF2 = { {0xF, 0xF}, {0xF, 0xF}, {0xB, 0xB}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stackFull), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stackBC), 1u); ASSERT_EQ(callStack.ExpandCallStack(0, stackABC), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stackBFF), 1u); // use stackBFF ASSERT_EQ(callStack.ExpandCallStack(0, stackBFF2, 2), 1u); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackFailure, TestSize.Level1) { /* 0. A -> B -> C -> E -> F 1. C -> E -> F 2. B -> C 3. A -> B -> C 4. B -> F -> F */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stackFull = { {0xC, 0xC}, {0xB, 0xB}, {0xA, 0xA}, }; std::vector<CallFrame> stackDE = { {0xE, 0xE}, {0xD, 0xD}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stackFull), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stackDE), 0u); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackTwoChance, TestSize.Level1) { /* 0. A -> B -> C -> E -> F 1. 2 -> C -> E -> F 2. C */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH); CallStack callStack; std::vector<CallFrame> stack0 = { {0xE, 0xE}, {0xD, 0xD}, {0xC, 0xC}, {0xB, 0xB}, {0xA, 0xA}, }; std::vector<CallFrame> stack1 = { {0xE, 0xE}, {0xD, 0xD}, {0xC, 0xC}, {0x2, 0x2}, }; std::vector<CallFrame> stackC = { {0xC, 0xC}, }; std::vector<CallFrame> stackC2 = { {0xC, 0xC}, {0x2, 0x2}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack0), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stackC), 1u); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackFullCache, TestSize.Level1) { CallStack callStack; for (size_t i = 0; i < MAX_CALL_FRAME_EXPAND_CACHE_SIZE; i++) { std::vector<CallFrame> stack = {{rnd_(), rnd_()}}; callStack.ExpandCallStack(0, stack); } for (size_t i = 0; i < MAX_CALL_FRAME_EXPAND_CACHE_SIZE; i++) { std::vector<CallFrame> stack = {{rnd_(), rnd_()}}; callStack.ExpandCallStack(0, stack); } EXPECT_EQ(callStack.cachedCallFramesMap_[0].size(), MAX_CALL_FRAME_EXPAND_CACHE_SIZE); } /** * @tc.name: LibUnwindEmptyFunc * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, LibUnwindEmptyFunc, TestSize.Level1) { CallStack callStack = {}; unw_addr_space_t as = {}; unw_word_t word = {}; unw_word_t *wordPtr = {}; void *voidPtr = {}; char *buf = {}; size_t size = {}; unw_proc_info_t pi = {}; unw_regnum_t rn = {}; unw_fpreg_t fp = {}; unw_cursor_t t = {}; EXPECT_LE(CallStack::getProcName(as, word, buf, size, wordPtr, voidPtr), 0); CallStack::PutUnwindInfo(as, &pi, voidPtr); EXPECT_LE(CallStack::AccessFpreg(as, rn, &fp, 0, voidPtr), 0); EXPECT_LE(CallStack::Resume(as, &t, voidPtr), 0); } /** * @tc.name: GetUnwErrorName * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, GetUnwErrorName, TestSize.Level1) { EXPECT_STREQ(CallStack::GetUnwErrorName(UNW_ENOINFO).c_str(), "UNKNOW_UNW_ERROR"); EXPECT_STRNE(CallStack::GetUnwErrorName(-UNW_ENOINFO).c_str(), "UNKNOW_UNW_ERROR"); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackSmall, TestSize.Level1) { CallStack callStack; std::vector<CallFrame> stack0 = {}; std::vector<CallFrame> stack1 = {{0x1, 0x1}}; std::vector<CallFrame> stack2 = {{0x1, 0x1}, {0x2, 0x2}}; ASSERT_EQ(callStack.ExpandCallStack(0, stack0), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2, 2), 0u); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackLimit, TestSize.Level1) { /* 3 2 1 0 cache A -> B -> C stack2 C expand C stack3 B -> C expand A -> B -> C stack4 C -> D expand C */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH, true); CallStack callStack; std::vector<CallFrame> stack1 = { {0x1u, 0x1u}, {0x2u, 0x2u}, {0x3u, 0x3u}, }; std::vector<CallFrame> stack2 = { {0x1u, 0x1u}, }; std::vector<CallFrame> stack3 = { {0x1u, 0x1u}, {0x2u, 0x2u}, }; std::vector<CallFrame> stack4 = { {0x0u, 0x0u}, {0x1u, 0x1u}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1, 2u), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2, 2u), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack3, 2u), 1u); EXPECT_THAT(stack1, ContainerEq(stack3)); ASSERT_EQ(callStack.ExpandCallStack(0, stack4, 2u), 0u); } /** * @tc.name: ExpandCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, ExpendCallStackABABAB, TestSize.Level1) { /* Caller Called cache A -> B -> C -> A -> B -> C -> A -> B stack2 C expand A -> B -> C -> A -> B -> C stack3 B -> C expand A -> B -> C -> A -> B -> C stack4 C -> D expand A -> B -> C -> A -> B -> C -> D */ ScopeDebugLevel tempLogLevel(LEVEL_MUCH, true); CallStack callStack; std::vector<CallFrame> stack1 = { {0xb, 0xb}, {0xa, 0xa}, {0xc, 0xc}, {0xb, 0xb}, {0xa, 0xa}, {0xc, 0xc}, {0xb, 0xb}, {0xa, 0xa}, }; std::vector<CallFrame> stack2 = { {0xc, 0xc}, }; std::vector<CallFrame> stack3 = { {0xc, 0xc}, {0xb, 0xb}, }; std::vector<CallFrame> stack4 = { {0xd, 0xd}, {0xc, 0xc}, }; ASSERT_EQ(callStack.ExpandCallStack(0, stack1), 0u); ASSERT_EQ(callStack.ExpandCallStack(0, stack2), 5u); ASSERT_EQ(callStack.ExpandCallStack(0, stack3), 4u); ASSERT_EQ(callStack.ExpandCallStack(0, stack4), 5u); } /** * @tc.name: UnwindCallStack * @tc.desc: * @tc.type: FUNC */ HWTEST_F(CallStackTest, UnwindCallStack, TestSize.Level1) { #if is_linux return; #endif std::vector<u64> regs; std::vector<u8> data; LoadFromFile(PATH_RESOURCE_TEST_DWARF_DATA + TEST_DWARF_USER_REGS_0, regs); LoadFromFile(PATH_RESOURCE_TEST_DWARF_DATA + TEST_DWARF_USER_DATA_0, data); if (regs.size() > 0 and data.size() > 0) { #ifdef __arm__ ASSERT_EQ(regs.size(), 16u); #endif std::vector<std::unique_ptr<SymbolsFile>> symbolsFiles; auto &symbolsFile = symbolsFiles.emplace_back(SymbolsFile::CreateSymbolsFile( SYMBOL_ELF_FILE, PATH_RESOURCE_TEST_DWARF_DATA + TEST_DWARF_ELF)); ASSERT_EQ(symbolsFile->LoadSymbols(), true); // fix the name symbolsFile->filePath_ = TEST_DWARF_MMAP.front().fileName; VirtualThread thread(getpid(), symbolsFiles); MakeMaps(thread); std::vector<CallFrame> callFrames; CallStack callStack; callStack.UnwindCallStack(thread, false, regs.data(), regs.size(), data.data(), data.size(), callFrames); ASSERT_LE(TEST_DWARF_FRAMES.size(), callFrames.size()); for (size_t i = 0; i < TEST_DWARF_FRAMES.size(); i++) { EXPECT_EQ(TEST_DWARF_FRAMES[i].ip, callFrames[i].ip_); EXPECT_EQ(TEST_DWARF_FRAMES[i].sp, callFrames[i].sp_); } } } } // namespace HiPerf } // namespace Developtools } // namespace OHOS