diff --git a/tools/acrn-crashlog/acrnprobe/sender.c b/tools/acrn-crashlog/acrnprobe/sender.c index e446d72f8..e3809f3f5 100644 --- a/tools/acrn-crashlog/acrnprobe/sender.c +++ b/tools/acrn-crashlog/acrnprobe/sender.c @@ -23,6 +23,17 @@ #include "property.h" #include "startupreason.h" #include "log_sys.h" +#include "telemetry.h" + +#define CRASH_SEVERITY 4 +#define INFO_SEVERITY 2 + +struct telemd_data_t { + char *class; + char *srcdir; + char *eventid; + uint32_t severity; +}; /* 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) @@ -261,6 +272,124 @@ static void get_log_cmd(struct log_t *log, char *desdir) out_via_fork(log, desdir); } +static bool telemd_send_data(char *payload, char *eventid, uint32_t severity, + char *class) +{ + int res; + struct telem_ref *handle = NULL; + const uint32_t version = 1; + + res = tm_create_record(&handle, severity, class, version); + if (res < 0) { + LOGE("failed to create record: %s\n", + strerror(-res)); + goto fail; + } + + /* eventid with 32 character length */ + if (eventid) { + res = tm_set_event_id(handle, eventid); + if (res < 0) { + LOGE("failed to set eventid: %s\n", strerror(-res)); + goto free; + } + } + + res = tm_set_payload(handle, payload); + if (res < 0) { + LOGE("failed to set payload: %s\n", strerror(-res)); + goto free; + } + + res = tm_send_record(handle); + if (res < 0) { + LOGE("failed to send record: %s\n", strerror(-res)); + goto free; + } + + tm_free_record(handle); + return true; + +free: + tm_free_record(handle); +fail: + return false; +} + +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; + + 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) { + 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; + } + + return; + +send_nologs: + ret = asprintf(&msg, "no log generated on %s, check probe's log.", + log->name); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + return; + } + + telemd_send_data(msg, d->eventid, d->severity, d->class); + free(msg); +} + static void crashlog_get_log(struct log_t *log, void *data) { @@ -298,6 +427,354 @@ static void crashlog_get_log(struct log_t *log, void *data) LOGW("get (%s) spend %ds\n", log->name, spent); } +static void telemd_send_crash(struct event_t *e) +{ + struct crash_t *crash; + struct log_t *log; + char *class; + char *eventid; + int id; + int ret; + struct telemd_data_t data = { + .srcdir = e->dir, + .severity = CRASH_SEVERITY, + }; + + crash = (struct crash_t *)e->private; + + ret = asprintf(&class, "clearlinux/crash/%s", crash->name); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + return; + } + + eventid = generate_eventid256(class); + if (eventid == NULL) { + LOGE("generate eventid failed, error (%s)\n", strerror(errno)); + goto free_class; + } + + data.class = class; + data.eventid = eventid; + + for_each_log_collect(id, log, crash) { + if (!log) + continue; + + log->get(log, (void *)&data); + } + if (!strcmp(e->channel, "inotify")) { + char *des; + /* get the trigger file */ + ret = asprintf(&des, "%s/%s", e->dir, e->path); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + goto free_eventid; + } + + if (!file_exists(des)) { + /* find the original path */ + char *ori; + + ret = asprintf(&ori, "%s/%s", crash->trigger->path, + e->path); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + free(des); + goto free_eventid; + } + + LOGW("(%s) unavailable, try the original path (%s)\n", + des, ori); + if (!file_exists(ori)) { + LOGE("original path (%s) is unavailable\n", + ori); + } else { + telemd_send_data(ori, eventid, CRASH_SEVERITY, + class); + } + + free(ori); + } else { + telemd_send_data(des, eventid, CRASH_SEVERITY, class); + } + + free(des); + } +free_eventid: + free(eventid); +free_class: + free(class); +} + +static void telemd_send_info(struct event_t *e) +{ + struct info_t *info; + struct log_t *log; + char *class; + char *eventid; + int id; + int ret; + struct telemd_data_t data = { + .srcdir = e->dir, + .severity = INFO_SEVERITY, + }; + + info = (struct info_t *)e->private; + ret = asprintf(&class, "clearlinux/info/%s", info->name); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + return; + } + + eventid = generate_eventid256(class); + if (eventid == NULL) { + LOGE("generate eventid failed, error (%s)\n", strerror(errno)); + goto free_class; + } + + data.class = class; + data.eventid = eventid; + + for_each_log_collect(id, log, info) { + if (!log) + continue; + + log->get(log, (void *)&data); + } + + free(eventid); + +free_class: + free(class); +} + +static void telemd_send_uptime(void) +{ + struct sender_t *telemd; + struct uptime_t *uptime; + char *class; + char boot_time[24]; + int hours; + int ret; + static int uptime_hours; + static int loop_uptime_event = 1; + + ret = get_uptime_string(boot_time, &hours); + if (ret < 0) { + LOGE("cannot get uptime - %s\n", strerror(-ret)); + return; + } + telemd = get_sender_by_name("telemd"); + uptime = telemd->uptime; + uptime_hours = atoi(uptime->eventhours); + if (hours / uptime_hours >= loop_uptime_event) { + char *content; + + loop_uptime_event = (hours / uptime_hours) + 1; + ret = asprintf(&class, "clearlinux/uptime/%s", boot_time); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + return; + } + + ret = asprintf(&content, "system boot time: %s", boot_time); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + free(class); + return; + } + + telemd_send_data(content, NULL, INFO_SEVERITY, class); + free(class); + free(content); + } +} + +static void telemd_send_reboot(void) +{ + struct sender_t *telemd; + char *class; + char reason[MAXLINESIZE]; + int ret; + + telemd = get_sender_by_name("telemd"); + if (swupdated(telemd)) { + char *content; + + ret = asprintf(&class, "clearlinux/swupdate/-"); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + return; + } + + ret = asprintf(&content, "system update to: %s", + gbuildversion); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + free(class); + return; + } + + telemd_send_data(content, NULL, INFO_SEVERITY, class); + free(class); + free(content); + } + + read_startupreason(reason); + ret = asprintf(&class, "clearlinux/reboot/%s", reason); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + return; + } + + telemd_send_data("reboot", NULL, INFO_SEVERITY, class); + free(class); +} + +static void telemd_new_vmevent(char *line_to_sync, struct vm_t *vm) +{ + char event[96] = {0}; + char longtime[96] = {0}; + char type[96] = {0}; + char rest[PATH_MAX] = {0}; + char *vmlogpath[1] = {0}; + char vmkey[SHA_DIGEST_LENGTH + 1] = {0}; + char *log; + char *class; + char *eventid; + char *files[512]; + int count; + int i; + uint32_t severity; + int ret; + + /* VM events in history_event look like this: + * + * "CRASH xxxxxxxxxxxxxxxxxxxx 2017-11-11/03:12:59 JAVACRASH + * /data/logs/crashlog0_xxxxxxxxxxxxxxxxxxxx" + * "REBOOT xxxxxxxxxxxxxxxxxxxx 2011-11-11/11:20:51 POWER-ON + * 0000:00:00" + */ + char *vm_format = "%[^ ]%*[ ]%[^ ]%*[ ]%[^ ]%*[ ]%[^ ]%*[ ]%[^\n]%*c"; + + ret = sscanf(line_to_sync, vm_format, event, vmkey, longtime, + type, rest); + if (ret != 5) { + LOGE("get a invalied line from (%s), skip\n", vm->name); + return; + } + + if (strcmp(event, "CRASH") == 0) + severity = CRASH_SEVERITY; + else + severity = INFO_SEVERITY; + + /* if line contains log, fill vmlogpath */ + log = strstr(rest, "/logs/"); + if (log) { + struct sender_t *crashlog = get_sender_by_name("crashlog"); + + ret = find_file(crashlog->outdir, log + strlen("/logs/"), + 2, vmlogpath, 1); + if (ret < 0) { + LOGE("find (%s) in (%s) failed, strerror (%s)\n", + log + strlen("/logs/"), crashlog->outdir, + strerror(-ret)); + return; + } + } + + ret = asprintf(&class, "%s/%s/%s", vm->name, event, type); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + goto free_vmlogpath; + } + + eventid = generate_eventid256(class); + if (eventid == NULL) { + LOGE("generate eventid failed, error (%s)\n", strerror(errno)); + goto free_class; + } + + if (vmlogpath[0] == 0) { + telemd_send_data("no logs", eventid, severity, class); + goto free; + } + + /* send logs */ + count = lsdir(vmlogpath[0], files, ARRAY_SIZE(files)); + if (count > 2) { + for (i = 0; i < count; i++) { + if (!strstr(files[i], "/.") && + !strstr(files[i], "/..")) + telemd_send_data(files[i], eventid, severity, + class); + } + } else if (count == 2) { + char *content; + + ret = asprintf(&content, "no logs under (%s)", vmlogpath[0]); + if (ret < 0) { + LOGE("compute string failed, out of memory\n"); + goto free; + } + + telemd_send_data(content, eventid, severity, class); + free(content); + } else if (count < 0) { + LOGE("lsdir (%s) failed, error (%s)\n", vmlogpath[0], + strerror(-count)); + } else { + LOGE("get (%d) files in (%s) ???\n", count, vmlogpath[0]); + } + + while (count > 0) + free(files[--count]); + +free: + free(eventid); +free_class: + free(class); +free_vmlogpath: + if (vmlogpath[0]) + free(vmlogpath[0]); +} + +static void telemd_send(struct event_t *e) +{ + int id; + struct log_t *log; + + for_each_log(id, log, conf) { + if (!log) + continue; + + log->get = telemd_get_log; + } + + switch (e->event_type) { + case CRASH: + telemd_send_crash(e); + break; + case INFO: + telemd_send_info(e); + break; + case UPTIME: + telemd_send_uptime(); + break; + case REBOOT: + telemd_send_reboot(); + break; + case VM: + refresh_vm_history(get_sender_by_name("telemd"), + telemd_new_vmevent); + break; + default: + LOGE("unsupoorted event type %d\n", e->event_type); + } +} + static void crashlog_send_crash(struct event_t *e) { struct crash_t *crash; @@ -645,6 +1122,9 @@ int init_sender(void) ret = prepare_history(); if (ret) return -1; + } else if (!strncmp(sender->name, "telemd", + strlen(sender->name))) { + sender->send = telemd_send; } }