1/*
2 * Copyright (c) 2021-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 "cppcrash_reporter.h"
17
18#include <cinttypes>
19#include <dlfcn.h>
20#include <fcntl.h>
21#include <map>
22#include <string>
23#include "dfx_define.h"
24#include "dfx_logger.h"
25#include "dfx_process.h"
26#include "dfx_signal.h"
27#include "dfx_thread.h"
28#include "faultlogger_client_msg.h"
29#ifndef HISYSEVENT_DISABLE
30#include "hisysevent.h"
31#endif
32
33static const char FOUNDATION_PROCESS_NAME[] = "foundation";
34static const char HIVIEW_PROCESS_NAME[] = "/system/bin/hiview";
35static const char REGS_KEY_WORD[] = "Registers:\n";
36#ifndef HISYSEVENT_DISABLE
37static const char KILL_REASON_CPP_CRASH[] = "Kill Reason:Cpp Crash";
38#endif
39
40using RecordAppExitReason = int (*)(int reason, const char *exitMsg);
41
42namespace OHOS {
43namespace HiviewDFX {
44
45bool CppCrashReporter::Format()
46{
47    if (process_ == nullptr) {
48        return false;
49    }
50
51    cmdline_ = process_->processInfo_.processName;
52    pid_ = process_->processInfo_.pid;
53    uid_ = process_->processInfo_.uid;
54    reason_ = process_->reason;
55    auto msg = process_->GetFatalMessage();
56    if (!msg.empty()) {
57        stack_ = "LastFatalMessage:" + msg + "\n";
58    }
59    std::shared_ptr<DfxThread> thread = dumpMode_ == FUSION_MODE ? process_->keyThread_ : process_->vmThread_;
60    if (thread != nullptr) {
61        std::string threadInfo = thread->ToString();
62        auto iterator = threadInfo.begin();
63        while (iterator != threadInfo.end() && *iterator != '\n') {
64            if (isdigit(*iterator)) {
65                iterator = threadInfo.erase(iterator);
66            } else {
67                iterator++;
68            }
69        }
70        stack_ += threadInfo;
71
72        // regs
73        registers_ = GetRegsString(process_->regs_);
74    }
75    return true;
76}
77
78void CppCrashReporter::ReportToHiview()
79{
80    if (!Format()) {
81        DFXLOGW("Failed to format crash report.");
82        return;
83    }
84    if (process_->processInfo_.processName.find(HIVIEW_PROCESS_NAME) != std::string::npos) {
85        DFXLOGW("Failed to report, hiview is crashed.");
86        return;
87    }
88
89    void* handle = dlopen("libfaultlogger.z.so", RTLD_LAZY | RTLD_NODELETE);
90    if (handle == nullptr) {
91        DFXLOGW("Failed to dlopen libfaultlogger, %{public}s\n", dlerror());
92        return;
93    }
94
95    auto addFaultLog = reinterpret_cast<void (*)(FaultDFXLOGIInner*)>(dlsym(handle, "AddFaultLog"));
96    if (addFaultLog == nullptr) {
97        DFXLOGW("Failed to dlsym AddFaultLog, %{public}s\n", dlerror());
98        dlclose(handle);
99        return;
100    }
101
102    FaultDFXLOGIInner info;
103    info.time = time_;
104    info.id = uid_;
105    info.pid = pid_;
106    info.pipeFd = WriteCppCrashInfoByPipe();
107    info.faultLogType = 2; // 2 : CPP_CRASH_TYPE
108    info.module = cmdline_;
109    info.reason = reason_;
110    info.summary = stack_;
111    info.registers = registers_;
112    addFaultLog(&info);
113    DFXLOGI("Finish report fault to FaultLogger %{public}s(%{public}d,%{public}d)", cmdline_.c_str(), pid_, uid_);
114    dlclose(handle);
115}
116
117// read fd will be closed after transfering to hiview
118int32_t CppCrashReporter::WriteCppCrashInfoByPipe()
119{
120    size_t sz = cppCrashInfo_.size();
121    if (sz > MAX_PIPE_SIZE) {
122        DFXLOGE("the size of json string is greater than max pipe size, do not report");
123        return -1;
124    }
125    int pipeFd[2] = {-1, -1};
126    if (pipe(pipeFd) != 0) {
127        DFXLOGE("Failed to create pipe.");
128        return -1;
129    }
130    if (fcntl(pipeFd[PIPE_READ], F_SETPIPE_SZ, sz) < 0 ||
131        fcntl(pipeFd[PIPE_WRITE], F_SETPIPE_SZ, sz) < 0) {
132        DFXLOGE("[%{public}d]: failed to set pipe size.", __LINE__);
133        return -1;
134    }
135    if (fcntl(pipeFd[PIPE_READ], F_GETFL) < 0) {
136        DFXLOGE("[%{public}d]: failed to set pipe size.", __LINE__);
137        return -1;
138    } else {
139        uint32_t flags = static_cast<uint32_t>(fcntl(pipeFd[PIPE_READ], F_GETFL));
140        flags |= O_NONBLOCK;
141        if (fcntl(pipeFd[PIPE_READ], F_SETFL, flags) < 0) {
142            DFXLOGE("Failed to set pipe flag.");
143            return -1;
144        }
145    }
146    ssize_t realWriteSize = -1;
147    realWriteSize = OHOS_TEMP_FAILURE_RETRY(write(pipeFd[PIPE_WRITE], cppCrashInfo_.c_str(), sz));
148    close(pipeFd[PIPE_WRITE]);
149    if (static_cast<ssize_t>(cppCrashInfo_.size()) != realWriteSize) {
150        DFXLOGE("Failed to write pipe. realWriteSize %{public}zd, json size %{public}zd", realWriteSize, sz);
151        close(pipeFd[PIPE_READ]);
152        return -1;
153    }
154    return pipeFd[PIPE_READ];
155}
156
157void CppCrashReporter::ReportToAbilityManagerService()
158{
159    if (process_->processInfo_.processName.find(FOUNDATION_PROCESS_NAME) != std::string::npos) {
160        DFXLOGW("Do not to report to AbilityManagerService, foundation is crashed.");
161        return;
162    }
163
164    void* handle = dlopen("libability_manager_c.z.so", RTLD_LAZY | RTLD_NODELETE);
165    if (handle == nullptr) {
166        DFXLOGW("Failed to dlopen libabilityms, %{public}s\n", dlerror());
167        return;
168    }
169
170    RecordAppExitReason recordAppExitReason = (RecordAppExitReason)dlsym(handle, "RecordAppExitReason");
171    if (recordAppExitReason == nullptr) {
172        DFXLOGW("Failed to dlsym RecordAppExitReason, %{public}s\n", dlerror());
173        dlclose(handle);
174        return;
175    }
176
177    // defined in interfaces/inner_api/ability_manager/include/ability_state.h
178    const int cppCrashExitReason = 2;
179    recordAppExitReason(cppCrashExitReason, reason_.c_str());
180    dlclose(handle);
181#ifndef HISYSEVENT_DISABLE
182    int result = HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, "PROCESS_KILL", HiSysEvent::EventType::FAULT,
183        "PID", pid_, "PROCESS_NAME", cmdline_.c_str(), "MSG", KILL_REASON_CPP_CRASH);
184    DFXLOGW("hisysevent write result=%{public}d, send event [FRAMEWORK,PROCESS_KILL], pid=%{public}d,"
185        " processName=%{public}s, msg=%{public}s", result, pid_, cmdline_.c_str(), KILL_REASON_CPP_CRASH);
186#endif
187}
188
189std::string CppCrashReporter::GetRegsString(std::shared_ptr<DfxRegs> regs)
190{
191    std::string regsString = "";
192    if (regs == nullptr) {
193        return regsString;
194    }
195    regsString = regs->PrintRegs();
196    // if start with 'Registers:\n', need remove
197    if (regsString.find(REGS_KEY_WORD) == 0) {
198        regsString = regsString.substr(strlen(REGS_KEY_WORD));
199    }
200    return regsString;
201}
202} // namespace HiviewDFX
203} // namespace OHOS
204