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