From 8fc857049519a73f1c4c5efb0dab15f0907f540a Mon Sep 17 00:00:00 2001 From: xlgitee Date: Fri, 27 Dec 2024 09:41:06 +0800 Subject: [PATCH] kernel crash snapshot Signed-off-by: xlgitee Change-Id: I88a6c70b57281878a11d0a0e4593dfe7d1509d0e --- interfaces/common/dfx_dump_request.h | 7 +- .../signal_handler/dfx_dumprequest.c | 85 ++- services/BUILD.gn | 1 + services/config/faultloggerd.cfg | 3 +- services/fault_kernel_snapshot.cpp | 536 ++++++++++++++++++ services/fault_kernel_snapshot.h | 28 + services/main.cpp | 3 + tools/process_dump/process_dumper.cpp | 38 +- 8 files changed, 683 insertions(+), 18 deletions(-) create mode 100644 services/fault_kernel_snapshot.cpp create mode 100644 services/fault_kernel_snapshot.h diff --git a/interfaces/common/dfx_dump_request.h b/interfaces/common/dfx_dump_request.h index 37a26948e..25a3cc63a 100644 --- a/interfaces/common/dfx_dump_request.h +++ b/interfaces/common/dfx_dump_request.h @@ -106,13 +106,16 @@ struct ProcessDumpRequest { /** is integrate crash dump flow 0:false 1:true */ int32_t dumpMode; /** vm process pid addr */ - intptr_t vmProcRealPid; + intptr_t vmProcRealPidAddr; /** whether block source process pid */ - intptr_t isBlockCrash; + intptr_t blockCrashExitAddr; + /** whether processdump unwind crash success */ + intptr_t unwindResultAddr; uintptr_t crashObj; }; static const int CRASH_BLOCK_EXIT_FLAG = 0x13579BDF; +static const int CRASH_UNWIND_SUCCESS_FLAG = 0x2468ACEF; #ifdef __cplusplus } #endif diff --git a/interfaces/innerkits/signal_handler/dfx_dumprequest.c b/interfaces/innerkits/signal_handler/dfx_dumprequest.c index e223794d4..41d86dc37 100644 --- a/interfaces/innerkits/signal_handler/dfx_dumprequest.c +++ b/interfaces/innerkits/signal_handler/dfx_dumprequest.c @@ -74,8 +74,9 @@ static struct ProcessDumpRequest *g_request = NULL; static void *g_reservedChildStack = NULL; -static long g_blockFlag = 0; +static long g_blockExit = 0; static long g_vmRealPid = 0; +static long g_unwindResult = 0; enum PIPE_FD_TYPE { WRITE_TO_DUMP, @@ -90,6 +91,7 @@ static int g_pipeFds[PIPE_MAX][2] = { static const int SIGNALHANDLER_TIMEOUT = 10000; // 10000 us static const int ALARM_TIME_S = 10; +static const uint32_t CRASH_SNAPSHOT_FLAG = 0x8; enum DumpPreparationStage { CREATE_PIPE_FAIL = 1, SET_PIPE_LEN_FAIL, @@ -104,6 +106,17 @@ static bool InitPipe(void); static bool ReadPipeTimeout(int fd, uint64_t timeout, uint32_t* value); static bool ReadProcessDumpGetRegsMsg(void); +static void ResetFlags(void) +{ + g_unwindResult = 0; + g_blockExit = 0; +} + +static bool IsDumpSignal(int signo) +{ + return signo == SIGDUMP || signo == SIGLEAK_STACK; +} + static const char* GetCrashDescription(const int32_t errCode) { size_t i; @@ -430,8 +443,9 @@ static bool StartProcessdump(void) DFXLOGI("dump remain %{public}" PRId64 "ms", endTime - curTime); } if (endTime == 0 || endTime > curTime) { - g_request->isBlockCrash = (intptr_t)&g_blockFlag; - g_request->vmProcRealPid = (intptr_t)&g_vmRealPid; + g_request->blockCrashExitAddr = (intptr_t)&g_blockExit; + g_request->vmProcRealPidAddr = (intptr_t)&g_vmRealPid; + g_request->unwindResultAddr = (intptr_t)&g_unwindResult; DFX_ExecDump(); } else { DFXLOGI("current has spend all time, not execl processdump"); @@ -489,8 +503,8 @@ static void CleanPipe(void) } } -static bool InitPipe(void) -{ + static bool InitPipe(void) + { bool ret = true; for (int i = 0; i < PIPE_MAX; i++) { if (syscall(SYS_pipe2, g_pipeFds[i], 0) == -1) { @@ -567,14 +581,63 @@ static bool ReadProcessDumpGetRegsMsg(void) return false; } -static void ReadUnwindFinishMsg(int sig) +static void SetKernelSnapshot(bool enable) { - if (sig == SIGDUMP) { + const char *filePath = "/proc/self/unexpected_die_catch"; + if (access(filePath, F_OK) < 0) { + return; + } + int dieCatchFd = open(filePath, O_RDWR); + if (dieCatchFd < 0) { + DFXLOGE("Failed to open unexpecterd_die_catch %{public}d", errno); return; } + do { + char val[10] = {0}; // 10 : to save diecatch val + if (read(dieCatchFd, val, sizeof(val)) < 0) { + DFXLOGE("Failed to read unexpecterd_die_catch %{public}d", errno); + break; + } + if (lseek(dieCatchFd, 0, SEEK_SET) < 0) { + DFXLOGE("Failed to lseek unexpecterd_die_catch %{public}d", errno); + break; + } - DFXLOGI("crash processdump unwind finish, blockFlag %{public}ld", g_blockFlag); - if (g_blockFlag == CRASH_BLOCK_EXIT_FLAG) { + uint32_t num = (uint32_t)strtoul(val, NULL, 16); // 16 : val is hex + if (errno == ERANGE) { + DFXLOGE("Failed to cast unexpecterd_die_catch val to int %{public}d", errno); + break; + } + if (enable) { + num |= CRASH_SNAPSHOT_FLAG; + } else { + num &= (~CRASH_SNAPSHOT_FLAG); + } + + (void)memset_s(val, sizeof(val), 0, sizeof(val)); + if (snprintf_s(val, sizeof(val), sizeof(val) - 1, "%x", num) < 0) { + DFXLOGE("Failed to format unexpecterd_die_catch val %{public}d", errno); + break; + } + if (write(dieCatchFd, val, sizeof(val)) < 0) { + DFXLOGE("Failed to write unexpecterd_die_catch %{public}d", errno); + } + } while (false); + syscall(SYS_close, dieCatchFd); +} + +static void ReadUnwindFinishMsg(int signo) +{ + if (IsDumpSignal(signo)) { + return; + } + + DFXLOGI("crash processdump unwind finish, unwind success Flag %{public}ld, blockFlag %{public}ld", + g_unwindResult, g_blockExit); + if (g_unwindResult == CRASH_UNWIND_SUCCESS_FLAG) { + SetKernelSnapshot(false); + } + if (g_blockExit == CRASH_BLOCK_EXIT_FLAG) { syscall(SYS_tgkill, g_request->nsPid, g_request->tid, SIGSTOP); } } @@ -583,6 +646,10 @@ static int ProcessDump(int sig) { int prevDumpableStatus = prctl(PR_GET_DUMPABLE); bool isTracerStatusModified = SetDumpState(); + if (!IsDumpSignal(sig)) { + ResetFlags(); + SetKernelSnapshot(true); + } g_request->dumpMode = FUSION_MODE; diff --git a/services/BUILD.gn b/services/BUILD.gn index 75ea5de51..b4cfac56c 100644 --- a/services/BUILD.gn +++ b/services/BUILD.gn @@ -15,6 +15,7 @@ import("//base/hiviewdfx/faultloggerd/faultloggerd.gni") faultloggerd_sources = [ "epoll_manager.cpp", + "fault_kernel_snapshot.cpp", "fault_logger_config.cpp", "fault_logger_daemon.cpp", "fault_logger_server.cpp", diff --git a/services/config/faultloggerd.cfg b/services/config/faultloggerd.cfg index 2a37ecebf..083ccda08 100644 --- a/services/config/faultloggerd.cfg +++ b/services/config/faultloggerd.cfg @@ -13,7 +13,8 @@ "mkdir /data/log/faultlog 0750 hiview log", "mkdir /data/log/faultlog/temp 0770 system system", "restorecon /dev/unix/socket/faultloggerd.server", - "restorecon /dev/unix/socket/faultloggerd.crash.server" + "restorecon /dev/unix/socket/faultloggerd.crash.server", + "chown faultloggerd root /sys/kbox/snapshot_clear" ] } ], diff --git a/services/fault_kernel_snapshot.cpp b/services/fault_kernel_snapshot.cpp new file mode 100644 index 000000000..4ac9754a9 --- /dev/null +++ b/services/fault_kernel_snapshot.cpp @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2024 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 "fault_kernel_snapshot.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef is_ohos_lite +#include "parameters.h" +#endif // !is_ohos_lite + +#include "faultlogger_client_msg.h" + +#include "dfx_log.h" +#include "string_util.h" + +namespace OHOS { +namespace HiviewDFX { +namespace { +constexpr const char * const KERNEL_KBOX_SNAPSHOT = "/sys/kbox/snapshot_clear"; +constexpr const char * const KBOX_SNAPSHOT_DUMP_PATH = "/data/log/faultlog/temp/"; +constexpr const char * const KERNEL_SNAPSHOT_CHECK_INTERVAL = "kernel_snapshot_check_interval"; +constexpr const char * const DEFAULT_CHECK_INTERVAL = "60"; +constexpr const char * const KERNEL_SNAPSHOT_REASON = "CppCrashKernelSnapshot"; +constexpr int MIN_CHECK_INTERVAL = 3; +constexpr int BUFFER_LEN = 1024; +constexpr int SEQUENCE_LEN = 7; + +enum class CrashSection { + TIME_STAMP, + PID, + UID, + PROCESS_NAME, + REASON, + FAULT_THREAD_INFO, + REGISTERS, + MEMORY_NEAR_REGISTERS, + FAULT_STACK, + MAPS, + EXCEPTION_REGISTERS, + INVALID_SECTION +}; + +enum class SnapshotSection { + TRANSACTION_START, + EXCEPTION_REGISTERS, + ABORT_ADDRESS_PTE, + THREAD_INFO, + DUMP_REGISTERS, + DUMP_FPU_OR_SIMD_REGISTERS, + STACK_BACKTRACE, + ELF_LOAD_INFO, + DATA_ON_TARGET_OF_LAST_BL, + DATA_AROUND_REGS, + CONTENT_OF_USER_STACK, + BASE_ACTV_DUMPED, + TRANSACTION_END +}; + +struct SnapshotSectionInfo { + SnapshotSection type; + const char* key; +}; + +const SnapshotSectionInfo SNAPSHOT_SECTION_KEYWORDS[] = { + {SnapshotSection::TRANSACTION_START, "[transaction start] now mono_time"}, + {SnapshotSection::EXCEPTION_REGISTERS, "Exception registers:"}, + {SnapshotSection::ABORT_ADDRESS_PTE, "Abort address pte"}, + {SnapshotSection::THREAD_INFO, "Thread info:"}, + {SnapshotSection::DUMP_REGISTERS, "Dump registers:"}, + {SnapshotSection::DUMP_FPU_OR_SIMD_REGISTERS, "Dump fpu or simd registers:"}, + {SnapshotSection::STACK_BACKTRACE, "Stack backtrace"}, + {SnapshotSection::ELF_LOAD_INFO, "Elf load info"}, + {SnapshotSection::DATA_ON_TARGET_OF_LAST_BL, "Data on target of last"}, + {SnapshotSection::DATA_AROUND_REGS, "Data around regs"}, + {SnapshotSection::CONTENT_OF_USER_STACK, "Contents of user stack"}, + {SnapshotSection::BASE_ACTV_DUMPED, "[base actv dumped]"}, + {SnapshotSection::TRANSACTION_END, "[transaction end] now mono_time"} +}; + +using CrashMap = std::unordered_map; + +void SaveSnapshot(CrashMap& output); + +class CrashKernelFrame { +public: + void Parse(const std::string& line) + { + size_t pos = 0; + pc = ExtractContent(line, pos, '[', ']'); + fp = ExtractContent(line, ++pos, '[', ']'); + funcNameOffset = ExtractContent(line, ++pos, '<', '>'); + elf = ExtractContent(line, ++pos, '(', ')'); + } + + std::string ToString(int count) const + { + std::string data = std::string("#") + (count < 10 ? "0" : "") + std::to_string(count); + data += " pc " + pc + " " + elf + " " + funcNameOffset; + return data; + } + +private: + std::string pc; + std::string fp; + std::string funcNameOffset; + std::string elf; + + static std::string ExtractContent(const std::string& line, size_t& pos, char startChar, char endChar) + { + size_t start = line.find(startChar, pos); + size_t end = line.find(endChar, start); + pos = end + 1; + if (start != std::string::npos && end != std::string::npos) { + return line.substr(start + 1, end - start - 1); + } + return ""; + } +}; + +void ReportCrashEvent(CrashMap& output) +{ + if (output[CrashSection::UID].empty()) { + DFXLOGE("uid is empty, not report"); + return; + } + + void* handle = dlopen("libfaultlogger.z.so", RTLD_LAZY | RTLD_NODELETE); + if (handle == nullptr) { + DFXLOGW("Failed to dlopen libfaultlogger, %{public}s\n", dlerror()); + return; + } + + auto addFaultLog = reinterpret_cast(dlsym(handle, "AddFaultLog")); + if (addFaultLog == nullptr) { + DFXLOGW("Failed to dlsym AddFaultLog, %{public}s\n", dlerror()); + dlclose(handle); + return; + } + + FaultDFXLOGIInner info; + const int base = 10; + info.time = strtol(output[CrashSection::TIME_STAMP].c_str(), nullptr, base); + info.id = static_cast(strtoul(output[CrashSection::UID].c_str(), nullptr, base)); + info.pid = static_cast(strtol(output[CrashSection::PID].c_str(), nullptr, base)); + info.faultLogType = 2; // 2 : CPP_CRASH_TYPE + info.module = output[CrashSection::PROCESS_NAME]; + info.reason = KERNEL_SNAPSHOT_REASON; + info.summary = output[CrashSection::FAULT_THREAD_INFO]; + addFaultLog(&info); + DFXLOGI("Finish report fault to FaultLogger (%{public}u,%{public}d)", info.id, info.pid); + dlclose(handle); +} + +std::string GetBuildInfo() +{ +#ifndef is_ohos_lite + static std::string buildInfo = OHOS::system::GetParameter("const.product.software.version", "Unknown"); + return buildInfo; +#else + return "Unknown"; +#endif +} + +bool IsDevMode() +{ +#ifndef is_ohos_lite + return OHOS::system::GetParameter("const.security.developermode.state", "") == "true"; +#else + return false; +#endif +} + +bool PreProcessLine(std::string& line) +{ + if (line.size() <= SEQUENCE_LEN || line[0] == '\t') { + return false; + } + // move timestamp to end + if (isdigit(line[1])) { + auto pos = line.find('[', 1); + if (pos != std::string::npos) { + std::string tmp = line.substr(0, pos); + line = line.substr(pos) + tmp; + } + } + return true; +} + +std::unordered_map ConvertThreadInfoToPairs(const std::string& line) +{ + std::unordered_map pairs; + size_t pos = 0; + while (pos < line.size()) { + while (pos < line.size() && line[pos] == ' ') { + pos++; + } + size_t keyStart = pos; + + while (pos < line.size() && line[pos] != '=') { + pos++; + } + if (pos >= line.size()) { + break; + } + std::string key = line.substr(keyStart, pos - keyStart); + + size_t valueStart = ++pos; + while (pos < line.size() && line[pos] != ',') { + pos++; + } + if (pos >= line.size()) { + break; + } + pairs[key] = line.substr(valueStart, pos - valueStart); + pos++; + } + return pairs; +} + +void ParseTransStart(const std::string& cont, CrashMap& output) +{ + if (cont.find("mono_time") == std::string::npos) { + return; + } + auto pos = cont.rfind("["); + if (pos != std::string::npos && pos + 1 < cont.length()) { + output[CrashSection::TIME_STAMP] = cont.substr(pos + 1, 10) + "000"; // 10 : timestamp length + } +} + +void ParseThreadInfo(const std::vector& lines, int start, int end, CrashMap& output) +{ + if (start + 1 > end) { + return; + } + std::string info = lines[start + 1]; + DFXLOGI("kenel snapshot thread info : %{public}s", info.c_str()); + auto pairs = ConvertThreadInfoToPairs(info); + output[CrashSection::PROCESS_NAME] = pairs["name"]; // native process use this + output[CrashSection::FAULT_THREAD_INFO] = "Tid:" + pairs["tid"] + ", Name: " + pairs["name"] + "\n"; + output[CrashSection::PID] = pairs["pid"]; + output[CrashSection::UID] = pairs["uid"]; +} + +void ParseStackBacktrace(const std::vector& lines, int start, int end, CrashMap& output) +{ + CrashKernelFrame frame; + for (int i = start + 1; i <= end; i++) { + frame.Parse(lines[i]); + output[CrashSection::FAULT_THREAD_INFO] += frame.ToString(i - start - 1) + "\n"; + } +} + +CrashSection GetSnapshotMapCrashItem(const SnapshotSection& item) +{ + switch (item) { + case SnapshotSection::DUMP_REGISTERS: + return CrashSection::REGISTERS; + case SnapshotSection::DATA_AROUND_REGS: + return CrashSection::MEMORY_NEAR_REGISTERS; + case SnapshotSection::CONTENT_OF_USER_STACK: + return CrashSection::FAULT_STACK; + case SnapshotSection::ELF_LOAD_INFO: + return CrashSection::MAPS; + case SnapshotSection::EXCEPTION_REGISTERS: + return CrashSection::EXCEPTION_REGISTERS; + default: + return CrashSection::INVALID_SECTION; + } +} + +void ParseDefaultAction(const std::vector& lines, int start, int end, + SnapshotSection key, CrashMap& output) +{ + auto it = GetSnapshotMapCrashItem(key); + if (it == CrashSection::INVALID_SECTION) { + return; + } + for (int i = start + 1; i <= end; i++) { + output[it] += lines[i] + "\n"; + } +} + +void ProcessSnapshotSection(SnapshotSection sectionKey, const std::vector& lines, + size_t start, size_t end, CrashMap& output) +{ + switch (sectionKey) { + case SnapshotSection::TRANSACTION_START: + ParseTransStart(lines[start], output); + break; + case SnapshotSection::THREAD_INFO: + ParseThreadInfo(lines, start, end, output); + break; + case SnapshotSection::STACK_BACKTRACE: + ParseStackBacktrace(lines, start, end, output); + break; + default: + ParseDefaultAction(lines, start, end, sectionKey, output); + break; + } +} + +bool ProcessTransStart(const std::vector& lines, size_t& index, + std::list>& keywordList, CrashMap& output) +{ + const auto& keyword = keywordList.front().second; + for (; index < lines.size(); index++) { + if (StartsWith(lines[index], keyword)) { + break; + } + } + + if (index == lines.size()) { + return false; + } + + ProcessSnapshotSection(SnapshotSection::TRANSACTION_START, lines, index, index, output); + index++; + keywordList.pop_front(); + return true; +} + +void ParseSnapshotUnit(const std::vector& lines, size_t& index) +{ + CrashMap output; + std::list> keywordList; + for (const auto& item : SNAPSHOT_SECTION_KEYWORDS) { + keywordList.emplace_back(item.type, item.key); + } + + if (!ProcessTransStart(lines, index, keywordList, output)) { + return; + } + + // process other snapshot sections + int snapshotSecIndex = -1; + SnapshotSection snapshotSecKey; + bool isTransEnd = false; + + for (; index < lines.size() && !isTransEnd; index++) { + for (auto it = keywordList.begin(); it != keywordList.end(); it++) { + if (!StartsWith(lines[index], it->second)) { + continue; + } + if (snapshotSecIndex == -1) { + snapshotSecIndex = index; + snapshotSecKey = it->first; + break; + } + ProcessSnapshotSection(snapshotSecKey, lines, snapshotSecIndex, index - 1, output); + snapshotSecIndex = index; + snapshotSecKey = it->first; + if (it->first == SnapshotSection::TRANSACTION_END) { + isTransEnd = true; + } + keywordList.erase(it); + break; + } + } + + SaveSnapshot(output); + ReportCrashEvent(output); +} + +void ParseSameSeqSnapshot(const std::vector& lines) +{ + size_t curLineNum = 0; + while (curLineNum < lines.size()) { + ParseSnapshotUnit(lines, curLineNum); + } +} + +void ParseSnapshot(std::vector& snapshotLines) +{ + std::unordered_map> kernelSnapshotMap; + // devide snapshot info by sequence number + for (auto &line : snapshotLines) { + if (!PreProcessLine(line)) { + continue; + } + + std::string seqNum = line.substr(0, SEQUENCE_LEN); + kernelSnapshotMap[seqNum].emplace_back(line.substr(SEQUENCE_LEN)); + } + + for (auto &item : kernelSnapshotMap) { + ParseSameSeqSnapshot(item.second); + } +} + +std::string FilterEmptySection(const std::string& secHead, const std::string& secCont, const std::string& end) +{ + if (secCont.empty()) { + return ""; + } + return secHead + secCont + end; +} + +void OutputToFile(const std::string& filePath, CrashMap& output) +{ + FILE* file = fopen(filePath.c_str(), "w"); + if (file == nullptr) { + DFXLOGE("open file failed %{public}s errno %{public}d", filePath.c_str(), errno); + return; + } + std::string outputCont; + outputCont += FilterEmptySection("Build info: ", GetBuildInfo(), "\n"); + outputCont += FilterEmptySection("Timestamp: ", output[CrashSection::TIME_STAMP], "\n"); + outputCont += FilterEmptySection("Pid: ", output[CrashSection::PID], "\n"); + outputCont += FilterEmptySection("Uid: ", output[CrashSection::UID], "\n"); + outputCont += FilterEmptySection("Reason: ", KERNEL_SNAPSHOT_REASON, "\n"); + outputCont += FilterEmptySection("Exception registers:\n", output[CrashSection::EXCEPTION_REGISTERS], ""); + outputCont += FilterEmptySection("Fault thread info:\n", output[CrashSection::FAULT_THREAD_INFO], ""); + outputCont += FilterEmptySection("Registers:\n", output[CrashSection::REGISTERS], ""); + outputCont += FilterEmptySection("Memory near registers:\n", output[CrashSection::MEMORY_NEAR_REGISTERS], ""); + outputCont += FilterEmptySection("FaultStack:\n", output[CrashSection::FAULT_STACK], ""); + outputCont += FilterEmptySection("Elfs:\n", output[CrashSection::MAPS], ""); + if (fwrite(outputCont.c_str(), sizeof(char), outputCont.length(), file) != outputCont.length()) { + DFXLOGE("write file failed %{public}s errno %{public}d", filePath.c_str(), errno); + } + if (fclose(file) != 0) { + DFXLOGE("close file failed %{public}s errno %{public}d", filePath.c_str(), errno); + } +} + +void SaveSnapshot(CrashMap& output) +{ + if (output[CrashSection::PID].empty()) { + DFXLOGE("pid is empty, not save snapshot"); + return; + } + + std::string filePath = std::string(KBOX_SNAPSHOT_DUMP_PATH) + "cppcrash-" + + output[CrashSection::PID] + "-" + + output[CrashSection::TIME_STAMP]; + OutputToFile(filePath, output); +} + +int GetSnapshotCheckInterval() +{ +#ifndef is_ohos_lite + std::string interval = OHOS::system::GetParameter(KERNEL_SNAPSHOT_CHECK_INTERVAL, DEFAULT_CHECK_INTERVAL); +#else + std::string interval = DEFAULT_CHECK_INTERVAL; +#endif + int value = static_cast(strtol(interval.c_str(), nullptr, 10)); // 10 : decimal + if (errno == ERANGE) { + DFXLOGE("get snapshot check interval failed, use default interval"); + value = 60; // 60 : default interval + } + value = value < MIN_CHECK_INTERVAL ? MIN_CHECK_INTERVAL : value; + + DFXLOGI("monitor crash kernel snapshot interval %{public}d", value); + return value; +} + +void SplitByNewLine(const std::string& str, std::vector& lines) +{ + size_t start = 0; + while (start < str.size()) { + size_t end = str.find('\n', start); + if (end == std::string::npos) { + end = str.size(); + } + lines.emplace_back(str.substr(start, end - start)); + start = end + 1; + } +} + +void MonitorCrashKernelSnapshot() +{ + DFXLOGI("enter %{public}s ", __func__); + pthread_setname_np(pthread_self(), "KernelSnapshot"); + int interval = GetSnapshotCheckInterval(); + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(interval)); + if (access(KERNEL_KBOX_SNAPSHOT, F_OK) < 0) { + DFXLOGE("can't find %{public}s, just exit", KERNEL_KBOX_SNAPSHOT); + return; + } + int snapshotFd = open(KERNEL_KBOX_SNAPSHOT, O_RDONLY); + if (snapshotFd < 0) { + DFXLOGE("open snapshot filed %{public}d", errno); + continue; + } + + char buffer[BUFFER_LEN] = {0}; + std::vector snapshotLines; + std::string snapshotCont; + + int ret = read(snapshotFd, buffer, BUFFER_LEN - 1); + while (ret > 0) { + snapshotCont += buffer; + ret = read(snapshotFd, buffer, BUFFER_LEN - 1); + } + close(snapshotFd); + + SplitByNewLine(snapshotCont, snapshotLines); + ParseSnapshot(snapshotLines); + } +} +} // namespace + +void FaultKernelSnapshot::StartMonitor() +{ + DFXLOGI("monitor kernel crash snapshot start!"); + if (!IsDevMode()) { + DFXLOGW("monitor kernel crash snapshot func not support"); + return; + } + std::thread catchThread = std::thread([] { MonitorCrashKernelSnapshot(); }); + catchThread.detach(); +} +} // namespace HiviewDFX +} // namespace OHOS diff --git a/services/fault_kernel_snapshot.h b/services/fault_kernel_snapshot.h new file mode 100644 index 000000000..2b0ebd01b --- /dev/null +++ b/services/fault_kernel_snapshot.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 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. + */ + +#ifndef FAULT_KERNEL_SNAPSHOT_H +#define FAULT_KERNEL_SNAPSHOT_H + +namespace OHOS { +namespace HiviewDFX { +class FaultKernelSnapshot { +public: + void StartMonitor(); +}; +} // namespace HiviewDFX +} // namespace OHOS + +#endif diff --git a/services/main.cpp b/services/main.cpp index d5fbc39b9..fd0c17e70 100644 --- a/services/main.cpp +++ b/services/main.cpp @@ -19,6 +19,7 @@ #include "dfx_dump_request.h" #include "dfx_signal_local_handler.h" #include "dfx_util.h" +#include "fault_kernel_snapshot.h" #include "temp_file_manager.h" static int DoGetCrashFd(const struct ProcessDumpRequest* request) @@ -37,6 +38,8 @@ int main(int argc, char *argv[]) DFX_GetCrashFdFunc(DoGetCrashFd); DFX_InstallLocalSignalHandler(); #endif + OHOS::HiviewDFX::FaultKernelSnapshot snapshot; + snapshot.StartMonitor(); auto& faultLoggerDaemon = OHOS::HiviewDFX::FaultLoggerDaemon::GetInstance(); faultLoggerDaemon.StartServer(); return 0; diff --git a/tools/process_dump/process_dumper.cpp b/tools/process_dump/process_dumper.cpp index ec0ac257e..ad5cd58bc 100644 --- a/tools/process_dump/process_dumper.cpp +++ b/tools/process_dump/process_dumper.cpp @@ -261,7 +261,7 @@ void ReadVmRealPid(std::shared_ptr request, unsigned long vm long data = 0; DFXLOGI("start wait exit event happen"); waitpid(vmPid, &waitStatus, 0); // wait exit stop - data = ptrace(PTRACE_PEEKDATA, vmPid, reinterpret_cast(request->vmProcRealPid), NULL); + data = ptrace(PTRACE_PEEKDATA, vmPid, reinterpret_cast(request->vmProcRealPidAddr), nullptr); if (data < 0) { DFXLOGI("ptrace peek data error %{public}lu %{public}d", vmPid, errno); } @@ -371,6 +371,34 @@ static bool IsDebugSignal(const siginfo_t& siginfo) return false; } } + +void InfoCrashUnwindResult(const std::shared_ptr request, bool isUnwindSucc) +{ + if (!isUnwindSucc) { + return; + } + if (ptrace(PTRACE_POKEDATA, request->nsPid, reinterpret_cast(request->unwindResultAddr), + CRASH_UNWIND_SUCCESS_FLAG) < 0) { + DFXLOGE("pok unwind success flag to nsPid %{public}d fail %{public}s", request->nsPid, strerror(errno)); + } +} + +void BlockCrashProcExit(const std::shared_ptr request) +{ + if (!IsBlockCrashProcess()) { + return; + } + DFXLOGI("start block crash process pid %{public}d nspid %{public}d", request->pid, request->nsPid); + if (ptrace(PTRACE_POKEDATA, request->nsPid, reinterpret_cast(request->blockCrashExitAddr), + CRASH_BLOCK_EXIT_FLAG) < 0) { + DFXLOGE("pok block falg to nsPid %{public}d fail %{public}s", request->nsPid, strerror(errno)); + } +} + +bool IsDumpSignal(int signo) +{ + return signo == SIGDUMP || signo == SIGLEAK_STACK; +} } ProcessDumper &ProcessDumper::GetInstance() @@ -390,11 +418,9 @@ void ProcessDumper::Dump() if (isCrash_ && process_->vmThread_ != nullptr) { process_->vmThread_->Detach(); } - if (isCrash_ && (request->dumpMode == FUSION_MODE) && IsBlockCrashProcess()) { - DFXLOGI("start block crash process pid %{public}d nspid %{public}d", request->pid, request->nsPid); - if (ptrace(PTRACE_POKEDATA, request->nsPid, (void*)request->isBlockCrash, CRASH_BLOCK_EXIT_FLAG) < 0) { - DFXLOGE("pok block falg to nsPid %{public}d fail %{public}s", request->nsPid, strerror(errno)); - } + if (!IsDumpSignal(request->siginfo.si_signo) && (request->dumpMode == FUSION_MODE)) { + InfoCrashUnwindResult(request, resDump_ == DumpErrorCode::DUMP_ESUCCESS); + BlockCrashProcExit(request); } if (process_->keyThread_ != nullptr) { process_->keyThread_->Detach(); -- Gitee