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