From a5853d6d8a5630c190f3b142c17e4ab261efeac5 Mon Sep 17 00:00:00 2001 From: "Liu, Xinwu" Date: Tue, 3 Jul 2018 10:08:14 +0800 Subject: [PATCH] tools:acrn-crashlog: Improve the process of crash reclassify 1. Reload the trigger file(s) of subcrash while doing crash reclassify. 2. Support simple format for trigger file. Signed-off-by: Liu, Xinwu Acked-by: Chen Gang --- .../acrn-crashlog/acrnprobe/android_events.c | 4 + .../acrnprobe/crash_reclassify.c | 177 +++++--- tools/acrn-crashlog/acrnprobe/history.c | 4 +- .../acrnprobe/include/load_conf.h | 4 +- .../acrnprobe/include/probeutils.h | 3 + tools/acrn-crashlog/acrnprobe/probeutils.c | 14 +- tools/acrn-crashlog/acrnprobe/sender.c | 407 +++++++----------- tools/acrn-crashlog/common/cmdutils.c | 12 +- tools/acrn-crashlog/common/fsutils.c | 283 ++++++++++-- tools/acrn-crashlog/common/include/cmdutils.h | 8 +- tools/acrn-crashlog/common/include/fsutils.h | 16 +- tools/acrn-crashlog/data/acrnprobe.xml | 8 +- 12 files changed, 571 insertions(+), 369 deletions(-) diff --git a/tools/acrn-crashlog/acrnprobe/android_events.c b/tools/acrn-crashlog/acrnprobe/android_events.c index c9665edb6..81e24067d 100644 --- a/tools/acrn-crashlog/acrnprobe/android_events.c +++ b/tools/acrn-crashlog/acrnprobe/android_events.c @@ -400,6 +400,7 @@ static void get_last_line_synced(const struct sender_t *sender) int sid; int ret; struct vm_t *vm; + char *p; char vmkey[ANDROID_WORD_LEN]; char vm_name[32]; @@ -438,6 +439,9 @@ static void get_last_line_synced(const struct sender_t *sender) strerror(errno)); continue; } + p = strchr(vmkey, ' '); + if (p) + *p = 0; ret = refresh_key_synced_stage1(sender, vm, vmkey, MM_ONLY); if (ret < 0) { diff --git a/tools/acrn-crashlog/acrnprobe/crash_reclassify.c b/tools/acrn-crashlog/acrnprobe/crash_reclassify.c index 1eabd4458..a966f2b4d 100644 --- a/tools/acrn-crashlog/acrnprobe/crash_reclassify.c +++ b/tools/acrn-crashlog/acrnprobe/crash_reclassify.c @@ -27,7 +27,7 @@ * * @return 1 if find the same string, or 0 if not. */ -static int has_content(char *file, char *content) +static int has_content(const char *file, const char *content) { if (content && strstr(file, content)) return 1; @@ -44,12 +44,12 @@ static int has_content(char *file, char *content) * * @return 1 if all configured strings were found, or 0 if not. */ -static int crash_has_all_contents(struct crash_t *crash, - char *file) +static int crash_has_all_contents(const struct crash_t *crash, + const char *file) { int id; int ret = 1; - char *content; + const char *content; for_each_content_crash(id, content, crash) { if (!content) @@ -78,14 +78,14 @@ static int crash_has_all_contents(struct crash_t *crash, * * @return 1 if result is true, or 0 if false. */ -static int crash_has_mightcontents(struct crash_t *crash, - char *file) +static int crash_has_mightcontents(const struct crash_t *crash, + const char *file) { int ret = 1; int ret_exp; int expid, cntid; - char **exp; - char *content; + char * const *exp; + const char *content; for_each_expression_crash(expid, exp, crash) { if (!exp || !exp_valid(exp)) @@ -119,13 +119,14 @@ static int crash_has_mightcontents(struct crash_t *crash, * * @return 1 if file matches these strings configured in crash, or 0 if not. */ -static int crash_reclassify(struct crash_t *crash, char *file) +static int crash_match_content(const struct crash_t *crash, const char *file) { return crash_has_all_contents(crash, file) && crash_has_mightcontents(crash, file); } -static int _get_data(char *file, struct crash_t *crash, char **data, int index) +static int _get_data(const char *file, const struct crash_t *crash, + char **data, const int index) { char *search_key; char *value; @@ -172,7 +173,7 @@ static int _get_data(char *file, struct crash_t *crash, char **data, int index) * * @return 0 if successful, or errno if not. */ -static int get_data(char *file, struct crash_t *crash, +static int get_data(const char *file, const struct crash_t *crash, char **data0, char **data1, char **data2) { int res; @@ -201,6 +202,62 @@ fail: return res; } +static struct crash_t *crash_find_matched_child(const struct crash_t *crash, + const char *rtrfmt) +{ + struct crash_t *child; + struct crash_t *matched_child = NULL; + int i; + int count; + int res; + const char *trfile_fmt; + char **trfiles; + void *content; + unsigned long size; + + if (!crash) + return NULL; + + for_crash_children(child, crash) { + if (!child->trigger) + continue; + + if (!strcmp(child->trigger->type, "dir")) + trfile_fmt = rtrfmt; + else + trfile_fmt = child->trigger->path; + + count = config_fmt_to_files(trfile_fmt, &trfiles); + if (count <= 0) + continue; + + for (i = 0; i < count; i++) { + res = read_file(trfiles[i], &size, &content); + if (res == -1) { + LOGE("read %s failed, error (%s)\n", + trfiles[i], strerror(errno)); + continue; + } + if (crash_match_content(child, content)) { + free(content); + matched_child = child; + break; + } + free(content); + } + + for (i = 0; i < count; i++) + free(trfiles[i]); + free(trfiles); + + if (matched_child) + break; + } + + /* It returns the first matched crash */ + return matched_child; +} + /** * Judge the crash type. We only got a root crash from channel, sometimes, * we need to calculate a more specific type. @@ -208,7 +265,7 @@ fail: * This function couldn't use for binary file. * * @param rcrash Root crash obtained from channel. - * @param trfile Path of trigger file. + * @param rtrfile_fmt Path fmt of trigger file of root crash. * @param[out] data0 Searched result, according to 'data0' configuread in crash. * @param[out] data1 Searched result, according to 'data1' configuread in crash. * @param[out] data2 Searched result, according to 'data2' configuread in crash. @@ -216,64 +273,64 @@ fail: * @return a pointer to the calculated crash structure if successful, * or NULL if not. */ -static struct crash_t *crash_reclassify_by_content(struct crash_t *rcrash, - char *trfile, char **data0, - char **data1, char **data2) +static struct crash_t *crash_reclassify_by_content(const struct crash_t *rcrash, + const char *rtrfile_fmt, char **data0, + char **data1, char **data2) { - int depth; - int level; - int ret; - struct crash_t *crash; - struct crash_t *ret_crash = NULL; - void *file; + int count; + const struct crash_t *crash; + const struct crash_t *ret_crash = rcrash; + const char *trfile_fmt; + char **trfiles; + void *content; unsigned long size; - int id; + int res; + int i; if (!rcrash) return NULL; - if (trfile) { - ret = read_file(trfile, &size, &file); - if (ret == -1) { - LOGE("read %s failed, error (%s)\n", - trfile, strerror(errno)); - return NULL; - } - } else - return rcrash; + crash = rcrash; - /* traverse every crash from leaf, return the first crash we find - * consider that we have few CRASH TYPE, so just using this simple - * implementation. - */ - depth = crash_depth(rcrash); - for (level = depth; level >= 0; level--) { - for_each_crash(id, crash, conf) { - if (!crash || - crash->trigger != rcrash->trigger || - crash->channel != rcrash->channel || - crash->level != level) - continue; + while (1) { + crash = crash_find_matched_child(crash, rtrfile_fmt); + if (!crash) + break; - if (crash_reclassify(crash, file)) { - ret = get_data(file, crash, data0, data1, - data2); - if (ret < 0) { - LOGE("get data error, error (%s)\n", - strerror(-ret)); - goto fail_data; - } else { - ret_crash = crash; - goto fail_data; - } - } - } + ret_crash = crash; } -fail_data: - free(file); + if (!strcmp(ret_crash->trigger->type, "dir")) + trfile_fmt = rtrfile_fmt; + else + trfile_fmt = ret_crash->trigger->path; - return ret_crash; + count = config_fmt_to_files(trfile_fmt, &trfiles); + if (count <= 0) + return (struct crash_t *)ret_crash; + + /* get data from last file */ + res = read_file(trfiles[count - 1], &size, &content); + if (res == -1) { + LOGE("read %s failed, error (%s)\n", + trfiles[count - 1], strerror(errno)); + goto free_files; + } + + res = get_data(content, ret_crash, data0, data1, data2); + if (res < 0) { + LOGE("get data error, error (%s)\n", + strerror(res)); + } + + free(content); + +free_files: + for (i = 0; i < count; i++) + free(trfiles[i]); + free(trfiles); + + return (struct crash_t *)ret_crash; } /** @@ -286,7 +343,7 @@ void init_crash_reclassify(void) struct crash_t *crash; for_each_crash(id, crash, conf) { - if (!crash || !is_root_crash(crash)) + if (!crash) continue; crash->reclassify = crash_reclassify_by_content; diff --git a/tools/acrn-crashlog/acrnprobe/history.c b/tools/acrn-crashlog/acrnprobe/history.c index 24cae832d..f0e099f6c 100644 --- a/tools/acrn-crashlog/acrnprobe/history.c +++ b/tools/acrn-crashlog/acrnprobe/history.c @@ -118,7 +118,7 @@ void hist_raise_event(char *event, char *type, char *log, char *lastuptime, char *key) { char line[MAXLINESIZE]; - char eventtime[32]; + char eventtime[LONG_TIME_SIZE]; struct sender_t *crashlog; int maxlines; int ret; @@ -158,7 +158,7 @@ void hist_raise_event(char *event, char *type, char *log, char *lastuptime, void hist_raise_uptime(char *lastuptime) { - char boot_time[24]; + char boot_time[UPTIME_SIZE]; char firstline[MAXLINESIZE]; int hours; int ret; diff --git a/tools/acrn-crashlog/acrnprobe/include/load_conf.h b/tools/acrn-crashlog/acrnprobe/include/load_conf.h index 3bcd536bd..b1d28f3e5 100644 --- a/tools/acrn-crashlog/acrnprobe/include/load_conf.h +++ b/tools/acrn-crashlog/acrnprobe/include/load_conf.h @@ -65,8 +65,8 @@ struct crash_t { int wd; int level; - struct crash_t *(*reclassify)(struct crash_t *, char*, char**, char**, - char**); + struct crash_t *(*reclassify)(const struct crash_t *, const char*, + char**, char**, char**); }; struct info_t { diff --git a/tools/acrn-crashlog/acrnprobe/include/probeutils.h b/tools/acrn-crashlog/acrnprobe/include/probeutils.h index a711a645b..1ee423757 100644 --- a/tools/acrn-crashlog/acrnprobe/include/probeutils.h +++ b/tools/acrn-crashlog/acrnprobe/include/probeutils.h @@ -22,6 +22,9 @@ #ifndef __PROBEUTILS_H__ #define __PROBEUTILS_H__ +#define UPTIME_SIZE 24 +#define LONG_TIME_SIZE 32 + enum e_dir_mode { MODE_CRASH = 0, MODE_STATS, diff --git a/tools/acrn-crashlog/acrnprobe/probeutils.c b/tools/acrn-crashlog/acrnprobe/probeutils.c index 1564581dd..41dccc742 100644 --- a/tools/acrn-crashlog/acrnprobe/probeutils.c +++ b/tools/acrn-crashlog/acrnprobe/probeutils.c @@ -50,7 +50,7 @@ unsigned long long get_uptime(void) return time_ns; } -int get_uptime_string(char newuptime[24], int *hours) +int get_uptime_string(char *newuptime, int *hours) { long long tm; int seconds, minutes; @@ -68,11 +68,11 @@ int get_uptime_string(char newuptime[24], int *hours) /* hours */ *hours /= 60; - return snprintf(newuptime, 24, "%04d:%02d:%02d", *hours, + return snprintf(newuptime, UPTIME_SIZE, "%04d:%02d:%02d", *hours, minutes, seconds); } -int get_current_time_long(char buf[32]) +int get_current_time_long(char *buf) { time_t t; struct tm *time_val; @@ -82,7 +82,7 @@ int get_current_time_long(char buf[32]) if (!time_val) return -1; - return strftime(buf, 32, "%Y-%m-%d/%H:%M:%S ", time_val); + return strftime(buf, LONG_TIME_SIZE, "%Y-%m-%d/%H:%M:%S ", time_val); } /** @@ -348,16 +348,18 @@ void generate_crashfile(char *dir, char *event, char *hashkey, { char *buf; char *path; - char datetime[32]; - char uptime[32]; + char datetime[LONG_TIME_SIZE]; + char uptime[UPTIME_SIZE]; int hours; int ret; const int fmtsize = 128; int filesize; + datetime[0] = 0; ret = get_current_time_long(datetime); if (ret <= 0) return; + uptime[0] = 0; get_uptime_string(uptime, &hours); filesize = fmtsize + strlen(event) + diff --git a/tools/acrn-crashlog/acrnprobe/sender.c b/tools/acrn-crashlog/acrnprobe/sender.c index 6a61c3443..c18a4f7d2 100644 --- a/tools/acrn-crashlog/acrnprobe/sender.c +++ b/tools/acrn-crashlog/acrnprobe/sender.c @@ -38,57 +38,62 @@ struct telemd_data_t { }; #endif -/* get_log_file_* only used to copy regular file which can be mmaped */ -static void get_log_file_complete(struct log_t *log, char *desdir) +static int cal_log_filepath(char **out, const struct log_t *log, + const char *srcname, const char *desdir) { - char *des; - char *name; - int ret; + const char *filename; + int need_timestamp = 0; + int hours; + char timebuf[UPTIME_SIZE]; - name = log->name; + if (!out || !log || !desdir) + return -1; - ret = asprintf(&des, "%s/%s", desdir, name); - if (ret < 0) { - LOGE("compute string failed, out of memory\n"); - return; + if (is_ac_filefmt(log->path)) + filename = srcname; + else + filename = log->name; + + if (!filename) + return -1; + + if (!strcmp(log->type, "cmd") || log->lines) + need_timestamp = 1; + + if (need_timestamp) { + timebuf[0] = 0; + get_uptime_string(timebuf, &hours); + return asprintf(out, "%s/%s_%s", desdir, filename, timebuf); } - ret = do_copy_tail(log->path, des, 0); - if (ret < 0) { - LOGE("copy (%s) failed\n, error (%s)\n", log->path, - strerror(errno)); - } - - free(des); + return asprintf(out, "%s/%s", desdir, filename); } -static void get_log_file_tail(struct log_t *log, char *desdir) +/* get_log_file_* only used to copy regular file which can be mmaped */ +static void get_log_file_complete(const char *despath, const char *srcpath) +{ + const int ret = do_copy_tail(srcpath, despath, 0); + + if (ret < 0) { + LOGE("copy (%s) failed, error (%s)\n", srcpath, + strerror(errno)); + } +} + +static void get_log_file_tail(const char *despath, const char *srcpath, + const int lines) { - char *des; - char timebuf[24]; - char *name; char *start; - int lines; - int hours; int start_line; int file_lines; struct mm_file_t *mfile; int ret; - lines = atoi(log->lines); - name = log->name; - get_uptime_string(timebuf, &hours); - ret = asprintf(&des, "%s/%s_%s", desdir, name, timebuf); - if (ret < 0) { - LOGE("compute string failed, out of memory\n"); - return; - } - - mfile = mmap_file(log->path); + mfile = mmap_file(srcpath); if (!mfile) { - LOGE("mmap (%s) failed, error (%s)\n", log->path, + LOGE("mmap (%s) failed, error (%s)\n", srcpath, strerror(errno)); - goto free; + return; } file_lines = mm_count_lines(mfile); if (file_lines <= 0) { @@ -97,184 +102,65 @@ static void get_log_file_tail(struct log_t *log, char *desdir) } start_line = MAX(file_lines - lines, 0) + 1; start = mm_get_line(mfile, start_line); - ret = overwrite_file(des, start); + ret = overwrite_file(despath, start); if (ret < 0) { LOGE("create file with (%s, %p) failed, error (%s)\n", - des, start, strerror(errno)); + despath, start, strerror(errno)); goto unmap; } unmap: unmap_file(mfile); -free: - free(des); } -static void get_log_file(struct log_t *log, char *desdir) +static void get_log_file(const char *despath, const char *srcpath, + const char *tail_lines) { int lines; - if (log->lines == NULL) { - get_log_file_complete(log, desdir); + if (!tail_lines) { + get_log_file_complete(despath, srcpath); return; } - lines = atoi(log->lines); + lines = atoi(tail_lines); if (lines > 0) - get_log_file_tail(log, desdir); + get_log_file_tail(despath, srcpath, lines); else - get_log_file_complete(log, desdir); + get_log_file_complete(despath, srcpath); } -static void get_log_rotation(struct log_t *log, char *desdir) +static void get_log_node(const char *despath, const char *nodepath) { - char *suffix; - char *prefix; - char *dir; - char *p; - int count; - char *files[512]; - int number; - int target_num = -1; - char *target_file = NULL; - char *name; - int i; + const int res = do_copy_eof(nodepath, despath); - dir = strdup(log->path); - if (!dir) { - LOGE("compute string failed, out of memory\n"); - return; - } - - /* dir prefix suffix - * | | | - * /tmp/hvlog/hvlog_cur.[biggest] - */ - p = strrchr(dir, '/'); - if (p == NULL) { - LOGE("invalid path (%s) in log (%s), ", dir, log->name); - LOGE("file_rotation only support absolute path\n"); - goto free_dir; - } else { - prefix = p + 1; - *p = 0; - } - - p = strstr(prefix, ".["); - if (p == NULL) { - LOGE("invalid path (%s) in log (%s)\n", log->path, log->name); - goto free_dir; - } else { - suffix = p + 2; - *p = 0; - } - - p = suffix + strlen(suffix) - 1; - if (*p == ']') { - *p = 0; - } else { - LOGE("invalid path (%s) in log (%s)\n", log->path, log->name); - goto free_dir; - } - - - struct log_t toget; - - memcpy(&toget, log, sizeof(toget)); - count = lsdir(dir, files, ARRAY_SIZE(files)); - if (count > 2) { - for (i = 0; i < count; i++) { - name = strrchr(files[i], '/') + 1; - if (!name && !strstr(name, prefix)) - continue; - - number = atoi(strrchr(name, '.') + 1); - if (!strncmp(suffix, "biggest", 7)) { - if (target_num == -1 || - number > target_num){ - target_file = files[i]; - target_num = number; - } - } else if (!strncmp(suffix, "smallest", 8)) { - if (target_num == -1 || - number < target_num) { - target_file = files[i]; - target_num = number; - } - } else if (!strncmp(suffix, "all", 3)) { - toget.path = files[i]; - toget.name = name; - get_log_file(&toget, desdir); - } - } - } else if (count < 0) { - LOGE("lsdir (%s) failed, error (%s)\n", dir, - strerror(-count)); - goto free; - } - - if (!strncmp(suffix, "all", 3)) - goto free; - - if (target_file) { - toget.path = target_file; - get_log_file(&toget, desdir); - } else { - LOGW("no logs found for (%s)\n", log->name); - goto free; - } - -free: - while (count > 0) - free(files[--count]); -free_dir: - free(dir); -} - -static void get_log_node(struct log_t *log, char *desdir) -{ - char *des; - char *name; - int ret; - - name = log->name; - ret = asprintf(&des, "%s/%s", desdir, name); - if (ret < 0) { - LOGE("compute string failed, out of memory\n"); - return; - } - - ret = do_copy_eof(log->path, des); - if (ret < 0) { - LOGE("copy (%s) failed, error (%s)\n", log->path, + if (res < 0) { + LOGE("copy (%s) failed, error (%s)\n", nodepath, strerror(errno)); - goto free; } - -free: - free(des); } -static void out_via_fork(struct log_t *log, char *desdir) +static void get_log_cmd(const char *despath, const char *cmd) { - char *des; - int ret; + const int res = exec_out2file(despath, cmd); - ret = asprintf(&des, "%s/%s", desdir, log->name); - if (ret < 0) { - LOGE("compute string failed, out of memory\n"); + if (res) + LOGE("get_log_by_cmd exec %s returns (%d)\n", cmd, res); +} + +static void get_log_by_type(const char *despath, const struct log_t *log, + const char *srcpath) +{ + if (!despath || !log || !srcpath) return; - } - exec_out2file(des, log->path); - free(des); + if (!strcmp("file", log->type)) + get_log_file(despath, srcpath, log->lines); + else if (!strcmp("node", log->type)) + get_log_node(despath, log->path); + else if (!strcmp("cmd", log->type)) + get_log_cmd(despath, log->path); } - -static void get_log_cmd(struct log_t *log, char *desdir) -{ - out_via_fork(log, desdir); -} - #ifdef HAVE_TELEMETRICS_CLIENT static int telemd_send_data(char *payload, char *eventid, uint32_t severity, char *class) @@ -322,70 +208,47 @@ fail: static void telemd_get_log(struct log_t *log, void *data) { - struct telemd_data_t *d = (struct telemd_data_t *)data; - char name[NAME_MAX]; - char *path, *msg; - int ret; + const struct telemd_data_t *d = (struct telemd_data_t *)data; + char fpath[PATH_MAX]; + char *msg; + int count; + int res; + int i; + struct dirent **filelist; if (d->srcdir == NULL) goto send_nologs; - ret = dir_contains(d->srcdir, log->name, 0, name); - if (ret == 1) { - ret = asprintf(&path, "%s/%s", d->srcdir, name); - if (ret < 0) { - LOGE("compute string failed, out of memory\n"); - return; - } - telemd_send_data(path, d->eventid, d->severity, d->class); - free(path); - } else if (ret == 0) { + /* search file which use log->name as substring */ + count = ac_scandir(d->srcdir, &filelist, filter_filename_substr, + log->name, NULL); + if (count < 0) { + LOGE("search (%s) in dir (%s) failed, error (%s)\n", log->name, + d->srcdir, strerror(count)); + return; + } + if (!count) { LOGE("dir (%s) does not contains (%s)\n", d->srcdir, log->name); goto send_nologs; - } else if (ret > 1) { - /* got multiple files */ - int i; - int count; - char *name; - char *files[512]; - - if (!strstr(log->path, ".[all]")) { - LOGE("dir (%s) contains (%d) files (%s)\n", - d->srcdir, ret, log->name); - goto send_nologs; - } - - count = lsdir(d->srcdir, files, ARRAY_SIZE(files)); - if (count > 2) { - for (i = 0; i < count; i++) { - name = strrchr(files[i], '/') + 1; - if (!strstr(name, log->name)) - continue; - - telemd_send_data(files[i], d->eventid, - d->severity, d->class); - } - } else if (count < 0) { - LOGE("lsdir (%s) failed, error (%s)\n", d->srcdir, - strerror(-count)); - goto send_nologs; - } - - while (count > 0) - free(files[--count]); - } else { - LOGE("search (%s) in dir (%s) failed, error (%s)\n", log->name, - d->srcdir, strerror(-ret)); - goto send_nologs; } + for (i = 0; i < count; i++) { + snprintf(fpath, sizeof(fpath), "%s/%s", d->srcdir, + filelist[i]->d_name); + free(filelist[i]); + telemd_send_data(fpath, d->eventid, + d->severity, d->class); + } + + free(filelist); + return; send_nologs: - ret = asprintf(&msg, "no log generated on %s, check probe's log.", + res = asprintf(&msg, "no log generated on %s, check probe's log.", log->name); - if (ret < 0) { + if (res < 0) { LOGE("compute string failed, out of memory\n"); return; } @@ -402,6 +265,8 @@ static void crashlog_get_log(struct log_t *log, void *data) unsigned long long start, end; int spent; int quota; + int res; + char *des; char *desdir = (char *)data; crashlog = get_sender_by_name("crashlog"); @@ -415,14 +280,53 @@ static void crashlog_get_log(struct log_t *log, void *data) } start = get_uptime(); - if (!strcmp("file", log->type)) - get_log_file(log, desdir); - else if (!strcmp("node", log->type)) - get_log_node(log, desdir); - else if (!strcmp("cmd", log->type)) - get_log_cmd(log, desdir); - else if (!strcmp("file_rotation", log->type)) - get_log_rotation(log, desdir); + if (is_ac_filefmt(log->path)) { + int i; + char **files; + char *name; + + const int count = config_fmt_to_files(log->path, &files); + + if (count < 0) { + LOGE("parse config format (%s) failed, error (%s)\n", + log->path, strerror(count)); + return; + } + if (!count) { + LOGW("no logs found for (%s)\n", log->name); + return; + } + + for (i = 0; i < count; i++) { + name = strrchr(files[i], '/') + 1; + if (name == (char *)1) { + LOGE("invalid path (%s) in log (%s)", files[i], + log->name); + continue; + } + res = cal_log_filepath(&des, log, name, desdir); + if (res == -1) { + LOGE("cal_log_filepath failed, error (%s)\n", + strerror(errno)); + continue; + } + get_log_by_type(des, log, files[i]); + free(des); + } + + for (i = 0; i < count; i++) + free(files[i]); + free(files); + } else { + res = cal_log_filepath(&des, log, log->name, desdir); + if (res == -1) { + LOGE("cal_log_filepath failed, error (%s)\n", + strerror(errno)); + return; + } + get_log_by_type(des, log, log->path); + free(des); + } end = get_uptime(); spent = (int)((end - start) / 1000000000LL); @@ -560,7 +464,7 @@ static void telemd_send_uptime(void) struct sender_t *telemd; struct uptime_t *uptime; char *class; - char boot_time[24]; + char boot_time[UPTIME_SIZE]; int hours; int ret; static int uptime_hours; @@ -812,22 +716,21 @@ static void crashlog_send_crash(struct event_t *e) struct sender_t *crashlog; char *key = NULL; char *trfile = NULL; - char *data0; - char *data1; - char *data2; + char *data0 = NULL; + char *data1 = NULL; + char *data2 = NULL; int id; int ret = 0; int quota; struct crash_t *rcrash = (struct crash_t *)e->private; - if (!strcmp(rcrash->trigger->type, "file")) - ret = asprintf(&trfile, "%s", rcrash->trigger->path); - else if (!strcmp(rcrash->trigger->type, "dir")) + if (!strcmp(rcrash->trigger->type, "dir")) { ret = asprintf(&trfile, "%s/%s", rcrash->trigger->path, e->path); - if (ret < 0) { - LOGE("compute string failed, out of memory\n"); - return; + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + return; + } } crash = rcrash->reclassify(rcrash, trfile, &data0, &data1, &data2); diff --git a/tools/acrn-crashlog/common/cmdutils.c b/tools/acrn-crashlog/common/cmdutils.c index 55fd4614d..d2b62480f 100644 --- a/tools/acrn-crashlog/common/cmdutils.c +++ b/tools/acrn-crashlog/common/cmdutils.c @@ -35,7 +35,7 @@ * If all system calls succeed, then the return value is the * termination status of the child process used to execute command. */ -int execv_out2file(char *argv[], char *outfile) +int execv_out2file(char * const argv[], const char *outfile) { pid_t pid; @@ -103,13 +103,13 @@ int execv_out2file(char *argv[], char *outfile) return -1; } -int debugfs_cmd(char *loop_dev, char *cmd, char *outfile) +int debugfs_cmd(const char *loop_dev, const char *cmd, const char *outfile) { - char *argv[5] = {"debugfs", "-R", NULL, NULL, 0}; + const char *argv[5] = {"debugfs", "-R", NULL, NULL, 0}; argv[2] = cmd; argv[3] = loop_dev; - return execv_out2file(argv, outfile); + return execv_out2file((char * const *)argv, outfile); } /** @@ -128,7 +128,7 @@ int debugfs_cmd(char *loop_dev, char *cmd, char *outfile) * If all system calls succeed, then the return value is the * termination status of the child process used to execute command. */ -int exec_out2file(char *outfile, char *fmt, ...) +int exec_out2file(const char *outfile, const char *fmt, ...) { va_list args; char *cmd; @@ -184,7 +184,7 @@ int exec_out2file(char *outfile, char *fmt, ...) * * @return a pointer to command's output if successful, or NULL if not. */ -char *exec_out2mem(char *fmt, ...) +char *exec_out2mem(const char *fmt, ...) { va_list args; char *cmd; diff --git a/tools/acrn-crashlog/common/fsutils.c b/tools/acrn-crashlog/common/fsutils.c index ccfda9764..63597b586 100644 --- a/tools/acrn-crashlog/common/fsutils.c +++ b/tools/acrn-crashlog/common/fsutils.c @@ -300,7 +300,7 @@ void unmap_file(struct mm_file_t *mfile) * @return The number of bytes written to new file if successful, * or a negative errno-style value if not. */ -int do_copy_tail(char *src, char *dest, int limit) +int do_copy_tail(const char *src, const char *dest, int limit) { int rc = 0; int fsrc = -1, fdest = -1; @@ -916,41 +916,130 @@ int file_read_key_value_r(const char *path, const char *key, return _file_read_key_value(path, 'r', key, limit, value); } -int dir_contains(const char *dir, const char *filename, int exact, - char *fullname) +/** + * Because scandir's filter can't receive caller's parameter, so + * rewrite an ac_scandir to satisfy our usage. This function is + * very like scandir, except it has an additional parameter farg for + * filter. + * + * @param dirp Dir to scan. + * @param namelist Andress to receive result array of struct dirent. + * @param filter Function pointer to filter. See also scandir. + * @param farg The second arg of filter. + * @param compar See scandir. + * + * @return the count of scanned files if successful, or a negative + * errno-style value if not. + */ +int ac_scandir(const char *dirp, struct dirent ***namelist, + int (*filter)(const struct dirent *, const void *), + const void *farg, + int (*compar)(const struct dirent **, + const struct dirent **)) { - int ret, count = 0; + int i; + int count = 0; + int index = 0; + struct dirent **_filelist; + + if (!dirp || !namelist) + return -EINVAL; + + const int res = scandir(dirp, &_filelist, NULL, compar); + + if (!filter) { + *namelist = _filelist; + return res; + } + if (res == -1) + return -errno; + + /* overwrite filter */ + /* calculate the matched files, free unneeded files and mark them */ + for (i = 0; i < res; i++) { + if (!filter(_filelist[i], farg)) { + count++; + } else { + free(_filelist[i]); + _filelist[i] = 0; + } + } + + /* no matched result */ + if (!count) { + free(_filelist); + return 0; + } + + /* construct the out array */ + *namelist = malloc(count * sizeof(struct dirent *)); + if (!(*namelist)) + goto e_free; + + for (i = 0; i < res; i++) { + if (_filelist[i]) + (*namelist)[index++] = _filelist[i]; + } + + free(_filelist); + + return count; + +e_free: + for (i = 0; i < res; i++) + if (_filelist[i]) + free(_filelist[i]); + free(_filelist); + return -errno; +} + +/* filters return zero if the match is successful */ +int filter_filename_substr(const struct dirent *entry, const void *arg) +{ + const char *substr = (const char *)arg; + + return !strstr(entry->d_name, substr); +} + +int filter_filename_exactly(const struct dirent *entry, const void *arg) +{ + const char *fname = (const char *)arg; + + return strcmp(entry->d_name, fname); +} + +int filter_filename_startswith(const struct dirent *entry, + const void *arg) +{ + const char *str = (const char *)arg; + + return memcmp(entry->d_name, str, strlen(str)); +} + +int dir_contains(const char *dir, const char *filename, const int exact) +{ + int ret; + int i; struct dirent **filelist; - char *name; if (!dir || !filename) return -EINVAL; - /* pass parameters to scandir's filter is not convenience, so we use - * this implementation. - */ - ret = scandir(dir, &filelist, 0, 0); - if (ret < 0) - return -errno; + if (exact) + ret = ac_scandir(dir, &filelist, filter_filename_exactly, + (const void *)filename, 0); + else + ret = ac_scandir(dir, &filelist, filter_filename_substr, + (const void *)filename, 0); + if (ret <= 0) + return ret; - while (ret--) { - name = filelist[ret]->d_name; - if (exact) { - if (!strcmp(name, filename)) - count++; - } else { - if (strstr(name, filename)) { - count++; - if (fullname) - strcpy(fullname, name); - } - } - free(filelist[ret]); - } + for (i = 0; i < ret; i++) + free(filelist[i]); free(filelist); - return count; + return ret; } int lsdir(const char *dir, char *fullname[], int limit) @@ -1087,7 +1176,7 @@ int find_file(char *dir, char *target_file, int depth, char *path[], int limit) if (count >= limit) goto free; - ret = dir_contains(_dirs[i], target_file, 1, NULL); + ret = dir_contains(_dirs[i], target_file, 1); if (ret == 1) { ret = asprintf(&path[count++], "%s/%s", _dirs[i], target_file); @@ -1096,9 +1185,6 @@ int find_file(char *dir, char *target_file, int depth, char *path[], int limit) ret = -ENOMEM; goto fail; } - } else if (ret > 1) { - LOGE("found (%d) (%s) under (%s)??\n", - ret, target_file, dir); } else if (ret < 0) { LOGE("dir_contains failed, error (%s)\n", strerror(-ret)); @@ -1171,3 +1257,140 @@ free: close(fd); return -1; } + +int is_ac_filefmt(const char *file_fmt) +{ + /* Supported formats: + * - /dir/.../file[*] --> all files with prefix "file." + * - /dir/.../file[0] --> file with smallest subfix num. + * - /dir/.../file[-1] --> file with biggest subfix num. + */ + if (!file_fmt) + return 0; + + return (strstr(file_fmt, "[*]") || + strstr(file_fmt, "[0]") || + strstr(file_fmt, "[-1]")); +} + +/** + * The config file of acrnprobe could use some format to indicate a file/files. + * This function is used to parse the format and returns found files paths. + * + * @param file_fmt A string pointer of a file format. + * @param out Files were found. + * + * @return the count of searched files if successful, or a negative + * errno-style value if not. + */ +int config_fmt_to_files(const char *file_fmt, char ***out) +{ + char type[3]; + char *dir; + char *p; + char *subfix; + char *file_prefix; + int i; + int count; + int res; + int ret = 0; + struct dirent **filelist; + char **out_array; + + if (!file_fmt || !out) + return -EINVAL; + + dir = strdup(file_fmt); + if (!dir) + return -ENOMEM; + + if (!is_ac_filefmt(file_fmt)) { + /* It's an regular file as default */ + out_array = malloc(sizeof(char *)); + if (!out_array) { + ret = -errno; + goto free_dir; + } + + out_array[0] = dir; + *out = out_array; + return 1; + } + + /* get dir and file prefix from format */ + p = strrchr(dir, '/'); + if (!p) { + ret = -EINVAL; + goto free_dir; + } + *p = '\0'; + file_prefix = p + 1; + *strrchr(file_prefix, '[') = '\0'; + + if (!directory_exists(dir)) { + ret = 0; + goto free_dir; + } + /* get format type */ + subfix = strrchr(file_fmt, '['); + res = sscanf(subfix, "[%2[01-*]]", type); + if (res != 1) { + ret = -EINVAL; + goto free_dir; + } + + /* get all files which start with prefix */ + count = ac_scandir(dir, &filelist, filter_filename_startswith, + file_prefix, alphasort); + if (count <= 0) { + ret = count; + goto free_dir; + } + + /* construct output */ + out_array = (char **)malloc(count * sizeof(char *)); + if (!out_array) { + ret = -errno; + goto free_filelist; + } + + if (!strcmp(type, "*")) { + for (i = 0; i < count; i++) { + res = asprintf(&out_array[i], "%s/%s", dir, + filelist[i]->d_name); + if (res == -1) { + /* free from 0 to i -1 */ + while (i) + free(out_array[--i]); + break; + } + } + ret = count; + } else if (!strcmp(type, "0")) { + res = asprintf(&out_array[0], "%s/%s", dir, + filelist[0]->d_name); + ret = 1; + } else if (!strcmp(type, "-1")) { + res = asprintf(&out_array[0], "%s/%s", dir, + filelist[count - 1]->d_name); + ret = 1; + } + + /* error happends while constructing output */ + if (res == -1) { + ret = -errno; + free(out_array); + goto free_filelist; + } + + *out = out_array; + +free_filelist: + for (i = 0; i < count; i++) + free(filelist[i]); + free(filelist); +free_dir: + free(dir); + + return ret; +} diff --git a/tools/acrn-crashlog/common/include/cmdutils.h b/tools/acrn-crashlog/common/include/cmdutils.h index 498f1eb29..682568cb5 100644 --- a/tools/acrn-crashlog/common/include/cmdutils.h +++ b/tools/acrn-crashlog/common/include/cmdutils.h @@ -3,7 +3,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ -int execv_out2file(char *argv[], char *outfile); -int debugfs_cmd(char *loop_dev, char *cmd, char *outfile); -int exec_out2file(char *outfile, char *fmt, ...); -char *exec_out2mem(char *fmt, ...); +int execv_out2file(char * const argv[], const char *outfile); +int debugfs_cmd(const char *loop_dev, const char *cmd, const char *outfile); +int exec_out2file(const char *outfile, const char *fmt, ...); +char *exec_out2mem(const char *fmt, ...); diff --git a/tools/acrn-crashlog/common/include/fsutils.h b/tools/acrn-crashlog/common/include/fsutils.h index a3305dd00..77047c183 100644 --- a/tools/acrn-crashlog/common/include/fsutils.h +++ b/tools/acrn-crashlog/common/include/fsutils.h @@ -75,7 +75,7 @@ int mkdir_p(char *path); int mm_count_lines(struct mm_file_t *mfile); struct mm_file_t *mmap_file(const char *path); void unmap_file(struct mm_file_t *mfile); -int do_copy_tail(char *src, char *dest, int limit); +int do_copy_tail(const char *src, const char *dest, int limit); int do_mv(char *src, char *dest); int append_file(char *filename, char *text); int mm_replace_str_line(struct mm_file_t *mfile, char *replace, @@ -97,10 +97,20 @@ int file_read_key_value(const char *path, const char *key, const size_t limit, char *value); int file_read_key_value_r(const char *path, const char *key, const size_t limit, char *value); -int dir_contains(const char *dir, const char *filename, int exact, - char *fullname); +int ac_scandir(const char *dirp, struct dirent ***namelist, + int (*filter)(const struct dirent *, const void *), + const void *farg, + int (*compar)(const struct dirent **, + const struct dirent **)); +int filter_filename_substr(const struct dirent *entry, const void *arg); +int filter_filename_exactly(const struct dirent *entry, const void *arg); +int filter_filename_startswith(const struct dirent *entry, + const void *arg); +int dir_contains(const char *dir, const char *filename, int exact); int lsdir(const char *dir, char *fullname[], int limit); int find_file(char *dir, char *target_file, int depth, char *path[], int limit); int read_file(const char *path, unsigned long *size, void **data); +int is_ac_filefmt(const char *file_fmt); +int config_fmt_to_files(const char *file_fmt, char ***out); #endif diff --git a/tools/acrn-crashlog/data/acrnprobe.xml b/tools/acrn-crashlog/data/acrnprobe.xml index 78fc5ce1b..a2e51c02b 100644 --- a/tools/acrn-crashlog/data/acrnprobe.xml +++ b/tools/acrn-crashlog/data/acrnprobe.xml @@ -77,14 +77,14 @@ acrnlog_cur - file_rotation - /tmp/acrnlog/acrnlog_cur.[biggest] + file + /tmp/acrnlog/acrnlog_cur.[-1] 500 acrnlog_last - file_rotation - /tmp/acrnlog/acrnlog_last.[all] + file + /tmp/acrnlog/acrnlog_last.[*]