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 "crash_validator.h"
17
18 #include <csignal>
19 #include <cstdio>
20 #include <memory>
21 #include <set>
22
23 #include "faultlogger_client.h"
24 #include "hisysevent.h"
25 #include "hiview_logger.h"
26 #include "plugin_factory.h"
27 #include "string_util.h"
28 #include "time_util.h"
29
30 namespace OHOS {
31 namespace HiviewDFX {
32 static const int CHECK_TIME = 75; // match proceesdump report and kernel snapshot check interval(60s)
33 static const int CRASH_DUMP_LOCAL_REPORT = 206;
34 constexpr int MAX_CRASH_EVENT_SIZE = 100;
35 constexpr int MIN_APP_UID = 20000;
36
37 REGISTER(CrashValidator);
38 DEFINE_LOG_LABEL(0xD002D11, "HiView-CrashValidator");
CrashValidator()39 CrashValidator::CrashValidator() : hasLoaded_(false)
40 {
41 }
42
~CrashValidator()43 CrashValidator::~CrashValidator() {}
44
OnLoad()45 void CrashValidator::OnLoad()
46 {
47 if (GetHiviewContext() == nullptr) {
48 HIVIEW_LOGE("hiview context is null");
49 return;
50 }
51 InitWorkLoop();
52 GetHiviewContext()->AppendPluginToPipeline("CrashValidator", "faultloggerPipeline");
53 HIVIEW_LOGI("crash validator load");
54 hasLoaded_ = true;
55 }
56
OnUnload()57 void CrashValidator::OnUnload()
58 {
59 HIVIEW_LOGI("crash validator unload");
60 }
61
IsInterestedPipelineEvent(std::shared_ptr<Event> event)62 bool CrashValidator::IsInterestedPipelineEvent(std::shared_ptr<Event> event)
63 {
64 if (!hasLoaded_ || event == nullptr) {
65 return false;
66 }
67
68 if (event->eventName_ != "PROCESS_EXIT" &&
69 event->eventName_ != "CPP_CRASH" &&
70 event->eventName_ != "CPP_CRASH_EXCEPTION") {
71 return false;
72 }
73
74 return true;
75 }
76
Convert2SysEvent(std::shared_ptr<Event> & event)77 std::shared_ptr<SysEvent> CrashValidator::Convert2SysEvent(std::shared_ptr<Event>& event)
78 {
79 if (event == nullptr) {
80 HIVIEW_LOGE("event is null");
81 return nullptr;
82 }
83 if (event->messageType_ != Event::MessageType::SYS_EVENT) {
84 HIVIEW_LOGE("receive out of sys event type");
85 return nullptr;
86 }
87 std::shared_ptr<SysEvent> sysEvent = Event::DownCastTo<SysEvent>(event);
88 if (sysEvent == nullptr) {
89 HIVIEW_LOGE("sysevent is null");
90 }
91 return sysEvent;
92 }
93
94 /* use hiview shared workloop as our workloop */
InitWorkLoop()95 void CrashValidator::InitWorkLoop()
96 {
97 workLoop_ = GetHiviewContext()->GetSharedWorkLoop();
98 }
99
100 /* remove event in map. if empty, clear crash and crash exception maps */
RemoveMatchEvent(int32_t pid)101 void CrashValidator::RemoveMatchEvent(int32_t pid)
102 {
103 cppCrashEvents_.erase(pid);
104 cppCrashExceptionEvents_.erase(pid);
105 processExitEvents_.erase(pid);
106 if (processExitEvents_.empty()) {
107 HIVIEW_LOGI("processe events empty");
108 cppCrashEvents_.clear();
109 cppCrashExceptionEvents_.clear();
110 }
111 }
112
MatchKernelSnapshotEvent(int32_t pid)113 bool CrashValidator::MatchKernelSnapshotEvent(int32_t pid)
114 {
115 if (cppCrashEvents_.find(pid) == cppCrashEvents_.end() ||
116 cppCrashEvents_[pid]->GetEventValue("REASON") != "CppCrashKernelSnapshot") {
117 return false;
118 }
119 ReportMatchEvent("CPP_CRASH_MATCHED", cppCrashEvents_[pid]);
120 RemoveMatchEvent(pid);
121 return true;
122 }
123
124 /* only process exit with status !=0 will trigger this func be called */
MatchEvent(int32_t pid)125 bool CrashValidator::MatchEvent(int32_t pid)
126 {
127 std::lock_guard<std::mutex> lock(mutex_);
128
129 if (processExitEvents_.empty()) {
130 return false;
131 }
132
133 if (processExitEvents_.find(pid) == processExitEvents_.end()) {
134 HIVIEW_LOGE("process(pid = %d) does not in process exit map", pid);
135 return false;
136 }
137
138 if (MatchKernelSnapshotEvent(pid)) {
139 return true;
140 }
141 if (cppCrashExceptionEvents_.find(pid) != cppCrashExceptionEvents_.end()) {
142 ReportMatchEvent("CPP_CRASH_EXCEPTION_MATCHED", cppCrashExceptionEvents_[pid]);
143 } else if (cppCrashEvents_.find(pid) != cppCrashEvents_.end()) {
144 ReportMatchEvent("CPP_CRASH_MATCHED", cppCrashEvents_[pid]);
145 } else {
146 ReportDisMatchEvent(processExitEvents_[pid]);
147 }
148 RemoveMatchEvent(pid);
149 return true;
150 }
151
152 /* process exit、 crash exception、 crash events insert into each map */
AddEventToMap(int32_t pid,std::shared_ptr<SysEvent> sysEvent)153 void CrashValidator::AddEventToMap(int32_t pid, std::shared_ptr<SysEvent> sysEvent)
154 {
155 int64_t happendTime = sysEvent->GetEventIntValue("time_");
156 std::lock_guard<std::mutex> lock(mutex_);
157
158 if ((sysEvent->eventName_ == "PROCESS_EXIT")) {
159 if (processExitEvents_.size() > MAX_CRASH_EVENT_SIZE) {
160 HIVIEW_LOGE("failed to add %{public}d to processExitEvents_", pid);
161 return;
162 }
163 processExitEvents_.try_emplace(pid, sysEvent);
164 } else if (sysEvent->eventName_ == "CPP_CRASH") {
165 if (cppCrashEvents_.size() > MAX_CRASH_EVENT_SIZE) {
166 HIVIEW_LOGE("failed to add %{public}d to cppCrashEvents_", pid);
167 return;
168 }
169 if ((cppCrashEvents_.find(pid) == cppCrashEvents_.end()) ||
170 (cppCrashEvents_[pid]->GetEventIntValue("time_") - happendTime > 0)) {
171 cppCrashEvents_[pid] = sysEvent;
172 }
173 } else {
174 if (cppCrashExceptionEvents_.size() > MAX_CRASH_EVENT_SIZE) {
175 HIVIEW_LOGE("failed to add %{public}d to cppCrashExceptionEvents_", pid);
176 return;
177 }
178 if ((cppCrashExceptionEvents_.find(pid) == cppCrashExceptionEvents_.end()) ||
179 (cppCrashExceptionEvents_[pid]->GetEventIntValue("time_") - happendTime > 0)) {
180 cppCrashExceptionEvents_[pid] = sysEvent;
181 }
182 }
183 }
184
IsNormalExitEvent(std::shared_ptr<SysEvent> sysEvent)185 static bool IsNormalExitEvent(std::shared_ptr<SysEvent> sysEvent)
186 {
187 std::set<int32_t> crashSet = { SIGILL, SIGABRT, SIGBUS, SIGFPE,
188 SIGSEGV, SIGSTKFLT, SIGSYS, SIGTRAP };
189 int32_t status = sysEvent->GetEventIntValue("STATUS");
190 int32_t exitSigno = WTERMSIG(status);
191 if (crashSet.count(exitSigno)) {
192 return false;
193 }
194
195 return true;
196 }
197
OnEvent(std::shared_ptr<Event> & event)198 bool CrashValidator::OnEvent(std::shared_ptr<Event>& event)
199 {
200 if (!hasLoaded_ || event == nullptr) {
201 HIVIEW_LOGE("crash validator not ready");
202 return false;
203 }
204 if (event->rawData_ == nullptr) {
205 return false;
206 }
207
208 std::shared_ptr<SysEvent> sysEvent = Convert2SysEvent(event);
209 if (sysEvent == nullptr) {
210 return false;
211 }
212
213 if ((sysEvent->eventName_ == "PROCESS_EXIT") && IsNormalExitEvent(sysEvent)) {
214 return true;
215 }
216
217 int32_t pid = sysEvent->GetEventIntValue("PID");
218 if (sysEvent->eventName_ == "CPP_CRASH_EXCEPTION" &&
219 sysEvent->GetEventIntValue("ERROR_CODE") == CRASH_DUMP_LOCAL_REPORT) {
220 FaultLogInfoInner info;
221 info.time = sysEvent->GetEventUintValue("HAPPEN_TIME");
222 info.id = sysEvent->GetEventUintValue("UID");
223 info.pid = pid;
224 info.faultLogType = CPP_CRASH;
225 info.module = StringUtil::UnescapeJsonStringValue(sysEvent->GetEventValue("PROCESS_NAME"));
226 auto msg = StringUtil::UnescapeJsonStringValue(sysEvent->GetEventValue("ERROR_MSG"));
227 auto pos = msg.find_first_of("\n");
228 if (pos < msg.size() - 1) {
229 info.reason = msg.substr(0, pos);
230 msg = msg.substr(pos + 1);
231 }
232 info.summary = msg;
233 AddFaultLog(info);
234 return true; // report local crash to hiview through it, do not validate CRASH_DUMP_LOCAL_REPORT
235 }
236 if (sysEvent->GetEventIntValue("UID") < MIN_APP_UID) {
237 return true;
238 };
239 AddEventToMap(pid, sysEvent);
240 if (sysEvent->eventName_ == "PROCESS_EXIT") {
241 workLoop_->AddTimerEvent(nullptr, nullptr, [this, pid] {
242 MatchEvent(pid);
243 }, CHECK_TIME, false);
244 int32_t status = sysEvent->GetEventIntValue("STATUS");
245 int32_t exitSigno = WTERMSIG(status);
246 HIVIEW_LOGI("Add MatchEvent task, process pid = %{public}d, name = %{public}s, exitSigno = %{public}d",
247 pid, sysEvent->GetEventValue("PROCESS_NAME").c_str(), exitSigno);
248 }
249
250 return true;
251 }
252
ReportMatchEvent(std::string eventName,std::shared_ptr<SysEvent> sysEvent)253 void CrashValidator::ReportMatchEvent(std::string eventName, std::shared_ptr<SysEvent> sysEvent)
254 {
255 std::string summary;
256 std::string processName;
257
258 if (sysEvent == nullptr) {
259 HIVIEW_LOGE("report match sysEvent is null");
260 return;
261 }
262
263 if (eventName == "CPP_CRASH_MATCHED") {
264 summary = sysEvent->GetEventValue("SUMMARY");
265 processName = sysEvent->GetEventValue("MODULE");
266 } else if (eventName == "CPP_CRASH_EXCEPTION_MATCHED") {
267 summary = sysEvent->GetEventValue("ERROR_MSG");
268 processName = sysEvent->GetEventValue("PROCESS_NAME");
269 }
270
271 HiSysEventWrite(
272 HiSysEvent::Domain::RELIABILITY,
273 eventName,
274 HiSysEvent::EventType::FAULT,
275 "PROCESS_NAME", processName,
276 "PID", sysEvent->GetEventIntValue("PID"),
277 "UID", sysEvent->GetEventIntValue("UID"),
278 "HAPPEN_TIME", sysEvent->GetEventIntValue("HAPPEN_TIME"),
279 "SUMMARY", summary);
280 }
281
ReportDisMatchEvent(std::shared_ptr<SysEvent> sysEvent)282 void CrashValidator::ReportDisMatchEvent(std::shared_ptr<SysEvent> sysEvent)
283 {
284 if (sysEvent == nullptr) {
285 HIVIEW_LOGE("report dismatch sysEvent is null");
286 return;
287 }
288
289 HiSysEventWrite(
290 HiSysEvent::Domain::RELIABILITY,
291 "CPP_CRASH_DISMATCH",
292 HiSysEvent::EventType::FAULT,
293 "PROCESS_NAME", sysEvent->GetEventValue("PROCESS_NAME"),
294 "PID", sysEvent->GetEventIntValue("PID"),
295 "UID", sysEvent->GetEventIntValue("UID"));
296 }
297
298 }
299 }
300