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 "dfx_kernel_stack.h"
17
18 #include <dlfcn.h>
19 #include <fcntl.h>
20 #include <regex>
21 #include <string_ex.h>
22 #include <sys/ioctl.h>
23 #include <unistd.h>
24
25 #include "dfx_log.h"
26 #include "smart_fd.h"
27
28 #define LOGGER_GET_STACK _IO(0xAB, 9)
29 namespace OHOS {
30 namespace HiviewDFX {
31 namespace {
32 // keep sync with defines in kernel/hicollie
33 const char* const BBOX_PATH = "/dev/bbox";
34 const int BUFF_STACK_SIZE = 20 * 1024;
35 const uint32_t MAGIC_NUM = 0x9517;
36 typedef struct HstackVal {
37 uint32_t magic {0};
38 pid_t pid {0};
39 char hstackLogBuff[BUFF_STACK_SIZE] {0};
40 } HstackVal;
41 }
DfxGetKernelStack(int32_t pid,std::string & kernelStack)42 int32_t DfxGetKernelStack(int32_t pid, std::string& kernelStack)
43 {
44 auto kstackBuf = std::make_shared<HstackVal>();
45 if (kstackBuf == nullptr) {
46 DFXLOGW("Failed create HstackVal, pid:%{public}d, errno:%{public}d", pid, errno);
47 return KERNELSTACK_ECREATE;
48 }
49 kstackBuf->pid = pid;
50 kstackBuf->magic = MAGIC_NUM;
51 SmartFd fd(open(BBOX_PATH, O_WRONLY | O_CLOEXEC));
52 if (!fd) {
53 DFXLOGW("Failed to open bbox, pid:%{public}d, errno:%{public}d", pid, errno);
54 return KERNELSTACK_EOPEN;
55 }
56
57 int ret = ioctl(fd.GetFd(), LOGGER_GET_STACK, kstackBuf.get());
58 int32_t res = KERNELSTACK_ESUCCESS;
59 if (ret != 0) {
60 DFXLOGW("Failed to get pid(%{public}d) kernel stack, errno:%{public}d", pid, errno);
61 res = KERNELSTACK_EIOCTL;
62 } else {
63 kernelStack = std::string(kstackBuf->hstackLogBuff);
64 }
65 return res;
66 }
67
FormatThreadKernelStack(const std::string & kernelStack,DfxThreadStack & threadStack)68 bool FormatThreadKernelStack(const std::string& kernelStack, DfxThreadStack& threadStack)
69 {
70 #ifdef __aarch64__
71 std::regex headerPattern(R"(name=(.{1,20}), tid=(\d{1,10}), ([\w\=\.]{1,256}, ){3}pid=(\d{1,10}))");
72 std::smatch result;
73 if (!regex_search(kernelStack, result, headerPattern)) {
74 DFXLOGI("search thread name failed");
75 return false;
76 }
77 threadStack.threadName = result[1].str();
78 int base {10};
79 threadStack.tid = strtol(result[2].str().c_str(), nullptr, base); // 2 : second of searched element
80 auto pos = kernelStack.rfind("pid=" + result[result.size() - 1].str());
81 if (pos == std::string::npos) {
82 return false;
83 }
84 size_t index = 0;
85 std::regex framePattern(R"(\[(\w{16})\]\<[\w\?+/]{1,1024}\> \(([\w\-./]{1,1024})\))");
86 for (std::sregex_iterator it = std::sregex_iterator(kernelStack.begin() + pos, kernelStack.end(), framePattern);
87 it != std::sregex_iterator(); ++it) {
88 if ((*it)[2].str().rfind(".elf") != std::string::npos) { // 2 : second of searched element is map name
89 continue;
90 }
91 DfxFrame frame;
92 frame.index = index++;
93 base = 16; // 16 : Hexadecimal
94 frame.relPc = strtoull((*it)[1].str().c_str(), nullptr, base);
95 frame.mapName = (*it)[2].str(); // 2 : second of searched element is map name
96 threadStack.frames.emplace_back(frame);
97 }
98 return true;
99 #else
100 return false;
101 #endif
102 }
103
FormatProcessKernelStack(const std::string & kernelStack,std::vector<DfxThreadStack> & processStack)104 bool FormatProcessKernelStack(const std::string& kernelStack, std::vector<DfxThreadStack>& processStack)
105 {
106 #if !defined(is_ohos_lite) && defined(__aarch64__)
107 std::vector<std::string> threadKernelStackVec;
108 std::string keyWord = "Thread info:";
109 OHOS::SplitStr(kernelStack, keyWord, threadKernelStackVec);
110 if (threadKernelStackVec.size() == 1 && kernelStack.find(keyWord) == std::string::npos) {
111 DFXLOGE("Invalid kernelStack, please check it!");
112 return false;
113 }
114 for (const std::string& threadKernelStack : threadKernelStackVec) {
115 DfxThreadStack threadStack;
116 if (FormatThreadKernelStack(threadKernelStack, threadStack)) {
117 processStack.emplace_back(threadStack);
118 }
119 }
120 return true;
121 #else
122 return false;
123 #endif
124 }
125 }
126 }