* Copyright (c) 2024-2026 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 "dfx_kernel_stack.h"
#include <dlfcn.h>
#include <fcntl.h>
#include <regex>
#include <string_ex.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "dfx_log.h"
#include "dfx_trace_dlsym.h"
#include "elapsed_time.h"
#include "proc_util.h"
#include "smart_fd.h"
#define LOGGER_GET_STACK _IO(0xAB, 9)
#define LOGGER_GET_STACK_ARKTS _IO(0xAB, 10)
namespace OHOS {
namespace HiviewDFX {
namespace {
const char* const BBOX_PATH = "/dev/bbox";
constexpr int BUFF_STACK_SIZE = 20 * 1024;
const uint32_t MAGIC_NUM = 0x9517;
typedef struct HstackVal {
uint32_t magic {0};
pid_t pid {0};
char hstackLogBuff[BUFF_STACK_SIZE] {0};
} HstackVal;
}
int32_t DfxGetKernelStack(int32_t pid, std::string& kernelStack, bool needArkts)
{
auto kstackBuf = std::make_shared<HstackVal>();
if (kstackBuf == nullptr) {
DFXLOGW("Failed create HstackVal, pid:%{public}d, errno:%{public}d", pid, errno);
return KERNELSTACK_ECREATE;
}
kstackBuf->pid = pid;
kstackBuf->magic = MAGIC_NUM;
SmartFd fd(open(BBOX_PATH, O_WRONLY | O_CLOEXEC));
if (!fd) {
DFXLOGW("Failed to open bbox, pid:%{public}d, errno:%{public}d", pid, errno);
return KERNELSTACK_EOPEN;
}
int ctlCode = needArkts ? LOGGER_GET_STACK_ARKTS : LOGGER_GET_STACK;
int ret = ioctl(fd.GetFd(), ctlCode, kstackBuf.get());
int32_t res = KERNELSTACK_ESUCCESS;
if (ret != 0) {
DFXLOGW("Failed to get pid(%{public}d) kernel stack, errno:%{public}d, ret:%{public}d", pid, errno, ret);
res = KERNELSTACK_EIOCTL;
} else {
size_t len = strnlen(kstackBuf->hstackLogBuff, BUFF_STACK_SIZE);
kernelStack = std::string(kstackBuf->hstackLogBuff, len);
}
return res;
}
bool FormatThreadKernelStack(const std::string& kernelStack, DfxThreadStack& threadStack,
DfxOfflineParser *parser)
{
#ifdef __aarch64__
DFX_TRACE_SCOPED_DLSYM("FormatThreadKernelStack");
std::regex headerPattern(R"(name=(.{1,20}), tid=(\d{1,10}), ([\w\=\.]{1,256}, ){3}pid=(\d{1,10}))");
std::smatch result;
if (!regex_search(kernelStack, result, headerPattern)) {
DFXLOGI("search thread name failed");
return false;
}
threadStack.threadName = result[1].str();
int base {10};
threadStack.tid = strtol(result[2].str().c_str(), nullptr, base);
auto pos = kernelStack.rfind("pid=" + result[result.size() - 1].str() + "):");
if (pos == std::string::npos) {
DFXLOGE("search kernel stack failed!");
return false;
}
std::regex threadStatReg(
R"(state=.{0,16}, utime=\d{0,64}, stime=\d{0,64}, priority=[0-9-]{0,64}, nice=[0-9-]{0,64}, clk=\d{0,64})");
if (regex_search(kernelStack, result, threadStatReg)) {
threadStack.threadStat = result[0].str();
} else {
DFXLOGW("search thread stat failed");
}
size_t index = 0;
std::regex framePattern(R"(\[(\w{16})\]\<[\w\?+/]{1,1024}\> \(([\w\-./]{1,1024})\))");
for (std::sregex_iterator it = std::sregex_iterator(kernelStack.begin() + pos, kernelStack.end(), framePattern);
it != std::sregex_iterator(); ++it) {
if ((*it)[2].str().rfind(".elf") != std::string::npos) {
continue;
}
DfxFrame frame;
frame.index = index++;
base = 16;
frame.relPc = strtoull((*it)[1].str().c_str(), nullptr, base);
frame.mapName = (*it)[2].str();
if (parser) {
parser->ParseSymbolWithFrame(frame);
}
threadStack.frames.emplace_back(frame);
}
return true;
#else
return false;
#endif
}
bool FormatProcessKernelStack(const std::string& kernelStack, std::vector<DfxThreadStack>& processStack)
{
#if !defined(is_ohos_lite) && defined(__aarch64__)
ElapsedTime counter;
std::vector<std::string> threadKernelStackVec;
std::string keyWord = "Thread info:";
OHOS::SplitStr(kernelStack, keyWord, threadKernelStackVec);
if (threadKernelStackVec.size() == 1 && kernelStack.find(keyWord) == std::string::npos) {
DFXLOGE("Invalid kernelStack, please check it!");
return false;
}
for (const std::string& threadKernelStack : threadKernelStackVec) {
DfxThreadStack threadStack;
if (FormatThreadKernelStack(threadKernelStack, threadStack)) {
processStack.emplace_back(threadStack);
}
}
auto costTime = counter.Elapsed<std::chrono::milliseconds>();
DFXLOGI("FormatProcessKernelStack cost time = %{public}" PRId64 " ms", costTime);
return true;
#else
return false;
#endif
}
}
}