1 /*
2  * Copyright (c) 2023 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 #include "crash_validator.h"
16 
17 #include <cinttypes>
18 #include <csignal>
19 #include <iostream>
20 
21 #include <fcntl.h>
22 #include <hisysevent.h>
23 #include <securec.h>
24 #include <unistd.h>
25 
26 #include "hisysevent_manager.h"
27 
28 namespace OHOS {
29 namespace HiviewDFX {
30 namespace {
31 constexpr char EVENT_CPP_CRASH[] = "CPP_CRASH";
32 constexpr char KEY_PROCESS_EXIT[] = "PROCESS_EXIT";
33 constexpr char KEY_NAME[] = "PROCESS_NAME";
34 constexpr char KEY_PID[] = "PID";
35 constexpr char KEY_UID[] = "UID";
36 constexpr char KEY_STATUS[] = "STATUS";
37 constexpr char KEY_LOG_PATH[] = "LOG_PATH";
38 constexpr char KEY_MODULE[] = "MODULE";
39 constexpr char INIT_LOG_PATTERN[] = "Service warning ";
40 constexpr char KEY_NO_LOG_EVENT_NAME[] = "CPP_CRASH_NO_LOG";
41 constexpr char KEY_HAPPEN_TIME[] = "HAPPEN_TIME";
42 constexpr int32_t LOG_SIZE = 1024;
43 constexpr uint64_t MAX_LOG_GENERATE_TIME = 600; // 600 seconds
44 constexpr int32_t KMSG_SIZE = 2049;
45 }
CrashValidator()46 CrashValidator::CrashValidator() : stopReadKmsg_(false), totalEventCount_(0),
47     normalEventCount_(0), kmsgReaderThread_(nullptr)
48 {
49 }
50 
~CrashValidator()51 CrashValidator::~CrashValidator()
52 {
53     if (kmsgReaderThread_ != nullptr) {
54         kmsgReaderThread_ = nullptr;
55     }
56 }
57 
OnEvent(std::shared_ptr<HiviewDFX::HiSysEventRecord> sysEvent)58 void CrashValidator::OnEvent(std::shared_ptr<HiviewDFX::HiSysEventRecord> sysEvent)
59 {
60     std::lock_guard<std::mutex> lock(lock_);
61     if (sysEvent == nullptr) {
62         return;
63     }
64     auto domain = sysEvent->GetDomain();
65     auto eventName = sysEvent->GetEventName();
66     if (eventName == EVENT_CPP_CRASH) {
67         HandleCppCrashEvent(sysEvent);
68     } else if (eventName == KEY_PROCESS_EXIT) {
69         HandleProcessExitEvent(sysEvent);
70     }
71 }
72 
OnServiceDied()73 void CrashValidator::OnServiceDied()
74 {
75     printf("SysEventServiceDied?.\n");
76 }
77 
InitSysEventListener()78 bool CrashValidator::InitSysEventListener()
79 {
80     std::vector<ListenerRule> sysRules;
81     sysRules.emplace_back("RELIABILITY", "CPP_CRASH");
82     sysRules.emplace_back("STARTUP", "PROCESS_EXIT");
83     if (HiSysEventManager::AddListener(shared_from_this(), sysRules) != 0) {
84         return false;
85     }
86 
87     kmsgReaderThread_ = std::make_unique<std::thread>([this] {
88         ReadServiceCrashStatus();
89     });
90     kmsgReaderThread_->detach();
91     return true;
92 }
93 
RemoveSysEventListener()94 void CrashValidator::RemoveSysEventListener()
95 {
96     int32_t result = HiSysEventManager::RemoveListener(shared_from_this());
97     printf("remove listener result: %d\n", result);
98 }
99 
PrintEvents(int fd,const std::vector<CrashEvent> & events,bool isMatched)100 void CrashValidator::PrintEvents(int fd, const std::vector<CrashEvent>& events, bool isMatched)
101 {
102     std::vector<CrashEvent>::const_iterator it = events.begin();
103     while (it != events.end()) {
104         if (isMatched) {
105             dprintf(fd, "Module:%s Time:%" PRIu64 " Pid:%" PRIu64 " Uid:%" PRIu64 "\n",
106                 it->name.c_str(),
107                 static_cast<uint64_t>(it->time),
108                 static_cast<uint64_t>(it->pid),
109                 static_cast<uint64_t>(it->uid));
110         } else {
111             dprintf(fd, "Module:%s Time:%" PRIu64 " Pid:%" PRIu64 " Uid:%" PRIu64 " HasLog:%d\n",
112                 it->name.c_str(),
113                 static_cast<uint64_t>(it->time),
114                 static_cast<uint64_t>(it->pid),
115                 static_cast<uint64_t>(it->uid),
116                 it->isCppCrash);
117         }
118         ++it;
119     }
120 }
121 
Dump(int fd)122 void CrashValidator::Dump(int fd)
123 {
124     dprintf(fd, "Summary:\n");
125     dprintf(fd, "Total Signaled Process:%d\n", totalEventCount_);
126     dprintf(fd, "Total CppCrash Count:%d\n", normalEventCount_);
127     if (totalEventCount_ > 0) {
128         dprintf(fd, "CppCrash detect rate:%d%%\n",
129             (normalEventCount_ * 100) / totalEventCount_); // 100 : percent ratio
130     }
131 
132     std::lock_guard<std::mutex> lock(lock_);
133     if (!noLogEvents_.empty()) {
134         dprintf(fd, "No CppCrash Log Events(%zu):\n", noLogEvents_.size());
135         PrintEvents(fd, noLogEvents_, false);
136     }
137 
138     if (!pendingEvents_.empty()) {
139         dprintf(fd, "Pending CppCrash Log Events(%zu):\n", pendingEvents_.size());
140         PrintEvents(fd, pendingEvents_, false);
141     }
142 
143     if (!matchedEvents_.empty()) {
144         dprintf(fd, "Matched Events(%zu):\n", matchedEvents_.size());
145         PrintEvents(fd, matchedEvents_, true);
146     }
147 }
148 
RemoveSimilarEvent(const CrashEvent & event)149 bool CrashValidator::RemoveSimilarEvent(const CrashEvent& event)
150 {
151     for (const auto& matchedEvent : matchedEvents_) {
152         if (matchedEvent.pid == event.pid && matchedEvent.uid == event.uid) {
153             return true;
154         }
155     }
156     std::vector<CrashEvent>::iterator it = pendingEvents_.begin();
157     while (it != pendingEvents_.end()) {
158         if (it->uid == event.uid && it->pid == event.pid) {
159             if (it->isCppCrash != event.isCppCrash) {
160                 it = pendingEvents_.erase(it);
161                 normalEventCount_++;
162                 matchedEvents_.push_back(event);
163             }
164             return true;
165         } else {
166             ++it;
167         }
168     }
169     return false;
170 }
171 
HandleCppCrashEvent(std::shared_ptr<HiviewDFX::HiSysEventRecord> sysEvent)172 void CrashValidator::HandleCppCrashEvent(std::shared_ptr<HiviewDFX::HiSysEventRecord> sysEvent)
173 {
174     if (sysEvent == nullptr) {
175         return;
176     }
177     CrashEvent crashEvent;
178     crashEvent.isCppCrash = true;
179     sysEvent->GetParamValue(KEY_HAPPEN_TIME, crashEvent.time);
180     sysEvent->GetParamValue(KEY_UID, crashEvent.uid);
181     sysEvent->GetParamValue(KEY_PID, crashEvent.pid);
182     sysEvent->GetParamValue(KEY_LOG_PATH, crashEvent.path);
183     sysEvent->GetParamValue(KEY_MODULE, crashEvent.name);
184     printf("CPPCRASH:[Pid:%" PRIi64 " Uid:%" PRIi64 " Module:%s]\n",
185         crashEvent.pid, crashEvent.uid, crashEvent.name.c_str());
186     if (!RemoveSimilarEvent(crashEvent)) {
187         totalEventCount_++;
188         pendingEvents_.push_back(crashEvent);
189     }
190 }
191 
HandleProcessExitEvent(std::shared_ptr<HiviewDFX::HiSysEventRecord> sysEvent)192 void CrashValidator::HandleProcessExitEvent(std::shared_ptr<HiviewDFX::HiSysEventRecord> sysEvent)
193 {
194     if (sysEvent == nullptr) {
195         return;
196     }
197     int64_t status64 = 0;
198     sysEvent->GetParamValue(KEY_STATUS, status64);
199     int32_t status = static_cast<int32_t>(status64);
200     if (!WIFSIGNALED(status) && !WIFEXITED(status)) {
201         return;
202     }
203 
204     CrashEvent crashEvent;
205     crashEvent.isCppCrash = false;
206     crashEvent.time = sysEvent->GetTime();
207     sysEvent->GetParamValue(KEY_UID, crashEvent.uid);
208     sysEvent->GetParamValue(KEY_PID, crashEvent.pid);
209     sysEvent->GetParamValue(KEY_NAME, crashEvent.name);
210     int exitSigno = WTERMSIG(status);
211     // crash in pid namespace exit signo is zero, instead of use exit status code.
212     if (exitSigno == 0) {
213         exitSigno = WEXITSTATUS(status);
214     }
215 
216     int interestedSignalList[] = {
217         SIGABRT, SIGBUS, SIGFPE, SIGILL,
218         SIGSEGV, SIGSTKFLT, SIGSYS, SIGTRAP };
219     bool shouldGenerateCppCrash = false;
220     for (size_t i = 0; i < sizeof(interestedSignalList) / sizeof(interestedSignalList[0]); i++) {
221         if (interestedSignalList[i] == exitSigno) {
222             shouldGenerateCppCrash = true;
223             break;
224         }
225     }
226 
227     if (!shouldGenerateCppCrash) {
228         return;
229     }
230 
231     printf("Process Exit Name:%s Time:%llu [Pid:%" PRIi64 " Uid:%" PRIi64 "] status:%d\n",
232         crashEvent.name.c_str(),
233         static_cast<unsigned long long>(crashEvent.time),
234         crashEvent.pid,
235         crashEvent.uid,
236         status);
237     if (!RemoveSimilarEvent(crashEvent)) {
238         totalEventCount_++;
239         pendingEvents_.push_back(crashEvent);
240     }
241 }
242 
CheckOutOfDateEvents()243 void CrashValidator::CheckOutOfDateEvents()
244 {
245     std::vector<CrashEvent>::iterator it = pendingEvents_.begin();
246     while (it != pendingEvents_.end()) {
247         uint64_t now = time(nullptr);
248         uint64_t eventTime = it->time;
249         if (eventTime > now) {
250             eventTime = eventTime / 1000; // 1000 : convert to second
251         }
252 
253         if (now > eventTime && now - eventTime < MAX_LOG_GENERATE_TIME) {
254             ++it;
255             continue;
256         }
257 
258         if (!it->isCppCrash) {
259             HiSysEventWrite(HiSysEvent::Domain::RELIABILITY, KEY_NO_LOG_EVENT_NAME, HiSysEvent::EventType::FAULT,
260                 KEY_NAME, it->name,
261                 KEY_PID, it->pid,
262                 KEY_UID, it->uid,
263                 KEY_HAPPEN_TIME, it->time);
264             noLogEvents_.push_back(*it);
265         } else {
266             totalEventCount_++;
267             normalEventCount_++;
268         }
269         it = pendingEvents_.erase(it);
270     }
271 }
272 
ReadServiceCrashStatus()273 void CrashValidator::ReadServiceCrashStatus()
274 {
275     char kmsg[KMSG_SIZE];
276     int fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
277     if (fd == -1) {
278         printf("Failed to open /dev/kmsg.\n");
279         return;
280     }
281     lseek(fd, 0, 3); // 3 : SEEK_DATA
282     while (true) {
283         ssize_t len;
284         if (((len = read(fd, kmsg, sizeof(kmsg))) == -1) && errno == EPIPE) {
285             continue;
286         }
287         if (len == -1 && errno == EINVAL) {
288             printf("Failed to read kmsg\n");
289             close(fd);
290             return;
291         }
292         if (len < 1) {
293             continue;
294         }
295         kmsg[len] = 0;
296         if (stopReadKmsg_) {
297             break;
298         }
299         std::string line = kmsg;
300         auto pos = line.find(INIT_LOG_PATTERN);
301         if (pos == std::string::npos) {
302             continue;
303         }
304         std::string formattedLog = line.substr(pos + strlen(INIT_LOG_PATTERN));
305         char name[LOG_SIZE] {0};
306         int pid;
307         int uid;
308         int status;
309         int ret = sscanf_s(formattedLog.c_str(), "%[^,], SIGCHLD received, pid:%d uid:%d status:%d.",
310             name, sizeof(name), &pid, &uid, &status);
311         if (ret <= 0) {
312             printf("Failed to parse kmsg:%s", formattedLog.c_str());
313             continue;
314         }
315 
316         printf("Kernel:%s", formattedLog.c_str());
317         HiSysEventWrite(HiSysEvent::Domain::STARTUP, KEY_PROCESS_EXIT, HiSysEvent::EventType::BEHAVIOR,
318             KEY_NAME, name, KEY_PID, pid, KEY_UID, uid, KEY_STATUS, status);
319     }
320     close(fd);
321 }
322 
ValidateLogContent(const CrashEvent & event)323 bool CrashValidator::ValidateLogContent(const CrashEvent& event)
324 {
325     // check later
326     return true;
327 }
328 } // namespace HiviewDFX
329 } // namespace OHOS
330