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