mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -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/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();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue