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 
33 static const char FOUNDATION_PROCESS_NAME[] = "foundation";
34 static const char HIVIEW_PROCESS_NAME[] = "/system/bin/hiview";
35 static const char REGS_KEY_WORD[] = "Registers:\n";
36 #ifndef HISYSEVENT_DISABLE
37 static const char KILL_REASON_CPP_CRASH[] = "Kill Reason:Cpp Crash";
38 #endif
39 
40 using RecordAppExitReason = int (*)(int reason, const char *exitMsg);
41 
42 namespace OHOS {
43 namespace HiviewDFX {
44 
Format()45 bool 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 
ReportToHiview()78 void 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
WriteCppCrashInfoByPipe()118 int32_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 
ReportToAbilityManagerService()157 void 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 
GetRegsString(std::shared_ptr<DfxRegs> regs)189 std::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