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
28namespace OHOS {
29namespace HiviewDFX {
30static const int CHECK_TIME = 15;
31
32REGISTER(CrashValidator);
33DEFINE_LOG_LABEL(0xD002D11, "HiView-CrashValidator");
34CrashValidator::CrashValidator() : hasLoaded_(false)
35{
36}
37
38CrashValidator::~CrashValidator() {}
39
40void 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
52void CrashValidator::OnUnload()
53{
54    HIVIEW_LOGI("crash validator unload");
55}
56
57bool 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
72std::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 */
90void 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 */
96bool 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 */
109bool 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 */
137void 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
157static 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
170bool 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
204void 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
233void 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