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