/* * Copyright (C) <2018> Intel Corporation * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include "log_sys.h" #include "crash_dump.h" #include "cmdutils.h" #include "fsutils.h" #include "strutils.h" #define GET_TID "/proc/%d/status" #define GET_COMM "/proc/%d/exe" #define GET_K_STACK "/proc/%d/stack" #define GET_MAPS "/proc/%d/maps" #define GET_OPEN_FILES "/proc/%d/fd" #define DEBUGGER_SIGNAL (__SIGRTMIN + 3) #define DUMP_FILE "/tmp/core" #define BUFFER_SIZE 8196 #define LINK_LEN 512 #define TID "Tgid:" /* 128 means the length of the DUMP_FILE */ #define FORMAT_LENGTH (LINK_LEN + 128) static void loginfo(int fd, const char *fmt, ...) { char *buf; va_list ap; int len, ret; va_start(ap, fmt); len = vasprintf(&buf, fmt, ap); va_end(ap); if (len == -1) { LOGE("write to buf failed\n"); return; } ret = write(fd, buf, len); if (ret != len) { LOGE("write in loginfo failed\n"); } free(buf); } static const char *get_signame(int sig) { switch (sig) { case SIGABRT: return "SIGABRT"; case SIGBUS: return "SIGBUS"; case SIGFPE: return "SIGFPE"; case SIGILL: return "SIGILL"; case SIGSEGV: return "SIGSEGV"; #if defined(SIGSTKFLT) case SIGSTKFLT: return "SIGSTKFLT"; #endif case SIGSTOP: return "SIGSTOP"; case SIGSYS: return "SIGSYS"; case SIGTRAP: return "SIGTRAP"; case DEBUGGER_SIGNAL: return ""; default: return "?"; } } /** * Save core dump to file. * @filename: core dump file name * return 0 on success, or -1 on error */ static int save_coredump(const char *filename) { size_t read_len; size_t write_len; char input_buffer[BUFFER_SIZE]; FILE *dump_file; /* open dump file in write and binary mode */ dump_file = fopen(filename, "wb"); if (dump_file == NULL) { LOGE("fopen core file failed\n"); return -1; } /* Read from STDIN and write to dump file */ while (1) { read_len = fread(input_buffer, 1, BUFFER_SIZE, stdin); if (read_len == 0) break; write_len = fwrite(input_buffer, 1, read_len, dump_file); if (write_len == 0) { LOGE("fwrite error\n"); fclose(dump_file); return -1; } } fclose(dump_file); return 0; } static int get_backtrace(int pid, int fd, int sig, const char *comm) { char *membkt; char format[FORMAT_LENGTH]; size_t len, ret; int flen; loginfo(fd, "\nBackTrace:\n\n"); memset(format, 0, sizeof(format)); if (sig == DEBUGGER_SIGNAL) { flen = snprintf(format, sizeof(format), "-p %d", pid); } else { flen = snprintf(format, sizeof(format), "%s %s", comm, DUMP_FILE); if (save_coredump(DUMP_FILE) == -1) { LOGE("save core file failed\n"); return -1; } } if (s_not_expect(flen, sizeof(format))) { LOGE("failed to generate format\n"); return -1; } len = exec_out2mem(&membkt, GET_GDB_INFO, format); if (len <= 0) { LOGE("get gdb info failed\n"); return -1; } ret = write(fd, membkt, len); free(membkt); if (ret != len) { LOGE("write file failed\n"); return -1; } return 0; } /** * Save process proc info to file. * @pid: process pid * @fd: usercrash file fd * @path: process proc path * @name: a string save to file * return 0 on success, or -1 on error */ static int save_proc_info(int pid, int fd, const char *path, const char *name) { int ret; char *data; char format[128]; unsigned long size; loginfo(fd, "\n%s:\n\n", name); memset(format, 0, sizeof(format)); ret = snprintf(format, sizeof(format), path, pid); if (s_not_expect(ret, sizeof(format))) { LOGE("failed to generate format"); return -1; } ret = read_file(format, &size, (void *)&data); if (ret) { LOGE("read file failed\n"); return -1; } ret = write(fd, data, size); if ((unsigned long)ret != size) { LOGE("write file failed\n"); return -1; } free(data); return 0; } static int get_openfiles(int pid, int fd, const char *path, const char *name) { int ret; int loop; int fdcount; char *fdname; char format[128]; char *files[256]; char linkname[LINK_LEN]; loginfo(fd, "\n%s:\n\n", name); memset(format, 0, sizeof(format)); ret = snprintf(format, sizeof(format), path, pid); if (s_not_expect(ret, sizeof(format))) { LOGE("failed to generate format"); return -1; } fdcount = lsdir(format, files, ARRAY_SIZE(files)); if (fdcount < 0) { LOGE("get fd list failed\n"); return -1; } for (loop = 2; loop < fdcount; loop++) { memset(linkname, 0, LINK_LEN); ret = readlink(files[loop], linkname, LINK_LEN); if (ret < 0 || ret >= LINK_LEN) { LOGE("get fd link fd failed\n"); continue; } fdname = strrchr(files[loop], '/') + 1; loginfo(fd, "%s -> %s\n", fdname, linkname); } for (loop = 0; loop < fdcount; loop++) free(files[loop]); return 0; } static int save_usercrash_file(int pid, int tgid, const char *comm, int sig, int out_fd) { int ret; loginfo(out_fd, "*** *** *** *** *** *** *** *** *** *** *** *** *** "); loginfo(out_fd, "*** *** ***\n"); loginfo(out_fd, "pid: %d, tgid: %d, comm: %s\n\n\n", pid, tgid, comm); loginfo(out_fd, "signal: %d (%s)\n", sig, get_signame(sig)); ret = save_proc_info(pid, out_fd, GET_K_STACK, "Stack"); if (ret) { LOGE("save stack failed\n"); return -1; } ret = save_proc_info(pid, out_fd, GET_MAPS, "Maps"); if (ret) { LOGE("save maps failed\n"); return -1; } ret = get_openfiles(pid, out_fd, GET_OPEN_FILES, "Open files"); if (ret) { LOGE("save openfiles failed\n"); return -1; } ret = get_backtrace(pid, out_fd, sig, comm); if (ret) { LOGE("save backtrace failed\n"); return -1; } return 0; } static int get_key_value(int pid, const char *path, const char *key, const size_t klen, char *value, const size_t value_len) { int len; int ret; unsigned long size; char *data; char *msg; char *start; char *end; char format[128]; memset(format, 0, sizeof(format)); ret = snprintf(format, sizeof(format), path, pid); if (s_not_expect(ret, sizeof(format))) { LOGE("failed to generate format"); return -1; } ret = read_file(format, &size, (void *)&data); if (ret || !data) { LOGE("read file failed\n"); return -1; } msg = strstr(data, key); if (!msg) { LOGE("find key:%s failed\n", key); free(data); return -1; } end = strchr(msg, '\n'); if (end == NULL) end = data + size; start = msg + klen; len = end - start; if (len >= (int)value_len) { free(data); return -1; } memcpy(value, start, len); *(value + len) = 0; free(data); return 0; } /** * Get and save process info to out_fd. * @pid: process pid * @sig: signal from core dump * @out_fd: file fd to save info */ void crash_dump(int pid, int sig, int out_fd) { int tgid; int ret; char comm[LINK_LEN]; char result[16]; char format[128]; memset(format, 0, sizeof(format)); ret = snprintf(format, sizeof(format), GET_COMM, pid); if (s_not_expect(ret, sizeof(format))) { LOGE("failed to generate format\n"); return; } ret = readlink(format, comm, LINK_LEN); if (ret < 0 || ret >= LINK_LEN) { LOGE("get process exe link failed\n"); return; } comm[ret] = '\0'; ret = get_key_value(pid, GET_TID, TID, strlen(TID), result, sizeof(result)); if (ret) { LOGE("get Tgid error\n"); return; } tgid = (int)strtol(result, NULL, 10); if (!sig) sig = DEBUGGER_SIGNAL; if (save_usercrash_file(pid, tgid, comm, sig, out_fd)) LOGE("dump log file failed\n"); }