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