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