/* * Copyright (C) 2018 Knowles Electronics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #define LOG_TAG "ia_crash_event_logger" #include #include #include "crash_analyzer.h" #define UEVENT_MSG_LEN (1024) #define BUF_SIZE (4096) #define CRASH_LOGGER_DEV "/dev/crashdump" #define REGDUMP_LOGGER_DEV "/dev/regdump" #define CRASH_DUMP_FILE_PREFIX "/data/data/dump_crash_" #define REG_ACCESS_FILE_PREFIX "/data/data/dump_reg_access_history_" #define CRASH_REASON_FILE_PREFIX "/data/data/dump_crash_reason_" #define SSR_CRASH_REASON_PREFIX "ia_dump_crash_reason_" #define BIN_EXTN ".bin" #define TXT_EXTN ".txt" #define MAX_FILENAME_LEN 512 #define MAX_TIMESTR_LEN 64 #define CRASH_DUMP_ANALYZER_MAX_STR_LEN 512 #define SSR_RAMDUMP_PREFIX "ramdump_audio_codec_" #define SSR_CRASH_FILE_PREFIX "ia_crash_dump_" #define SSR_REG_FILE_PREFIX "ia_reg_access_history_" #define SSR_DUMP_PATH "/data/vendor/ssrdump/" int g_exit_socket[2]; void sigint_handler(int sig __unused) { ALOGE("Interrupted, setting the exit condition"); if (g_exit_socket[0] >= 0) write(g_exit_socket[0], "T", 1); } char *crash_dump_split_file_names[] = {"/data/data/dump_debug_CM4_", "/data/data/dump_debug_HMD_", "/data/data/dump_debug_DMX_", "/data/data/dump_crash_CM4_", "/data/data/dump_crash_HMD_", "/data/data/dump_crash_DMX_", "/data/data/dump_crash_SSP_RAM0_", "/data/data/dump_crash_SSP_RAM1_", "/data/data/dump_crash_SSP_ROM0_", CRASH_REASON_FILE_PREFIX }; char *ssr_crash_dump_split_file_names[] = { "ia_dump_debug_CM4_", "ia_dump_debug_HMD_", "ia_dump_debug_DMX_", "ia_dump_crash_CM4_", "ia_dump_crash_HMD_", "ia_dump_crash_DMX_", "ia_dump_crash_SSP_RAM0_", "ia_dump_crash_SSP_RAM1_", "ia_dump_crash_SSP_ROM0_", SSR_CRASH_REASON_PREFIX }; void dump_crash_reason(const unsigned char *crash_dump_buf, const int crash_dump_len, const unsigned char *crash_reason_buf, const int crash_reason_len, const char *time_stamp, bool is_ssr) { FILE *out_fp = NULL; char file_name[MAX_FILENAME_LEN] = {0}; char crash_dump_analyzer_str[CRASH_DUMP_ANALYZER_MAX_STR_LEN] = {0}; int len = 0; const char *crash_dump_title = " crash_analysis:"; if (is_ssr) { snprintf(file_name, MAX_FILENAME_LEN, "%s%s%s%s%s", SSR_DUMP_PATH, SSR_RAMDUMP_PREFIX, SSR_CRASH_REASON_PREFIX, time_stamp, BIN_EXTN); } else { snprintf(file_name, MAX_FILENAME_LEN, "%s%s%s", CRASH_REASON_FILE_PREFIX, time_stamp, TXT_EXTN); } out_fp = fopen(file_name, "w"); if (out_fp == NULL) { ALOGE("Failed to open %s for writting", file_name); goto exit; } len = strnlen((const char *)crash_reason_buf, crash_reason_len); if (fwrite(crash_reason_buf, 1, len, out_fp) != len) { ALOGE("%s: ERROR writing to CRASH REASON FILE", __func__); goto exit; } len = analyse_crash_info( crash_dump_buf, crash_dump_len, crash_dump_analyzer_str, CRASH_DUMP_ANALYZER_MAX_STR_LEN); if (len > 0) { fwrite(crash_dump_title, 1, strlen(crash_dump_title), out_fp); fwrite(crash_dump_analyzer_str, 1, strlen(crash_dump_analyzer_str), out_fp); } ALOGI("Crash logs saved to %s", file_name); exit: if (out_fp != NULL) { fclose(out_fp); } } int split_crash_dump_buffer(unsigned char *buf, const int len, const char* time_stamp) { unsigned int file_index = 0, size = 0, tot_len = 0, flen = 0; int fcount = 0; unsigned char *ptr = NULL; FILE *fp = NULL; char file_name[MAX_FILENAME_LEN] = {0}; int number_crashdump_files = sizeof(crash_dump_split_file_names) / sizeof(crash_dump_split_file_names[0]); if (buf == NULL || time_stamp == NULL || len <= 0) { ALOGE("%s: Bad parameters", __func__); return -1; } while ((tot_len + STEP_LENGTH - 1 < len) && (fcount++ < number_crashdump_files)) { file_index = buf[tot_len]; size = buf[tot_len + 8] | buf[tot_len + 9] << 8 | buf[tot_len + 10] << 16 | buf[tot_len + 11] << 24; tot_len += STEP_LENGTH; if (file_index >= number_crashdump_files || size > len - tot_len) { continue; } /* Some special handling is needed for crash reason file */ if (!strcmp(crash_dump_split_file_names[file_index], CRASH_REASON_FILE_PREFIX)) { dump_crash_reason(buf, len, buf + tot_len, size, time_stamp, false); } else { snprintf(file_name, MAX_FILENAME_LEN, "%s%s%s", crash_dump_split_file_names[file_index], time_stamp, BIN_EXTN); fp = fopen(file_name, "w+"); ptr = buf + tot_len; flen = fwrite(ptr , 1, size, fp); tot_len += size; fclose(fp); ALOGI("Crash logs saved to %s", file_name); } } return 0; } int split_crash_dump_file (const char* crash_dump_filename, const char* time_stamp) { int fd, fil_len; FILE *fp; struct stat st; unsigned char *buf; int len,ret ; fp = fopen(crash_dump_filename, "r"); if (!fp) { ALOGE("File open error %s \n", crash_dump_filename); return -1; } fd = fileno(fp); fstat(fd, &st); fil_len = st.st_size; buf = (unsigned char*) malloc(fil_len); if (NULL == buf) { ALOGE("Failed to allocate buffer exiting"); ret = -1; goto exit; } len = fread(buf,1, fil_len, fp); if (len <=0) { ALOGE("file reading error %s\n", crash_dump_filename); ret = -1; goto exit; } ret = split_crash_dump_buffer(buf, len, time_stamp); exit: if (fp) fclose (fp); if (buf) free(buf); return ret; } void dump_crash_log() { void *buf = NULL; int inp_fp = -1, out_fp = -1; int bytes_read = 0; int err = 0; time_t t; struct tm *tm; char file_name[MAX_FILENAME_LEN]; char curr_time[MAX_TIMESTR_LEN]; buf = malloc(BUF_SIZE); if (NULL == buf) { ALOGE("Failed to allocate buffer exiting"); err = -1; goto exit; } inp_fp = open(CRASH_LOGGER_DEV, O_RDONLY); if (inp_fp == -1) { ALOGE("Failed to open %s with error %d(%s)", CRASH_LOGGER_DEV, errno, strerror(errno)); goto exit; } strcpy(file_name, CRASH_DUMP_FILE_PREFIX); t = time(NULL); tm = localtime(&t); strftime(curr_time, 64, "%F_%H_%M_%S", tm); strcat(file_name, curr_time); strcat(file_name, BIN_EXTN); out_fp = open(file_name, O_WRONLY | O_CREAT, 0644); if (out_fp == -1) { ALOGE("Failed to open %s for writing", file_name); goto exit; } do { bytes_read = read(inp_fp, buf, BUF_SIZE); if (bytes_read > 0) write(out_fp, buf, bytes_read); } while (bytes_read > 0); ALOGI("Crash logs has been dumped to %s", file_name); close(out_fp); out_fp = -1; close(inp_fp); inp_fp = -1; free(buf); buf = NULL; split_crash_dump_file(file_name, curr_time); exit: if (out_fp != -1) { close(out_fp); } if (inp_fp != -1) { close(inp_fp); } if (buf) { free(buf); } } void dump_reg_access_hist_log() { void *buf = NULL; int inp_fp = -1, out_fp = -1; int bytes_read = 0; int err = 0; time_t t; struct tm *tm; char file_name[MAX_FILENAME_LEN]; char curr_time[MAX_TIMESTR_LEN]; buf = malloc(BUF_SIZE); if (!buf) { ALOGE("Failed to allocate buffer exiting"); err = -1; goto exit; } inp_fp = open(REGDUMP_LOGGER_DEV, O_RDONLY); if (inp_fp == -1) { ALOGE("Failed to open %s with error %d(%s)", REGDUMP_LOGGER_DEV, errno, strerror(errno)); goto exit; } strcpy(file_name, REG_ACCESS_FILE_PREFIX); t = time(NULL); tm = localtime(&t); strftime(curr_time, 64, "%F_%H_%M_%S", tm); strcat(file_name, curr_time); strcat(file_name, TXT_EXTN); out_fp = open(file_name, O_WRONLY | O_CREAT, 0644); if (out_fp == -1) { ALOGE("Failed to open %s for writing", file_name); goto exit; } do { bytes_read = read(inp_fp, buf, BUF_SIZE); if (bytes_read > 0) write(out_fp, buf, bytes_read); } while (bytes_read > 0); ALOGI("Register access history has been dumped to %s", file_name); exit: if (out_fp != -1) { close(out_fp); } if (inp_fp != -1) { close(inp_fp); } if (buf) { free(buf); } } /* --- functions for SSR detector ---*/ int ssr_split_bin(unsigned char *buf, int len, const char* time_stamp) { unsigned int file_index = 0, size = 0, tot_len = 0, flen = 0; unsigned char *ptr = NULL; char file_name[MAX_FILENAME_LEN] = {0}; FILE *fp = NULL; int fcount = 0; int number_crashdump_files = sizeof(ssr_crash_dump_split_file_names) / sizeof(ssr_crash_dump_split_file_names[0]); if (buf == NULL || time_stamp == NULL || len <= 0) { ALOGE("%s: Bad parameters", __func__); return -1; } while ((tot_len + STEP_LENGTH - 1 < len) && (fcount++ < number_crashdump_files)) { file_index = buf[tot_len]; size = buf[tot_len + 8] | buf[tot_len + 9] << 8 | buf[tot_len + 10] << 16 | buf[tot_len + 11] << 24 ; tot_len += STEP_LENGTH; if (file_index >= number_crashdump_files || size > len - tot_len) { continue; } /* Some special handling is needed for crash reason file */ if (!strcmp(ssr_crash_dump_split_file_names[file_index], SSR_CRASH_REASON_PREFIX)) { dump_crash_reason(buf, len, buf + tot_len, size, time_stamp, true); continue; } snprintf(file_name, MAX_FILENAME_LEN, "%s%s%s%s%s", SSR_DUMP_PATH, SSR_RAMDUMP_PREFIX, ssr_crash_dump_split_file_names[file_index], time_stamp, BIN_EXTN); fp = fopen(file_name, "w+"); ptr = buf + tot_len; flen = fwrite(ptr, 1, size, fp); tot_len += size; fclose(fp); ALOGI("SSR Crash logs saved to %s", file_name); } return 0; } int ssr_split_crash_dump_file(const char* crash_dump_filename, const char* time_stamp) { int fd = -1, fil_len = 0; FILE *fp = NULL; struct stat st; unsigned char *buf = NULL; int len = 0, ret = 0; fp = fopen(crash_dump_filename, "r"); if (!fp) { ALOGE("File open error %s \n", crash_dump_filename); return -1; } fd = fileno(fp); fstat(fd, &st); fil_len = st.st_size; buf = (unsigned char *)malloc(fil_len); if (NULL == buf) { ALOGE("Failed to allocate buffer exiting"); ret = -1; goto exit; } len = fread(buf, 1, fil_len, fp); if (len <= 0) { ALOGE("file reading error %s\n", crash_dump_filename); ret = -1; goto exit; } ret = ssr_split_bin(buf, len, time_stamp); exit: if (fp) { fclose(fp); } if (buf) { free(buf); } return ret; } void ssr_copy_log(const char* src_path, const char* dis_path) { int src_fp = -1, dis_fp = -1; int bytes_read = 0; void *temp_buf = NULL; // allocate temp buf temp_buf = malloc(BUF_SIZE); if (!temp_buf) { ALOGE("Failed to allocate buffer exiting"); goto exit; } // open src file src_fp = open(src_path, O_RDONLY); if (src_fp == -1) { ALOGE("Failed to open %s with error %d(%s)", src_path, errno, strerror(errno)); goto exit; } // open dis file and append dis_fp = open(dis_path, O_CREAT | O_SYNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (dis_fp == -1) { ALOGE("Failed to open %s with error %d(%s)", dis_path, errno, strerror(errno)); goto exit; } // copy data do { bytes_read = read(src_fp, temp_buf, BUF_SIZE); if (bytes_read > 0) write(dis_fp, temp_buf, bytes_read); } while (bytes_read > 0); ALOGI("Write data successfully from %s to %s", src_path, dis_path); exit: if (src_fp != -1) { close(src_fp); } if (dis_fp != -1) { close(dis_fp); } if (temp_buf) { free(temp_buf); } return; } void check_crash_reason_file(const char *time_stamp) { FILE *out_fp = NULL; char file_name[MAX_FILENAME_LEN] = {0}; const char *default_crash_reason = "Iaxxx Firmware Crashed"; snprintf(file_name, MAX_FILENAME_LEN, "%s%s%s%s%s", SSR_DUMP_PATH, SSR_RAMDUMP_PREFIX, SSR_CRASH_REASON_PREFIX, time_stamp, BIN_EXTN); if (access(file_name, F_OK) == -1) { ALOGE("Write default crash reason into the crash reason file"); out_fp = fopen(file_name, "w"); if (out_fp == NULL) { ALOGE("%s: Failed to open: %s , errno: %s", __func__, file_name, strerror(errno)); return; } fwrite(default_crash_reason, 1, strlen(default_crash_reason), out_fp); fclose(out_fp); } } void ssr_dump_log() { time_t t; struct tm *tm = NULL; char curr_time_for_property[MAX_TIMESTR_LEN] = {0}; char curr_time_for_dump[MAX_TIMESTR_LEN] = {0}; char out_crash_file_name[MAX_FILENAME_LEN] = {0}; char out_reg_file_name[MAX_FILENAME_LEN] = {0}; // get current time t = time(NULL); tm = localtime(&t); snprintf(curr_time_for_property, MAX_TIMESTR_LEN, "%.4d-%.2d-%.2d %.2d-%.2d-%.2d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); snprintf(curr_time_for_dump, MAX_TIMESTR_LEN, "%.02d-%.02d-%.02d_%.02d-%.02d-%.02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); // strftime(curr_time_for_dump, MAX_TIMESTR_LEN, "%F_%H_%M_%S", tm); // set property property_set("vendor.debug.ssrdump.subsys", "audio_codec"); property_set("vendor.debug.ssrdump.timestamp", curr_time_for_property); // copy crash log only snprintf(out_crash_file_name, MAX_FILENAME_LEN, "%s%s%s%s%s", SSR_DUMP_PATH, SSR_RAMDUMP_PREFIX, SSR_CRASH_FILE_PREFIX, curr_time_for_dump, BIN_EXTN); ssr_copy_log(CRASH_LOGGER_DEV, out_crash_file_name); ssr_split_crash_dump_file(out_crash_file_name, curr_time_for_dump); ALOGI("Crash logs has been dumped to %s", out_crash_file_name); // copy reg snprintf(out_reg_file_name, MAX_FILENAME_LEN, "%s%s%s%s%s", SSR_DUMP_PATH, SSR_RAMDUMP_PREFIX, SSR_REG_FILE_PREFIX, curr_time_for_dump, TXT_EXTN); ssr_copy_log(REGDUMP_LOGGER_DEV, out_reg_file_name); ALOGI("Register access history has been dumped %s", out_reg_file_name); // Check the crash reason file check_crash_reason_file(curr_time_for_dump); } /* --- main function --- */ int main(int argc, char** argv) { int err = 0; int timeout = -1; // Wait for event indefinitely struct pollfd fds[2]; char msg[UEVENT_MSG_LEN]; int i, n; bool ssr_monitor = false; if ((argc == 2) && !strcmp(argv[1], "-m")) { ALOGD("Monitor the crash logs"); (void)umask(S_IWGRP | S_IWOTH); ssr_monitor = true; } signal(SIGINT, sigint_handler); if ( (argc == 2) && !strcmp(argv[1], "-f")) { ALOGD("Read to get the crash logs"); dump_reg_access_hist_log(); dump_crash_log(); return 0; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, g_exit_socket) == -1) { ALOGE("%s: Failed to create termination socket", __func__); err = -1; goto exit; } fds[0].events = POLLIN; fds[0].fd = uevent_open_socket(64*1024, true); if (fds[0].fd == -1) { ALOGE("Error opening socket for hotplug uevent errno %d(%s)", errno, strerror(errno)); goto exit; } fds[1].events = POLLIN; fds[1].fd = g_exit_socket[1]; while (1) { err = poll (fds, 2, timeout); if (fds[0].revents & POLLIN) { n = uevent_kernel_multicast_recv(fds[0].fd, msg, UEVENT_MSG_LEN); if (n <= 0) { continue; } for (i = 0; i < n;) { if (strstr(msg + i, "IAXXX_CRASH_EVENT")) { ALOGD("IAXXX_CRASH_EVENT received trying to get the crash logs"); if (ssr_monitor) { ssr_dump_log(); } else { dump_reg_access_hist_log(); dump_crash_log(); } } i += strlen(msg + i) + 1; } } else if (fds[1].revents & POLLIN) { read(fds[1].fd, &n, sizeof(n)); /* clear the socket */ ALOGE("Interrupt received, exiting"); break; } else { ALOGI("Message ignored"); } } exit: if (g_exit_socket[0] >= 0) { close(g_exit_socket[0]); } if (g_exit_socket[1] >= 0) { close(g_exit_socket[1]); } return err; }