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