mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
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:
parent
d4515378e7
commit
3de4f0d48b
1 changed files with 113 additions and 22 deletions
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <spa/utils/result.h>
|
#include <spa/utils/result.h>
|
||||||
#include <spa/utils/string.h>
|
#include <spa/utils/string.h>
|
||||||
|
#include <spa/utils/json.h>
|
||||||
#include <spa/pod/parser.h>
|
#include <spa/pod/parser.h>
|
||||||
#include <spa/debug/types.h>
|
#include <spa/debug/types.h>
|
||||||
|
|
||||||
|
|
@ -36,6 +37,9 @@ struct data {
|
||||||
|
|
||||||
const char *filename;
|
const char *filename;
|
||||||
FILE *output;
|
FILE *output;
|
||||||
|
bool json_dump;
|
||||||
|
uint32_t iterations;
|
||||||
|
bool need_comma;
|
||||||
|
|
||||||
int64_t count;
|
int64_t count;
|
||||||
int64_t start_status;
|
int64_t start_status;
|
||||||
|
|
@ -58,6 +62,8 @@ struct measurement {
|
||||||
int64_t awake;
|
int64_t awake;
|
||||||
int64_t finish;
|
int64_t finish;
|
||||||
int32_t status;
|
int32_t status;
|
||||||
|
struct spa_fraction latency;
|
||||||
|
int32_t xrun_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct point {
|
struct point {
|
||||||
|
|
@ -70,16 +76,32 @@ struct point {
|
||||||
|
|
||||||
static int process_info(struct data *d, const struct spa_pod *pod, struct point *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_Long(&point->count),
|
||||||
SPA_POD_Float(&point->cpu_load[0]),
|
SPA_POD_Float(&point->cpu_load[0]),
|
||||||
SPA_POD_Float(&point->cpu_load[1]),
|
SPA_POD_Float(&point->cpu_load[1]),
|
||||||
SPA_POD_Float(&point->cpu_load[2]));
|
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)
|
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.flags),
|
||||||
SPA_POD_Int(&point->clock.id),
|
SPA_POD_Int(&point->clock.id),
|
||||||
SPA_POD_Stringn(point->clock.name, sizeof(point->clock.name)),
|
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_Long(&point->clock.delay),
|
||||||
SPA_POD_Double(&point->clock.rate_diff),
|
SPA_POD_Double(&point->clock.rate_diff),
|
||||||
SPA_POD_Long(&point->clock.next_nsec));
|
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)
|
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.signal),
|
||||||
SPA_POD_Long(&driver.awake),
|
SPA_POD_Long(&driver.awake),
|
||||||
SPA_POD_Long(&driver.finish),
|
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;
|
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) {
|
if (d->driver_id == 0) {
|
||||||
d->driver_id = driver_id;
|
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;
|
return -1;
|
||||||
|
|
||||||
point->driver = driver;
|
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);
|
strncpy(d->followers[idx].name, name, MAX_NAME);
|
||||||
d->followers[idx].name[MAX_NAME-1] = '\0';
|
d->followers[idx].name[MAX_NAME-1] = '\0';
|
||||||
d->followers[idx].id = id;
|
d->followers[idx].id = id;
|
||||||
printf("logging follower %u (\"%s\")\n", id, name);
|
|
||||||
|
pw_log_info("logging follower %u (\"%s\")", id, name);
|
||||||
|
|
||||||
return idx;
|
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.signal),
|
||||||
SPA_POD_Long(&m.awake),
|
SPA_POD_Long(&m.awake),
|
||||||
SPA_POD_Long(&m.finish),
|
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;
|
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 = find_follower(d, id, name)) < 0) {
|
||||||
if ((idx = add_follower(d, id, name)) < 0) {
|
if ((idx = add_follower(d, id, name)) < 0) {
|
||||||
pw_log_warn("too many followers");
|
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;
|
d->last_status = point->clock.nsec;
|
||||||
}
|
}
|
||||||
else if (point->clock.nsec - d->last_status > SPA_NSEC_PER_SEC) {
|
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),
|
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]);
|
point->cpu_load[0], point->cpu_load[1], point->cpu_load[2]);
|
||||||
d->last_status = point->clock.nsec;
|
d->last_status = point->clock.nsec;
|
||||||
|
|
@ -240,7 +309,7 @@ static void dump_scripts(struct data *d)
|
||||||
if (d->driver_id == 0)
|
if (d->driver_id == 0)
|
||||||
return;
|
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");
|
out = fopen("Timing1.plot", "we");
|
||||||
if (out == NULL) {
|
if (out == NULL) {
|
||||||
|
|
@ -440,7 +509,13 @@ static void profiler_profile(void *data, const struct spa_pod *pod)
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!d->json_dump)
|
||||||
dump_point(d, &point);
|
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)
|
if (proxy == NULL)
|
||||||
goto error_proxy;
|
goto error_proxy;
|
||||||
|
|
||||||
printf("Attaching to Profiler id:%d\n", id);
|
pw_log_info("Attaching to Profiler id:%d", id);
|
||||||
d->profiler = proxy;
|
d->profiler = proxy;
|
||||||
pw_proxy_add_object_listener(proxy, &d->profiler_listener, &profiler_events, d);
|
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"
|
" -h, --help Show this help\n"
|
||||||
" --version Show version\n"
|
" --version Show version\n"
|
||||||
" -r, --remote Remote daemon name\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,
|
name,
|
||||||
DEFAULT_FILENAME);
|
DEFAULT_FILENAME);
|
||||||
}
|
}
|
||||||
|
|
@ -542,6 +619,8 @@ int main(int argc, char *argv[])
|
||||||
{ "version", no_argument, NULL, 'V' },
|
{ "version", no_argument, NULL, 'V' },
|
||||||
{ "remote", required_argument, NULL, 'r' },
|
{ "remote", required_argument, NULL, 'r' },
|
||||||
{ "output", required_argument, NULL, 'o' },
|
{ "output", required_argument, NULL, 'o' },
|
||||||
|
{ "json", no_argument, NULL, 'J' },
|
||||||
|
{ "iterations", required_argument, NULL, 'n' },
|
||||||
{ NULL, 0, NULL, 0}
|
{ NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
int c;
|
int c;
|
||||||
|
|
@ -549,7 +628,7 @@ int main(int argc, char *argv[])
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
pw_init(&argc, &argv);
|
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) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
show_help(argv[0], false);
|
show_help(argv[0], false);
|
||||||
|
|
@ -568,6 +647,12 @@ int main(int argc, char *argv[])
|
||||||
case 'r':
|
case 'r':
|
||||||
opt_remote = optarg;
|
opt_remote = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'J':
|
||||||
|
data.json_dump = true;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
spa_atou32(optarg, &data.iterations, 10);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
show_help(argv[0], true);
|
show_help(argv[0], true);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -604,13 +689,16 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
data.filename = opt_output;
|
data.filename = opt_output;
|
||||||
|
|
||||||
|
if (!data.json_dump) {
|
||||||
data.output = fopen(data.filename, "we");
|
data.output = fopen(data.filename, "we");
|
||||||
if (data.output == NULL) {
|
if (data.output == NULL) {
|
||||||
fprintf(stderr, "Can't open file %s: %m\n", data.filename);
|
fprintf(stderr, "Can't open file %s: %m\n", data.filename);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
fprintf(stderr, "Logging to %s\n", data.filename);
|
||||||
printf("Logging to %s\n", data.filename);
|
} else {
|
||||||
|
printf("[");
|
||||||
|
}
|
||||||
|
|
||||||
pw_core_add_listener(data.core,
|
pw_core_add_listener(data.core,
|
||||||
&data.core_listener,
|
&data.core_listener,
|
||||||
|
|
@ -635,9 +723,12 @@ int main(int argc, char *argv[])
|
||||||
pw_context_destroy(data.context);
|
pw_context_destroy(data.context);
|
||||||
pw_main_loop_destroy(data.loop);
|
pw_main_loop_destroy(data.loop);
|
||||||
|
|
||||||
|
if (!data.json_dump) {
|
||||||
fclose(data.output);
|
fclose(data.output);
|
||||||
|
|
||||||
dump_scripts(&data);
|
dump_scripts(&data);
|
||||||
|
} else {
|
||||||
|
printf("\n]\n");
|
||||||
|
}
|
||||||
|
|
||||||
pw_deinit();
|
pw_deinit();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue