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