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)
28namespace OHOS {
29namespace HiviewDFX {
30namespace {
31// keep sync with defines in kernel/hicollie
32const char* const BBOX_PATH = "/dev/bbox";
33const int BUFF_STACK_SIZE = 20 * 1024;
34const uint32_t MAGIC_NUM = 0x9517;
35typedef struct HstackVal {
36    uint32_t magic {0};
37    pid_t pid {0};
38    char hstackLogBuff[BUFF_STACK_SIZE] {0};
39} HstackVal;
40}
41int DfxGetKernelStack(int32_t pid, std::string& kernelStack)
42{
43    auto kstackBuf = std::make_shared<HstackVal>();
44    if (kstackBuf == nullptr) {
45        DFXLOGW("Failed create HstackVal, errno:%{public}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        DFXLOGW("Failed to open bbox, errno:%{public}d", errno);
53        return -1;
54    }
55
56    int ret = ioctl(fd, LOGGER_GET_STACK, kstackBuf.get());
57    if (ret != 0) {
58        DFXLOGW("Failed to get pid(%{public}d) kernel stack, errno:%{public}d", pid, errno);
59    } else {
60        kernelStack = std::string(kstackBuf->hstackLogBuff);
61    }
62    close(fd);
63    return ret;
64}
65
66bool 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        DFXLOGI("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
102bool 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}