1 /*
2 * Copyright (c) 2024 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
16 #include "fault_kernel_snapshot.h"
17
18 #include <cstdio>
19 #include <cstdlib>
20 #include <dlfcn.h>
21 #include <fcntl.h>
22 #include <list>
23 #include <pthread.h>
24 #include <thread>
25 #include <unistd.h>
26 #include <unordered_map>
27
28 #ifndef HISYSEVENT_DISABLE
29 #include "hisysevent.h"
30 #endif
31 #ifndef is_ohos_lite
32 #include "parameters.h"
33 #endif // !is_ohos_lite
34
35 #include "faultlogger_client_msg.h"
36
37 #include "dfx_log.h"
38 #include "dfx_util.h"
39 #include "string_util.h"
40
41 namespace OHOS {
42 namespace HiviewDFX {
43 namespace {
44 constexpr const char * const KERNEL_KBOX_SNAPSHOT = "/sys/kbox/snapshot_clear";
45 constexpr const char * const KBOX_SNAPSHOT_DUMP_PATH = "/data/log/faultlog/temp/";
46 constexpr const char * const KERNEL_SNAPSHOT_CHECK_INTERVAL = "kernel_snapshot_check_interval";
47 constexpr const char * const DEFAULT_CHECK_INTERVAL = "60";
48 constexpr const char * const KERNEL_SNAPSHOT_REASON = "CppCrashKernelSnapshot";
49 constexpr int MIN_CHECK_INTERVAL = 3;
50 constexpr int BUFFER_LEN = 1024;
51 constexpr int SEQUENCE_LEN = 7;
52 constexpr int DECIMAL_BASE = 10;
53
54 enum class CrashSection {
55 TIME_STAMP,
56 PID,
57 UID,
58 PROCESS_NAME,
59 REASON,
60 FAULT_THREAD_INFO,
61 REGISTERS,
62 MEMORY_NEAR_REGISTERS,
63 FAULT_STACK,
64 MAPS,
65 EXCEPTION_REGISTERS,
66 INVALID_SECTION
67 };
68
69 enum class SnapshotSection {
70 TRANSACTION_START,
71 EXCEPTION_REGISTERS,
72 ABORT_ADDRESS_PTE,
73 THREAD_INFO,
74 DUMP_REGISTERS,
75 DUMP_FPU_OR_SIMD_REGISTERS,
76 STACK_BACKTRACE,
77 ELF_LOAD_INFO,
78 DATA_ON_TARGET_OF_LAST_BL,
79 DATA_AROUND_REGS,
80 CONTENT_OF_USER_STACK,
81 BASE_ACTV_DUMPED,
82 PROCESS_STATISTICS,
83 TRANSACTION_END
84 };
85
86 struct SnapshotSectionInfo {
87 SnapshotSection type;
88 const char* key;
89 };
90
91 const SnapshotSectionInfo SNAPSHOT_SECTION_KEYWORDS[] = {
92 {SnapshotSection::TRANSACTION_START, "[transaction start] now mono_time"},
93 {SnapshotSection::EXCEPTION_REGISTERS, "Exception registers:"},
94 {SnapshotSection::ABORT_ADDRESS_PTE, "Abort address pte"},
95 {SnapshotSection::THREAD_INFO, "Thread info:"},
96 {SnapshotSection::DUMP_REGISTERS, "Dump registers:"},
97 {SnapshotSection::DUMP_FPU_OR_SIMD_REGISTERS, "Dump fpu or simd registers:"},
98 {SnapshotSection::STACK_BACKTRACE, "Stack backtrace"},
99 {SnapshotSection::ELF_LOAD_INFO, "Elf load info"},
100 {SnapshotSection::DATA_ON_TARGET_OF_LAST_BL, "Data on target of last"},
101 {SnapshotSection::DATA_AROUND_REGS, "Data around regs"},
102 {SnapshotSection::CONTENT_OF_USER_STACK, "Contents of user stack"},
103 {SnapshotSection::BASE_ACTV_DUMPED, "[base actv dumped]"},
104 {SnapshotSection::PROCESS_STATISTICS, "Process statistics:"},
105 {SnapshotSection::TRANSACTION_END, "[transaction end] now mono_time"}
106 };
107
108 using CrashMap = std::unordered_map<CrashSection, std::string>;
109
110 void ReportCrashNoLogEvent(CrashMap& output);
111 void SaveSnapshot(CrashMap& output);
112
113 class CrashKernelFrame {
114 public:
Parse(const std::string & line)115 void Parse(const std::string& line)
116 {
117 size_t pos = 0;
118 pc = ExtractContent(line, pos, '[', ']');
119 fp = ExtractContent(line, ++pos, '[', ']');
120 funcNameOffset = ExtractContent(line, ++pos, '<', '>');
121 elf = ExtractContent(line, ++pos, '(', ')');
122 }
123
ToString(int count) const124 std::string ToString(int count) const
125 {
126 std::string data = std::string("#") + (count < 10 ? "0" : "") + std::to_string(count);
127 data += " pc " + pc + " " + elf;
128 return data;
129 }
130
131 private:
132 std::string pc;
133 std::string fp;
134 std::string funcNameOffset;
135 std::string elf;
136
ExtractContent(const std::string & line,size_t & pos,char startChar,char endChar)137 static std::string ExtractContent(const std::string& line, size_t& pos, char startChar, char endChar)
138 {
139 size_t start = line.find(startChar, pos);
140 size_t end = line.find(endChar, start);
141 pos = end + 1;
142 if (start != std::string::npos && end != std::string::npos) {
143 return line.substr(start + 1, end - start - 1);
144 }
145 return "";
146 }
147 };
148
ReportCrashEvent(CrashMap & output)149 void ReportCrashEvent(CrashMap& output)
150 {
151 if (output[CrashSection::UID].empty()) {
152 DFXLOGE("uid is empty, not report");
153 return;
154 }
155
156 void* handle = dlopen("libfaultlogger.z.so", RTLD_LAZY | RTLD_NODELETE);
157 if (handle == nullptr) {
158 DFXLOGW("Failed to dlopen libfaultlogger, %{public}s\n", dlerror());
159 return;
160 }
161
162 auto addFaultLog = reinterpret_cast<void (*)(FaultDFXLOGIInner*)>(dlsym(handle, "AddFaultLog"));
163 if (addFaultLog == nullptr) {
164 DFXLOGW("Failed to dlsym AddFaultLog, %{public}s\n", dlerror());
165 dlclose(handle);
166 return;
167 }
168
169 FaultDFXLOGIInner info;
170 info.time = strtoul(output[CrashSection::TIME_STAMP].c_str(), nullptr, DECIMAL_BASE);
171 info.id = static_cast<uint32_t>(strtoul(output[CrashSection::UID].c_str(), nullptr, DECIMAL_BASE));
172 info.pid = static_cast<int32_t>(strtol(output[CrashSection::PID].c_str(), nullptr, DECIMAL_BASE));
173 info.faultLogType = 2; // 2 : CPP_CRASH_TYPE
174 info.module = output[CrashSection::PROCESS_NAME];
175 info.reason = KERNEL_SNAPSHOT_REASON;
176 info.summary = output[CrashSection::FAULT_THREAD_INFO];
177 addFaultLog(&info);
178 DFXLOGI("Finish report fault to FaultLogger (%{public}u,%{public}d)", info.id, info.pid);
179 dlclose(handle);
180 }
181
GetBuildInfo()182 std::string GetBuildInfo()
183 {
184 #ifndef is_ohos_lite
185 static std::string buildInfo = OHOS::system::GetParameter("const.product.software.version", "Unknown");
186 return buildInfo;
187 #else
188 return "Unknown";
189 #endif
190 }
191
PreProcessLine(std::string & line)192 bool PreProcessLine(std::string& line)
193 {
194 if (line.size() <= SEQUENCE_LEN || line[0] == '\t') {
195 return false;
196 }
197 // move timestamp to end
198 if (isdigit(line[1])) {
199 auto pos = line.find('[', 1);
200 if (pos != std::string::npos) {
201 std::string tmp = line.substr(0, pos);
202 line = line.substr(pos) + tmp;
203 }
204 }
205 return true;
206 }
207
ConvertThreadInfoToPairs(const std::string & line)208 std::unordered_map<std::string, std::string> ConvertThreadInfoToPairs(const std::string& line)
209 {
210 std::unordered_map<std::string, std::string> pairs;
211 size_t pos = 0;
212 while (pos < line.size()) {
213 while (pos < line.size() && line[pos] == ' ') {
214 pos++;
215 }
216 size_t keyStart = pos;
217
218 while (pos < line.size() && line[pos] != '=') {
219 pos++;
220 }
221 if (pos >= line.size()) {
222 break;
223 }
224 std::string key = line.substr(keyStart, pos - keyStart);
225
226 size_t valueStart = ++pos;
227 while (pos < line.size() && line[pos] != ',') {
228 pos++;
229 }
230 if (pos >= line.size()) {
231 break;
232 }
233 pairs[key] = line.substr(valueStart, pos - valueStart);
234 pos++;
235 }
236 return pairs;
237 }
238
ParseTransStart(const std::string & cont,CrashMap & output)239 void ParseTransStart(const std::string& cont, CrashMap& output)
240 {
241 /**
242 * kernel crash snapshot transaction start format:
243 * [AB_00][transaction start] now mono_time is [45.006871][1733329272.590140]
244 */
245 if (cont.find("mono_time") == std::string::npos) {
246 return;
247 }
248 auto msPos = cont.rfind(".");
249 std::string millsecond;
250 const int millsecondLen = 3;
251 if (msPos != std::string::npos && msPos + 1 < cont.length()) {
252 millsecond = cont.substr(msPos + 1, millsecondLen);
253 }
254 millsecond = millsecond.length() != millsecondLen ? "000" : millsecond;
255 auto secondPos = cont.rfind("[");
256 if (secondPos != std::string::npos && secondPos + 1 < cont.length()) {
257 output[CrashSection::TIME_STAMP] = cont.substr(secondPos + 1, 10) + millsecond; // 10: second timestamp length
258 }
259 }
260
ParseThreadInfo(const std::vector<std::string> & lines,int start,int end,CrashMap & output)261 void ParseThreadInfo(const std::vector<std::string>& lines, int start, int end, CrashMap& output)
262 {
263 if (start + 1 > end) {
264 return;
265 }
266 /**
267 * kernel crash snapshot thread info format:
268 * Thread info:
269 * name=ei.hmsapp.music, tid=5601, state=RUNNING, sctime=40.362389062, tcb_cref=502520008108a, pid=5601,
270 * ppid=656, pgid=1, uid=20020048, cpu=7, cur_rq=7
271 */
272 std::string info = lines[start + 1];
273 DFXLOGI("kenel snapshot thread info : %{public}s", info.c_str());
274 auto pairs = ConvertThreadInfoToPairs(info);
275 output[CrashSection::PROCESS_NAME] = pairs["name"]; // native process use this
276 output[CrashSection::FAULT_THREAD_INFO] = "Tid:" + pairs["tid"] + ", Name: " + pairs["name"] + "\n";
277 output[CrashSection::PID] = pairs["pid"];
278 output[CrashSection::UID] = pairs["uid"];
279 }
280
ParseStackBacktrace(const std::vector<std::string> & lines,int start,int end,CrashMap & output)281 void ParseStackBacktrace(const std::vector<std::string>& lines, int start, int end, CrashMap& output)
282 {
283 CrashKernelFrame frame;
284 for (int i = start + 1; i <= end; i++) {
285 frame.Parse(lines[i]);
286 output[CrashSection::FAULT_THREAD_INFO] += frame.ToString(i - start - 1) + "\n";
287 }
288 }
289
ParseProcessRealName(const std::vector<std::string> & lines,int start,int end,CrashMap & output)290 void ParseProcessRealName(const std::vector<std::string>& lines, int start, int end, CrashMap& output)
291 {
292 /**
293 * kernel crash snapshot process statistics format:
294 * [ED_00]Process statistics:
295 * [ED_00] name tid state tcb_cref sched_cnt cpu_cur rq_cur cls rtprio ni pri pid ppid pgid
296 * [ED_00] SaInit1 1012 RUNNING 5022a0008106b 7 7 6 TS - 0 20 799 1 1
297 * [ED_00] audio_server 799 BLOCKED 5022a0008108a 325 4 6 TS - 0 20 799 1 1
298 */
299 for (int i = start + 2; i <= end; i++) { // 2 : skip header
300 const auto& item = lines[i];
301 size_t nameStart = item.find_first_not_of(' ');
302 if (nameStart == std::string::npos) {
303 continue;
304 }
305 size_t nameEnd = item.find_first_of(' ', nameStart);
306 if (nameEnd == std::string::npos) {
307 continue;
308 }
309 std::string name = item.substr(nameStart, nameEnd - nameStart);
310
311 size_t tidStart = item.find_first_not_of(' ', nameEnd);
312 if (tidStart == std::string::npos) {
313 continue;
314 }
315 size_t tidEnd = item.find_first_of(' ', tidStart);
316 if (tidEnd == std::string::npos) {
317 continue;
318 }
319 std::string tid = item.substr(tidStart, tidEnd - tidStart);
320 if (tid == output[CrashSection::PID]) {
321 output[CrashSection::PROCESS_NAME] = name;
322 break;
323 }
324 }
325 }
326
GetSnapshotMapCrashItem(const SnapshotSection & item)327 CrashSection GetSnapshotMapCrashItem(const SnapshotSection& item)
328 {
329 switch (item) {
330 case SnapshotSection::DUMP_REGISTERS:
331 return CrashSection::REGISTERS;
332 case SnapshotSection::DATA_AROUND_REGS:
333 return CrashSection::MEMORY_NEAR_REGISTERS;
334 case SnapshotSection::CONTENT_OF_USER_STACK:
335 return CrashSection::FAULT_STACK;
336 case SnapshotSection::ELF_LOAD_INFO:
337 return CrashSection::MAPS;
338 case SnapshotSection::EXCEPTION_REGISTERS:
339 return CrashSection::EXCEPTION_REGISTERS;
340 default:
341 return CrashSection::INVALID_SECTION;
342 }
343 }
344
ParseDefaultAction(const std::vector<std::string> & lines,int start,int end,SnapshotSection key,CrashMap & output)345 void ParseDefaultAction(const std::vector<std::string>& lines, int start, int end,
346 SnapshotSection key, CrashMap& output)
347 {
348 auto it = GetSnapshotMapCrashItem(key);
349 if (it == CrashSection::INVALID_SECTION) {
350 return;
351 }
352 for (int i = start + 1; i <= end; i++) {
353 output[it] += lines[i] + "\n";
354 }
355 }
356
ProcessSnapshotSection(SnapshotSection sectionKey,const std::vector<std::string> & lines,size_t start,size_t end,CrashMap & output)357 void ProcessSnapshotSection(SnapshotSection sectionKey, const std::vector<std::string>& lines,
358 size_t start, size_t end, CrashMap& output)
359 {
360 switch (sectionKey) {
361 case SnapshotSection::TRANSACTION_START:
362 ParseTransStart(lines[start], output);
363 break;
364 case SnapshotSection::THREAD_INFO:
365 ParseThreadInfo(lines, start, end, output);
366 break;
367 case SnapshotSection::STACK_BACKTRACE:
368 ParseStackBacktrace(lines, start, end, output);
369 break;
370 case SnapshotSection::PROCESS_STATISTICS:
371 ParseProcessRealName(lines, start, end, output);
372 break;
373 default:
374 ParseDefaultAction(lines, start, end, sectionKey, output);
375 break;
376 }
377 }
378
ProcessTransStart(const std::vector<std::string> & lines,size_t & index,std::list<std::pair<SnapshotSection,std::string>> & keywordList,CrashMap & output)379 bool ProcessTransStart(const std::vector<std::string>& lines, size_t& index,
380 std::list<std::pair<SnapshotSection, std::string>>& keywordList, CrashMap& output)
381 {
382 const auto& keyword = keywordList.front().second;
383 for (; index < lines.size(); index++) {
384 if (StartsWith(lines[index], keyword)) {
385 break;
386 }
387 }
388
389 if (index == lines.size()) {
390 return false;
391 }
392
393 ProcessSnapshotSection(SnapshotSection::TRANSACTION_START, lines, index, index, output);
394 index++;
395 keywordList.pop_front();
396 return true;
397 }
398
ParseSnapshotUnit(const std::vector<std::string> & lines,size_t & index)399 void ParseSnapshotUnit(const std::vector<std::string>& lines, size_t& index)
400 {
401 CrashMap output;
402 std::list<std::pair<SnapshotSection, std::string>> keywordList;
403 for (const auto& item : SNAPSHOT_SECTION_KEYWORDS) {
404 keywordList.emplace_back(item.type, item.key);
405 }
406
407 if (!ProcessTransStart(lines, index, keywordList, output)) {
408 return;
409 }
410
411 // process other snapshot sections
412 size_t snapshotSecIndex = 0;
413 SnapshotSection snapshotSecKey;
414 bool isTransEnd = false;
415
416 for (; index < lines.size() && !isTransEnd; index++) {
417 for (auto it = keywordList.begin(); it != keywordList.end(); it++) {
418 if (!StartsWith(lines[index], it->second)) {
419 continue;
420 }
421 if (snapshotSecIndex == 0) {
422 snapshotSecIndex = index;
423 snapshotSecKey = it->first;
424 break;
425 }
426 ProcessSnapshotSection(snapshotSecKey, lines, snapshotSecIndex, index - 1, output);
427 snapshotSecIndex = index;
428 snapshotSecKey = it->first;
429 if (it->first == SnapshotSection::TRANSACTION_END) {
430 isTransEnd = true;
431 }
432 keywordList.erase(it);
433 break;
434 }
435 }
436
437 SaveSnapshot(output);
438 ReportCrashEvent(output);
439 ReportCrashNoLogEvent(output);
440 }
441
ParseSameSeqSnapshot(const std::vector<std::string> & lines)442 void ParseSameSeqSnapshot(const std::vector<std::string>& lines)
443 {
444 size_t curLineNum = 0;
445 while (curLineNum < lines.size()) {
446 ParseSnapshotUnit(lines, curLineNum);
447 }
448 }
449
ParseSnapshot(std::vector<std::string> & snapshotLines)450 void ParseSnapshot(std::vector<std::string>& snapshotLines)
451 {
452 std::unordered_map<std::string, std::vector<std::string>> kernelSnapshotMap;
453 // devide snapshot info by sequence number
454 for (auto &line : snapshotLines) {
455 if (!PreProcessLine(line)) {
456 continue;
457 }
458
459 std::string seqNum = line.substr(0, SEQUENCE_LEN);
460 kernelSnapshotMap[seqNum].emplace_back(line.substr(SEQUENCE_LEN));
461 }
462
463 for (auto &item : kernelSnapshotMap) {
464 ParseSameSeqSnapshot(item.second);
465 }
466 }
467
FilterEmptySection(const std::string & secHead,const std::string & secCont,const std::string & end)468 std::string FilterEmptySection(const std::string& secHead, const std::string& secCont, const std::string& end)
469 {
470 if (secCont.empty()) {
471 return "";
472 }
473 return secHead + secCont + end;
474 }
475
FormatTimestamp(const std::string & timestamp)476 std::string FormatTimestamp(const std::string& timestamp)
477 {
478 uint64_t time = strtoul(timestamp.c_str(), nullptr, DECIMAL_BASE);
479 if (errno == ERANGE) {
480 DFXLOGE("Failed to convert timestamp to uint64_t");
481 time = 0;
482 }
483 return GetCurrentTimeStr(time);
484 }
485
OutputToFile(const std::string & filePath,CrashMap & output)486 void OutputToFile(const std::string& filePath, CrashMap& output)
487 {
488 FILE* file = fopen(filePath.c_str(), "w");
489 if (file == nullptr) {
490 DFXLOGE("open file failed %{public}s errno %{public}d", filePath.c_str(), errno);
491 return;
492 }
493 std::string outputCont;
494 outputCont += FilterEmptySection("Build info: ", GetBuildInfo(), "\n");
495 outputCont += FilterEmptySection("Timestamp: ", FormatTimestamp(output[CrashSection::TIME_STAMP]), "");
496 outputCont += FilterEmptySection("Pid: ", output[CrashSection::PID], "\n");
497 outputCont += FilterEmptySection("Uid: ", output[CrashSection::UID], "\n");
498 outputCont += FilterEmptySection("Process name: ", output[CrashSection::PROCESS_NAME], "\n");
499 outputCont += FilterEmptySection("Reason: ", KERNEL_SNAPSHOT_REASON, "\n");
500 outputCont += FilterEmptySection("Exception registers:\n", output[CrashSection::EXCEPTION_REGISTERS], "");
501 outputCont += FilterEmptySection("Fault thread info:\n", output[CrashSection::FAULT_THREAD_INFO], "");
502 outputCont += FilterEmptySection("Registers:\n", output[CrashSection::REGISTERS], "");
503 outputCont += FilterEmptySection("Memory near registers:\n", output[CrashSection::MEMORY_NEAR_REGISTERS], "");
504 outputCont += FilterEmptySection("FaultStack:\n", output[CrashSection::FAULT_STACK], "");
505 outputCont += FilterEmptySection("Elfs:\n", output[CrashSection::MAPS], "");
506 if (fwrite(outputCont.c_str(), sizeof(char), outputCont.length(), file) != outputCont.length()) {
507 DFXLOGE("write file failed %{public}s errno %{public}d", filePath.c_str(), errno);
508 }
509 if (fclose(file) != 0) {
510 DFXLOGE("close file failed %{public}s errno %{public}d", filePath.c_str(), errno);
511 }
512 }
513
SaveSnapshot(CrashMap & output)514 void SaveSnapshot(CrashMap& output)
515 {
516 if (output[CrashSection::PID].empty()) {
517 DFXLOGE("pid is empty, not save snapshot");
518 return;
519 }
520
521 std::string filePath = std::string(KBOX_SNAPSHOT_DUMP_PATH) + "cppcrash-" +
522 output[CrashSection::PID] + "-" +
523 output[CrashSection::TIME_STAMP];
524 OutputToFile(filePath, output);
525 }
526
GetSnapshotCheckInterval()527 int GetSnapshotCheckInterval()
528 {
529 #ifndef is_ohos_lite
530 std::string interval = OHOS::system::GetParameter(KERNEL_SNAPSHOT_CHECK_INTERVAL, DEFAULT_CHECK_INTERVAL);
531 #else
532 std::string interval = DEFAULT_CHECK_INTERVAL;
533 #endif
534 int value = static_cast<int>(strtol(interval.c_str(), nullptr, DECIMAL_BASE));
535 if (errno == ERANGE) {
536 DFXLOGE("get snapshot check interval failed, use default interval");
537 value = 60; // 60 : default interval
538 }
539 value = value < MIN_CHECK_INTERVAL ? MIN_CHECK_INTERVAL : value;
540
541 DFXLOGI("monitor crash kernel snapshot interval %{public}d", value);
542 return value;
543 }
544
SplitByNewLine(const std::string & str,std::vector<std::string> & lines)545 void SplitByNewLine(const std::string& str, std::vector<std::string>& lines)
546 {
547 size_t start = 0;
548 while (start < str.size()) {
549 size_t end = str.find('\n', start);
550 if (end == std::string::npos) {
551 end = str.size();
552 }
553 lines.emplace_back(str.substr(start, end - start));
554 start = end + 1;
555 }
556 }
557
MonitorCrashKernelSnapshot()558 void MonitorCrashKernelSnapshot()
559 {
560 DFXLOGI("enter %{public}s ", __func__);
561 pthread_setname_np(pthread_self(), "KernelSnapshot");
562 int interval = GetSnapshotCheckInterval();
563 while (true) {
564 std::this_thread::sleep_for(std::chrono::seconds(interval));
565 if (access(KERNEL_KBOX_SNAPSHOT, F_OK) < 0) {
566 DFXLOGE("can't find %{public}s, just exit", KERNEL_KBOX_SNAPSHOT);
567 return;
568 }
569 int snapshotFd = open(KERNEL_KBOX_SNAPSHOT, O_RDONLY);
570 if (snapshotFd < 0) {
571 DFXLOGE("open snapshot filed %{public}d", errno);
572 continue;
573 }
574
575 char buffer[BUFFER_LEN] = {0};
576 std::vector<std::string> snapshotLines;
577 std::string snapshotCont;
578
579 int ret = read(snapshotFd, buffer, BUFFER_LEN - 1);
580 while (ret > 0) {
581 snapshotCont += buffer;
582 ret = read(snapshotFd, buffer, BUFFER_LEN - 1);
583 }
584 close(snapshotFd);
585
586 SplitByNewLine(snapshotCont, snapshotLines);
587 ParseSnapshot(snapshotLines);
588 }
589 }
590
ReportCrashNoLogEvent(CrashMap & output)591 void ReportCrashNoLogEvent(CrashMap& output)
592 {
593 if (output[CrashSection::UID].empty()) {
594 DFXLOGE("uid is empty, not report");
595 return;
596 }
597 #ifndef HISYSEVENT_DISABLE
598 int32_t uid = static_cast<int32_t>(strtol(output[CrashSection::UID].c_str(), nullptr, DECIMAL_BASE));
599 int32_t pid = static_cast<int32_t>(strtol(output[CrashSection::PID].c_str(), nullptr, DECIMAL_BASE));
600 int64_t timeStamp = strtoll(output[CrashSection::TIME_STAMP].c_str(), nullptr, DECIMAL_BASE);
601 if (errno == ERANGE) {
602 DFXLOGE("Failed to convert string to number, error: %{public}s", strerror(errno));
603 return;
604 }
605
606 std::string summary;
607 summary += FilterEmptySection("Build info: ", GetBuildInfo(), "\n");
608 summary += FilterEmptySection("Timestamp: ", FormatTimestamp(output[CrashSection::TIME_STAMP]), "");
609 summary += FilterEmptySection("Pid: ", output[CrashSection::PID], "\n");
610 summary += FilterEmptySection("Uid: ", output[CrashSection::UID], "\n");
611 summary += FilterEmptySection("Process name: ", output[CrashSection::PROCESS_NAME], "\n");
612 summary += FilterEmptySection("Reason: ", KERNEL_SNAPSHOT_REASON, "\n");
613 summary += FilterEmptySection("Exception registers:\n", output[CrashSection::EXCEPTION_REGISTERS], "");
614 summary += FilterEmptySection("Fault thread info:\n", output[CrashSection::FAULT_THREAD_INFO], "");
615 summary += FilterEmptySection("Registers:\n", output[CrashSection::REGISTERS], "");
616 summary += FilterEmptySection("Elfs:\n", output[CrashSection::MAPS], "");
617
618 HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::RELIABILITY, "CPP_CRASH_NO_LOG",
619 OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
620 "UID", uid,
621 "PID", pid,
622 "PROCESS_NAME", output[CrashSection::PROCESS_NAME].c_str(),
623 "HAPPEN_TIME", timeStamp,
624 "SUMMARY", summary);
625 DFXLOGI("Report kernel snapshot event done.");
626 #else
627 DFXLOGI("Not supported for kernel snapshot report.");
628 #endif
629 }
630 } // namespace
631
StartMonitor()632 void FaultKernelSnapshot::StartMonitor()
633 {
634 DFXLOGI("monitor kernel crash snapshot start!");
635 if (!IsBetaVersion()) {
636 DFXLOGW("monitor kernel crash snapshot func not support");
637 return;
638 }
639 std::thread catchThread = std::thread([] { MonitorCrashKernelSnapshot(); });
640 catchThread.detach();
641 }
642 } // namespace HiviewDFX
643 } // namespace OHOS
644