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