diff --git a/src/tools/pw-profiler.c b/src/tools/pw-profiler.c index 277c9f9bb..e15eca954 100644 --- a/src/tools/pw-profiler.c +++ b/src/tools/pw-profiler.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -36,6 +37,9 @@ struct data { const char *filename; FILE *output; + bool json_dump; + uint32_t iterations; + bool need_comma; int64_t count; int64_t start_status; @@ -58,6 +62,8 @@ struct measurement { int64_t awake; int64_t finish; int32_t status; + struct spa_fraction latency; + int32_t xrun_count; }; struct point { @@ -70,16 +76,32 @@ struct point { static int process_info(struct data *d, const struct spa_pod *pod, struct point *point) { - return spa_pod_parse_struct(pod, + int res; + char cpu_load0[128], cpu_load1[128], cpu_load2[128]; + + res = spa_pod_parse_struct(pod, SPA_POD_Long(&point->count), SPA_POD_Float(&point->cpu_load[0]), SPA_POD_Float(&point->cpu_load[1]), SPA_POD_Float(&point->cpu_load[2])); + if (d->json_dump) { + fprintf(stdout, "%s\n{ \"type\": \"info\", \"count\": %"PRIu64", " + "\"cpu_load0\": %s, \"cpu_load1\": %s, \"cpu_load2\": %s }", + d->need_comma ? "," : "", + point->count, + spa_json_format_float(cpu_load0, sizeof(cpu_load0), point->cpu_load[0]), + spa_json_format_float(cpu_load1, sizeof(cpu_load1), point->cpu_load[1]), + spa_json_format_float(cpu_load2, sizeof(cpu_load2), point->cpu_load[2])); + d->need_comma = true; + } + return res; } static int process_clock(struct data *d, const struct spa_pod *pod, struct point *point) { - return spa_pod_parse_struct(pod, + int res; + char val[128]; + res = spa_pod_parse_struct(pod, SPA_POD_Int(&point->clock.flags), SPA_POD_Int(&point->clock.id), SPA_POD_Stringn(point->clock.name, sizeof(point->clock.name)), @@ -90,6 +112,21 @@ static int process_clock(struct data *d, const struct spa_pod *pod, struct point SPA_POD_Long(&point->clock.delay), SPA_POD_Double(&point->clock.rate_diff), SPA_POD_Long(&point->clock.next_nsec)); + if (d->json_dump) { + fprintf(stdout, "%s\n{ \"type\": \"clock\", \"flags\": %u, \"id\": %u, " + "\"name\": \"%s\", \"nsec\": %"PRIu64", \"rate\": \"%u/%u\", " + "\"position\": %"PRIu64", \"duration\": %"PRIu64", " + "\"delay\": %"PRIu64", \"diff\": %s, \"next_nsec\": %"PRIu64" }", + d->need_comma ? "," : "", + point->clock.flags, point->clock.id, point->clock.name, + point->clock.nsec, point->clock.rate.num, point->clock.rate.denom, + point->clock.position, point->clock.duration, + point->clock.delay, + spa_json_format_float(val, sizeof(val), (float)point->clock.rate_diff), + point->clock.next_nsec); + d->need_comma = true; + } + return res; } static int process_driver_block(struct data *d, const struct spa_pod *pod, struct point *point) @@ -107,14 +144,29 @@ static int process_driver_block(struct data *d, const struct spa_pod *pod, struc SPA_POD_Long(&driver.signal), SPA_POD_Long(&driver.awake), SPA_POD_Long(&driver.finish), - SPA_POD_Int(&driver.status))) < 0) + SPA_POD_Int(&driver.status), + SPA_POD_Fraction(&driver.latency), + SPA_POD_Int(&driver.xrun_count))) < 0) return res; + if (d->json_dump) { + fprintf(stdout, "%s\n{ \"type\": \"driver\", \"id\": %u, \"name\": \"%s\", \"prev\": %"PRIu64", " + "\"signal\": %"PRIu64", \"awake\": %"PRIu64", " + "\"finish\": %"PRIu64", \"status\": %d, \"latency\": \"%u/%u\", " + "\"xrun_count\": %u }", + d->need_comma ? "," : "", + driver_id, name, driver.prev_signal, driver.signal, + driver.awake, driver.finish, driver.status, + driver.latency.num, driver.latency.denom, + driver.xrun_count); + d->need_comma = true; + } + if (d->driver_id == 0) { d->driver_id = driver_id; - printf("logging driver %u\n", driver_id); + pw_log_info("logging driver %u", driver_id); } - else if (d->driver_id != driver_id) + else if (d->driver_id != driver_id && !d->json_dump) return -1; point->driver = driver; @@ -143,7 +195,8 @@ static int add_follower(struct data *d, uint32_t id, const char *name) strncpy(d->followers[idx].name, name, MAX_NAME); d->followers[idx].name[MAX_NAME-1] = '\0'; d->followers[idx].id = id; - printf("logging follower %u (\"%s\")\n", id, name); + + pw_log_info("logging follower %u (\"%s\")", id, name); return idx; } @@ -163,9 +216,25 @@ static int process_follower_block(struct data *d, const struct spa_pod *pod, str SPA_POD_Long(&m.signal), SPA_POD_Long(&m.awake), SPA_POD_Long(&m.finish), - SPA_POD_Int(&m.status))) < 0) + SPA_POD_Int(&m.status), + SPA_POD_Fraction(&m.latency), + SPA_POD_Int(&m.xrun_count))) < 0) return res; + if (d->json_dump) { + fprintf(stdout, "%s\n{ \"type\": \"follower\", \"id\": %u, \"name\": \"%s\", \"prev\": %"PRIu64", " + "\"signal\": %"PRIu64", \"awake\": %"PRIu64", " + "\"finish\": %"PRIu64", \"status\": %d, \"latency\": \"%u/%u\", " + "\"xrun_count\": %u }", + d->need_comma ? "," : "", + id, name, m.prev_signal, m.signal, + m.awake, m.finish, m.status, + m.latency.num, m.latency.denom, + m.xrun_count); + d->need_comma = true; + } + + if ((idx = find_follower(d, id, name)) < 0) { if ((idx = add_follower(d, id, name)) < 0) { pw_log_warn("too many followers"); @@ -224,7 +293,7 @@ static void dump_point(struct data *d, struct point *point) d->last_status = point->clock.nsec; } else if (point->clock.nsec - d->last_status > SPA_NSEC_PER_SEC) { - printf("logging %"PRIi64" samples %"PRIi64" seconds [CPU %f %f %f]\r", + fprintf(stderr, "logging %"PRIi64" samples %"PRIi64" seconds [CPU %f %f %f]\r", d->count, (int64_t) ((d->last_status - d->start_status) / SPA_NSEC_PER_SEC), point->cpu_load[0], point->cpu_load[1], point->cpu_load[2]); d->last_status = point->clock.nsec; @@ -240,7 +309,7 @@ static void dump_scripts(struct data *d) if (d->driver_id == 0) return; - printf("\ndumping scripts for %d followers\n", d->n_followers); + fprintf(stderr, "\ndumping scripts for %d followers\n", d->n_followers); out = fopen("Timing1.plot", "we"); if (out == NULL) { @@ -440,7 +509,13 @@ static void profiler_profile(void *data, const struct spa_pod *pod) if (res < 0) continue; - dump_point(d, &point); + if (!d->json_dump) + dump_point(d, &point); + + if (d->iterations > 0 && --d->iterations == 0) { + pw_main_loop_quit(d->loop); + break; + } } } @@ -468,7 +543,7 @@ static void registry_event_global(void *data, uint32_t id, if (proxy == NULL) goto error_proxy; - printf("Attaching to Profiler id:%d\n", id); + pw_log_info("Attaching to Profiler id:%d", id); d->profiler = proxy; pw_proxy_add_object_listener(proxy, &d->profiler_listener, &profiler_events, d); @@ -526,7 +601,9 @@ static void show_help(const char *name, bool error) " -h, --help Show this help\n" " --version Show version\n" " -r, --remote Remote daemon name\n" - " -o, --output Profiler output name (default \"%s\")\n", + " -o, --output Profiler output name (default \"%s\")\n" + " -J, --json Dump raw data as JSON\n" + " -n, --iterations Collect this many samples\n", name, DEFAULT_FILENAME); } @@ -542,6 +619,8 @@ int main(int argc, char *argv[]) { "version", no_argument, NULL, 'V' }, { "remote", required_argument, NULL, 'r' }, { "output", required_argument, NULL, 'o' }, + { "json", no_argument, NULL, 'J' }, + { "iterations", required_argument, NULL, 'n' }, { NULL, 0, NULL, 0} }; int c; @@ -549,7 +628,7 @@ int main(int argc, char *argv[]) setlocale(LC_ALL, ""); pw_init(&argc, &argv); - while ((c = getopt_long(argc, argv, "hVr:o:", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "hVr:o:Jn:", long_options, NULL)) != -1) { switch (c) { case 'h': show_help(argv[0], false); @@ -568,6 +647,12 @@ int main(int argc, char *argv[]) case 'r': opt_remote = optarg; break; + case 'J': + data.json_dump = true; + break; + case 'n': + spa_atou32(optarg, &data.iterations, 10); + break; default: show_help(argv[0], true); return -1; @@ -604,14 +689,17 @@ int main(int argc, char *argv[]) data.filename = opt_output; - data.output = fopen(data.filename, "we"); - if (data.output == NULL) { - fprintf(stderr, "Can't open file %s: %m\n", data.filename); - return -1; + if (!data.json_dump) { + data.output = fopen(data.filename, "we"); + if (data.output == NULL) { + fprintf(stderr, "Can't open file %s: %m\n", data.filename); + return -1; + } + fprintf(stderr, "Logging to %s\n", data.filename); + } else { + printf("["); } - printf("Logging to %s\n", data.filename); - pw_core_add_listener(data.core, &data.core_listener, &core_events, &data); @@ -635,9 +723,12 @@ int main(int argc, char *argv[]) pw_context_destroy(data.context); pw_main_loop_destroy(data.loop); - fclose(data.output); - - dump_scripts(&data); + if (!data.json_dump) { + fclose(data.output); + dump_scripts(&data); + } else { + printf("\n]\n"); + } pw_deinit();