pw-profiler: add -n and -J options

The -n option sets the number of iterations to log.

The -J option dumps the raw profiler data as JSON on stdout.
This commit is contained in:
Wim Taymans 2024-07-15 12:23:58 +02:00
parent d4515378e7
commit 3de4f0d48b

View file

@ -9,6 +9,7 @@
#include <spa/utils/result.h>
#include <spa/utils/string.h>
#include <spa/utils/json.h>
#include <spa/pod/parser.h>
#include <spa/debug/types.h>
@ -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;
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,13 +689,16 @@ int main(int argc, char *argv[])
data.filename = opt_output;
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;
}
printf("Logging to %s\n", data.filename);
fprintf(stderr, "Logging to %s\n", data.filename);
} else {
printf("[");
}
pw_core_add_listener(data.core,
&data.core_listener,
@ -635,9 +723,12 @@ int main(int argc, char *argv[])
pw_context_destroy(data.context);
pw_main_loop_destroy(data.loop);
if (!data.json_dump) {
fclose(data.output);
dump_scripts(&data);
} else {
printf("\n]\n");
}
pw_deinit();