1/*
2 * Copyright (c) 2022-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#include "dfx_crash_local_handler.h"
16
17#include <securec.h>
18#include <csignal>
19#include <sys/time.h>
20#include <sys/ucontext.h>
21#include <cinttypes>
22#include <unistd.h>
23#include <pthread.h>
24#include <cerrno>
25#include "dfx_log.h"
26#include "dfx_cutil.h"
27#include "dfx_signalhandler_exception.h"
28#include "faultloggerd_client.h"
29#include "unwinder.h"
30
31#ifdef LOG_DOMAIN
32#undef LOG_DOMAIN
33#define LOG_DOMAIN 0xD002D11
34#endif
35
36#ifdef LOG_TAG
37#undef LOG_TAG
38#define LOG_TAG "DfxCrashLocalHandler"
39#endif
40
41#define MAX_FRAME 64
42#define BUF_SZ 512
43#define MAPINFO_SIZE 256
44#define TIME_DIV 1000
45#define BUF_SZ_SMALL 256
46
47static __attribute__((noinline)) int RequestOutputLogFile(const struct ProcessDumpRequest* request)
48{
49    struct FaultLoggerdRequest faultloggerdRequest;
50    (void)memset_s(&faultloggerdRequest, sizeof(faultloggerdRequest), 0, sizeof(struct FaultLoggerdRequest));
51
52    faultloggerdRequest.type = (int32_t)CPP_CRASH;
53    faultloggerdRequest.pid = request->pid;
54    faultloggerdRequest.tid = request->tid;
55    faultloggerdRequest.uid = request->uid;
56    faultloggerdRequest.time = request->timeStamp + 1;
57    return RequestFileDescriptorEx(&faultloggerdRequest);
58}
59
60static __attribute__((noinline)) void PrintLog(int fd, const char *format, ...)
61{
62    char buf[BUF_SZ] = {0};
63    va_list args;
64    va_start(args, format);
65    int size = vsnprintf_s(buf, sizeof(buf), sizeof(buf) - 1, format, args);
66    va_end(args);
67    if (size == -1) {
68        if (fd > 0) {
69            const char* error = "PrintLog vsnprintf_s fail\n";
70            (void)OHOS_TEMP_FAILURE_RETRY(write(fd, error, strlen(error)));
71        }
72        return;
73    }
74    DFXLOGE("%{public}s", buf);
75    if (fd > 0) {
76        (void)OHOS_TEMP_FAILURE_RETRY(write(fd, buf, strlen(buf)));
77    }
78}
79
80__attribute__((noinline)) void CrashLocalUnwind(const int fd, const ucontext_t* uc)
81{
82    if (uc == nullptr) {
83        return;
84    }
85    OHOS::HiviewDFX::Unwinder unwind;
86    unwind.UnwindLocalWithContext(*uc);
87    auto regs = OHOS::HiviewDFX::DfxRegs::CreateFromUcontext(*uc);
88    std::string logContext = unwind.GetFramesStr(unwind.GetFrames()) + regs->PrintRegs();
89    logContext.append("\nMaps:\n");
90    for (const auto &map : unwind.GetMaps()->GetMaps()) {
91        logContext.append(map->ToString());
92    }
93
94    for (unsigned int i = 0; i < logContext.length(); i += BUF_SZ_SMALL) {
95        PrintLog(fd, "%s", logContext.substr(i, BUF_SZ_SMALL).c_str());
96    }
97}
98
99// currently, only stacktrace is logged to faultloggerd
100void CrashLocalHandler(const struct ProcessDumpRequest* request)
101{
102    int fd = RequestOutputLogFile(request);
103    CrashLocalHandlerFd(fd, request);
104    if (fd >= 0) {
105        close(fd);
106    }
107}
108
109static void PrintTimeStamp(const int fd)
110{
111    uint64_t currentTime = GetTimeMilliseconds();
112    char secBuf[BUF_SZ] = {0};
113    char printBuf[BUF_SZ] = {0};
114    time_t sec = static_cast<time_t>(currentTime / TIME_DIV);
115    uint64_t millisec = currentTime % TIME_DIV;
116    struct tm* t = localtime(&sec);
117    if (!t) {
118        return;
119    }
120    (void)strftime(secBuf, sizeof(secBuf) - 1, "%Y-%m-%d %H:%M:%S", t);
121    if (snprintf_s(printBuf, sizeof(printBuf), sizeof(printBuf) - 1,
122            "%s.%03" PRIx64 "\n", secBuf, millisec) < 0) {
123        DFXLOGE("snprintf timestamp fail");
124        return;
125    }
126    PrintLog(fd, "Timestamp:%s", printBuf);
127}
128
129static void ReportToHiview(const char* logPath, const struct ProcessDumpRequest* request)
130{
131    struct CrashDumpException exception;
132    (void)memset_s(&exception, sizeof(struct CrashDumpException), 0, sizeof(struct CrashDumpException));
133    exception.pid = request->pid;
134    exception.uid = request->uid;
135    exception.error = CRASH_DUMP_LOCAL_REPORT;
136    exception.time = static_cast<int64_t>(GetTimeMilliseconds());
137    if (strncpy_s(exception.message, sizeof(exception.message) - 1, logPath, strlen(logPath)) != 0) {
138        DFXLOGE("strcpy exception msg fail");
139        return;
140    }
141    ReportException(exception);
142}
143
144void CrashLocalHandlerFd(const int fd, const struct ProcessDumpRequest* request)
145{
146    if (request == nullptr) {
147        return;
148    }
149    PrintTimeStamp(fd);
150    PrintLog(fd, "Pid:%d\n", request->pid);
151    PrintLog(fd, "Uid:%d\n", request->uid);
152    PrintLog(fd, "Process name:%s\n", request->processName);
153#if defined(__LP64__)
154    PrintLog(fd, "Reason:Signal(%d)@%018p\n", request->siginfo.si_signo, request->siginfo.si_addr);
155#else
156    PrintLog(fd, "Reason:Signal(%d)@%010p\n", request->siginfo.si_signo, request->siginfo.si_addr);
157#endif
158    PrintLog(fd, "Fault thread info:\n");
159    PrintLog(fd, "Tid:%d, Name:%s\n", request->tid, request->threadName);
160    CrashLocalUnwind(fd, &(request->context));
161    char logFileName[BUF_SZ] = {0};
162    if (snprintf_s(logFileName, sizeof(logFileName), sizeof(logFileName) - 1,
163        "/data/log/faultlog/temp/cppcrash-%d-%llu", request->pid, request->timeStamp + 1) < 0) {
164        DFXLOGE("snprintf logFileName fail");
165        return;
166    }
167    ReportToHiview(logFileName, request);
168}
169