brintos

brintos / linux-shallow public Read only

0
0
Text · 24.6 KiB · 214e2c9 Raw
1003 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>4 */5 6#define _GNU_SOURCE7#include <getopt.h>8#include <stdlib.h>9#include <string.h>10#include <signal.h>11#include <unistd.h>12#include <errno.h>13#include <stdio.h>14#include <time.h>15#include <sched.h>16 17#include "utils.h"18#include "osnoise.h"19 20struct osnoise_hist_params {21	char			*cpus;22	cpu_set_t		monitored_cpus;23	char			*trace_output;24	char			*cgroup_name;25	unsigned long long	runtime;26	unsigned long long	period;27	long long		threshold;28	long long		stop_us;29	long long		stop_total_us;30	int			sleep_time;31	int			duration;32	int			set_sched;33	int			output_divisor;34	int			cgroup;35	int			hk_cpus;36	cpu_set_t		hk_cpu_set;37	struct sched_attr	sched_param;38	struct trace_events	*events;39	char			no_header;40	char			no_summary;41	char			no_index;42	char			with_zeros;43	int			bucket_size;44	int			entries;45	int			warmup;46	int			buffer_size;47};48 49struct osnoise_hist_cpu {50	int			*samples;51	int			count;52 53	unsigned long long	min_sample;54	unsigned long long	sum_sample;55	unsigned long long	max_sample;56 57};58 59struct osnoise_hist_data {60	struct tracefs_hist	*trace_hist;61	struct osnoise_hist_cpu	*hist;62	int			entries;63	int			bucket_size;64	int			nr_cpus;65};66 67/*68 * osnoise_free_histogram - free runtime data69 */70static void71osnoise_free_histogram(struct osnoise_hist_data *data)72{73	int cpu;74 75	/* one histogram for IRQ and one for thread, per CPU */76	for (cpu = 0; cpu < data->nr_cpus; cpu++) {77		if (data->hist[cpu].samples)78			free(data->hist[cpu].samples);79	}80 81	/* one set of histograms per CPU */82	if (data->hist)83		free(data->hist);84 85	free(data);86}87 88/*89 * osnoise_alloc_histogram - alloc runtime data90 */91static struct osnoise_hist_data92*osnoise_alloc_histogram(int nr_cpus, int entries, int bucket_size)93{94	struct osnoise_hist_data *data;95	int cpu;96 97	data = calloc(1, sizeof(*data));98	if (!data)99		return NULL;100 101	data->entries = entries;102	data->bucket_size = bucket_size;103	data->nr_cpus = nr_cpus;104 105	data->hist = calloc(1, sizeof(*data->hist) * nr_cpus);106	if (!data->hist)107		goto cleanup;108 109	for (cpu = 0; cpu < nr_cpus; cpu++) {110		data->hist[cpu].samples = calloc(1, sizeof(*data->hist->samples) * (entries + 1));111		if (!data->hist[cpu].samples)112			goto cleanup;113	}114 115	/* set the min to max */116	for (cpu = 0; cpu < nr_cpus; cpu++)117		data->hist[cpu].min_sample = ~0;118 119	return data;120 121cleanup:122	osnoise_free_histogram(data);123	return NULL;124}125 126static void osnoise_hist_update_multiple(struct osnoise_tool *tool, int cpu,127					 unsigned long long duration, int count)128{129	struct osnoise_hist_params *params = tool->params;130	struct osnoise_hist_data *data = tool->data;131	unsigned long long total_duration;132	int entries = data->entries;133	int bucket;134	int *hist;135 136	if (params->output_divisor)137		duration = duration / params->output_divisor;138 139	bucket = duration / data->bucket_size;140 141	total_duration = duration * count;142 143	hist = data->hist[cpu].samples;144	data->hist[cpu].count += count;145	update_min(&data->hist[cpu].min_sample, &duration);146	update_sum(&data->hist[cpu].sum_sample, &total_duration);147	update_max(&data->hist[cpu].max_sample, &duration);148 149	if (bucket < entries)150		hist[bucket] += count;151	else152		hist[entries] += count;153}154 155/*156 * osnoise_destroy_trace_hist - disable events used to collect histogram157 */158static void osnoise_destroy_trace_hist(struct osnoise_tool *tool)159{160	struct osnoise_hist_data *data = tool->data;161 162	tracefs_hist_pause(tool->trace.inst, data->trace_hist);163	tracefs_hist_destroy(tool->trace.inst, data->trace_hist);164}165 166/*167 * osnoise_init_trace_hist - enable events used to collect histogram168 */169static int osnoise_init_trace_hist(struct osnoise_tool *tool)170{171	struct osnoise_hist_params *params = tool->params;172	struct osnoise_hist_data *data = tool->data;173	int bucket_size;174	char buff[128];175	int retval = 0;176 177	/*178	 * Set the size of the bucket.179	 */180	bucket_size = params->output_divisor * params->bucket_size;181	snprintf(buff, sizeof(buff), "duration.buckets=%d", bucket_size);182 183	data->trace_hist = tracefs_hist_alloc(tool->trace.tep, "osnoise", "sample_threshold",184			buff, TRACEFS_HIST_KEY_NORMAL);185	if (!data->trace_hist)186		return 1;187 188	retval = tracefs_hist_add_key(data->trace_hist, "cpu", 0);189	if (retval)190		goto out_err;191 192	retval = tracefs_hist_start(tool->trace.inst, data->trace_hist);193	if (retval)194		goto out_err;195 196	return 0;197 198out_err:199	osnoise_destroy_trace_hist(tool);200	return 1;201}202 203/*204 * osnoise_read_trace_hist - parse histogram file and file osnoise histogram205 */206static void osnoise_read_trace_hist(struct osnoise_tool *tool)207{208	struct osnoise_hist_data *data = tool->data;209	long long cpu, counter, duration;210	char *content, *position;211 212	tracefs_hist_pause(tool->trace.inst, data->trace_hist);213 214	content = tracefs_event_file_read(tool->trace.inst, "osnoise",215					  "sample_threshold",216					  "hist", NULL);217	if (!content)218		return;219 220	position = content;221	while (true) {222		position = strstr(position, "duration: ~");223		if (!position)224			break;225		position += strlen("duration: ~");226		duration = get_llong_from_str(position);227		if (duration == -1)228			err_msg("error reading duration from histogram\n");229 230		position = strstr(position, "cpu:");231		if (!position)232			break;233		position += strlen("cpu: ");234		cpu = get_llong_from_str(position);235		if (cpu == -1)236			err_msg("error reading cpu from histogram\n");237 238		position = strstr(position, "hitcount:");239		if (!position)240			break;241		position += strlen("hitcount: ");242		counter = get_llong_from_str(position);243		if (counter == -1)244			err_msg("error reading counter from histogram\n");245 246		osnoise_hist_update_multiple(tool, cpu, duration, counter);247	}248	free(content);249}250 251/*252 * osnoise_hist_header - print the header of the tracer to the output253 */254static void osnoise_hist_header(struct osnoise_tool *tool)255{256	struct osnoise_hist_params *params = tool->params;257	struct osnoise_hist_data *data = tool->data;258	struct trace_seq *s = tool->trace.seq;259	char duration[26];260	int cpu;261 262	if (params->no_header)263		return;264 265	get_duration(tool->start_time, duration, sizeof(duration));266	trace_seq_printf(s, "# RTLA osnoise histogram\n");267	trace_seq_printf(s, "# Time unit is %s (%s)\n",268			params->output_divisor == 1 ? "nanoseconds" : "microseconds",269			params->output_divisor == 1 ? "ns" : "us");270 271	trace_seq_printf(s, "# Duration: %s\n", duration);272 273	if (!params->no_index)274		trace_seq_printf(s, "Index");275 276	for (cpu = 0; cpu < data->nr_cpus; cpu++) {277		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))278			continue;279 280		if (!data->hist[cpu].count)281			continue;282 283		trace_seq_printf(s, "   CPU-%03d", cpu);284	}285	trace_seq_printf(s, "\n");286 287	trace_seq_do_printf(s);288	trace_seq_reset(s);289}290 291/*292 * osnoise_print_summary - print the summary of the hist data to the output293 */294static void295osnoise_print_summary(struct osnoise_hist_params *params,296		       struct trace_instance *trace,297		       struct osnoise_hist_data *data)298{299	int cpu;300 301	if (params->no_summary)302		return;303 304	if (!params->no_index)305		trace_seq_printf(trace->seq, "count:");306 307	for (cpu = 0; cpu < data->nr_cpus; cpu++) {308		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))309			continue;310 311		if (!data->hist[cpu].count)312			continue;313 314		trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].count);315	}316	trace_seq_printf(trace->seq, "\n");317 318	if (!params->no_index)319		trace_seq_printf(trace->seq, "min:  ");320 321	for (cpu = 0; cpu < data->nr_cpus; cpu++) {322		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))323			continue;324 325		if (!data->hist[cpu].count)326			continue;327 328		trace_seq_printf(trace->seq, "%9llu ",	data->hist[cpu].min_sample);329 330	}331	trace_seq_printf(trace->seq, "\n");332 333	if (!params->no_index)334		trace_seq_printf(trace->seq, "avg:  ");335 336	for (cpu = 0; cpu < data->nr_cpus; cpu++) {337		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))338			continue;339 340		if (!data->hist[cpu].count)341			continue;342 343		if (data->hist[cpu].count)344			trace_seq_printf(trace->seq, "%9.2f ",345				((double) data->hist[cpu].sum_sample) / data->hist[cpu].count);346		else347			trace_seq_printf(trace->seq, "        - ");348	}349	trace_seq_printf(trace->seq, "\n");350 351	if (!params->no_index)352		trace_seq_printf(trace->seq, "max:  ");353 354	for (cpu = 0; cpu < data->nr_cpus; cpu++) {355		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))356			continue;357 358		if (!data->hist[cpu].count)359			continue;360 361		trace_seq_printf(trace->seq, "%9llu ", data->hist[cpu].max_sample);362 363	}364	trace_seq_printf(trace->seq, "\n");365	trace_seq_do_printf(trace->seq);366	trace_seq_reset(trace->seq);367}368 369/*370 * osnoise_print_stats - print data for all CPUs371 */372static void373osnoise_print_stats(struct osnoise_hist_params *params, struct osnoise_tool *tool)374{375	struct osnoise_hist_data *data = tool->data;376	struct trace_instance *trace = &tool->trace;377	int has_samples = 0;378	int bucket, cpu;379	int total;380 381	osnoise_hist_header(tool);382 383	for (bucket = 0; bucket < data->entries; bucket++) {384		total = 0;385 386		if (!params->no_index)387			trace_seq_printf(trace->seq, "%-6d",388					 bucket * data->bucket_size);389 390		for (cpu = 0; cpu < data->nr_cpus; cpu++) {391			if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))392				continue;393 394			if (!data->hist[cpu].count)395				continue;396 397			total += data->hist[cpu].samples[bucket];398			trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].samples[bucket]);399		}400 401		if (total == 0 && !params->with_zeros) {402			trace_seq_reset(trace->seq);403			continue;404		}405 406		/* There are samples above the threshold */407		has_samples = 1;408		trace_seq_printf(trace->seq, "\n");409		trace_seq_do_printf(trace->seq);410		trace_seq_reset(trace->seq);411	}412 413	/*414	 * If no samples were recorded, skip calculations, print zeroed statistics415	 * and return.416	 */417	if (!has_samples) {418		trace_seq_reset(trace->seq);419		trace_seq_printf(trace->seq, "over: 0\ncount: 0\nmin: 0\navg: 0\nmax: 0\n");420		trace_seq_do_printf(trace->seq);421		trace_seq_reset(trace->seq);422		return;423	}424 425	if (!params->no_index)426		trace_seq_printf(trace->seq, "over: ");427 428	for (cpu = 0; cpu < data->nr_cpus; cpu++) {429		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))430			continue;431 432		if (!data->hist[cpu].count)433			continue;434 435		trace_seq_printf(trace->seq, "%9d ",436				 data->hist[cpu].samples[data->entries]);437	}438	trace_seq_printf(trace->seq, "\n");439	trace_seq_do_printf(trace->seq);440	trace_seq_reset(trace->seq);441 442	osnoise_print_summary(params, trace, data);443}444 445/*446 * osnoise_hist_usage - prints osnoise hist usage message447 */448static void osnoise_hist_usage(char *usage)449{450	int i;451 452	static const char * const msg[] = {453		"",454		"  usage: rtla osnoise hist [-h] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",455		"	  [-T us] [-t[file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",456		"	  [-c cpu-list] [-H cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\",457		"	  [--no-index] [--with-zeros] [-C[=cgroup_name]] [--warm-up]",458		"",459		"	  -h/--help: print this menu",460		"	  -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",461		"	  -p/--period us: osnoise period in us",462		"	  -r/--runtime us: osnoise runtime in us",463		"	  -s/--stop us: stop trace if a single sample is higher than the argument in us",464		"	  -S/--stop-total us: stop trace if the total sample is higher than the argument in us",465		"	  -T/--threshold us: the minimum delta to be considered a noise",466		"	  -c/--cpus cpu-list: list of cpus to run osnoise threads",467		"	  -H/--house-keeping cpus: run rtla control threads only on the given cpus",468		"	  -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",469		"	  -d/--duration time[s|m|h|d]: duration of the session",470		"	  -D/--debug: print debug info",471		"	  -t/--trace[file]: save the stopped trace to [file|osnoise_trace.txt]",472		"	  -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",473		"	     --filter <filter>: enable a trace event filter to the previous -e event",474		"	     --trigger <trigger>: enable a trace event trigger to the previous -e event",475		"	  -b/--bucket-size N: set the histogram bucket size (default 1)",476		"	  -E/--entries N: set the number of entries of the histogram (default 256)",477		"	     --no-header: do not print header",478		"	     --no-summary: do not print summary",479		"	     --no-index: do not print index",480		"	     --with-zeros: print zero only entries",481		"	  -P/--priority o:prio|r:prio|f:prio|d:runtime:period: set scheduling parameters",482		"		o:prio - use SCHED_OTHER with prio",483		"		r:prio - use SCHED_RR with prio",484		"		f:prio - use SCHED_FIFO with prio",485		"		d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",486		"						       in nanoseconds",487		"	     --warm-up: let the workload run for s seconds before collecting data",488		"	     --trace-buffer-size kB: set the per-cpu trace buffer size in kB",489		NULL,490	};491 492	if (usage)493		fprintf(stderr, "%s\n", usage);494 495	fprintf(stderr, "rtla osnoise hist: a per-cpu histogram of the OS noise (version %s)\n",496			VERSION);497 498	for (i = 0; msg[i]; i++)499		fprintf(stderr, "%s\n", msg[i]);500 501	if (usage)502		exit(EXIT_FAILURE);503 504	exit(EXIT_SUCCESS);505}506 507/*508 * osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters509 */510static struct osnoise_hist_params511*osnoise_hist_parse_args(int argc, char *argv[])512{513	struct osnoise_hist_params *params;514	struct trace_events *tevent;515	int retval;516	int c;517 518	params = calloc(1, sizeof(*params));519	if (!params)520		exit(1);521 522	/* display data in microseconds */523	params->output_divisor = 1000;524	params->bucket_size = 1;525	params->entries = 256;526 527	while (1) {528		static struct option long_options[] = {529			{"auto",		required_argument,	0, 'a'},530			{"bucket-size",		required_argument,	0, 'b'},531			{"entries",		required_argument,	0, 'E'},532			{"cpus",		required_argument,	0, 'c'},533			{"cgroup",		optional_argument,	0, 'C'},534			{"debug",		no_argument,		0, 'D'},535			{"duration",		required_argument,	0, 'd'},536			{"house-keeping",	required_argument,		0, 'H'},537			{"help",		no_argument,		0, 'h'},538			{"period",		required_argument,	0, 'p'},539			{"priority",		required_argument,	0, 'P'},540			{"runtime",		required_argument,	0, 'r'},541			{"stop",		required_argument,	0, 's'},542			{"stop-total",		required_argument,	0, 'S'},543			{"trace",		optional_argument,	0, 't'},544			{"event",		required_argument,	0, 'e'},545			{"threshold",		required_argument,	0, 'T'},546			{"no-header",		no_argument,		0, '0'},547			{"no-summary",		no_argument,		0, '1'},548			{"no-index",		no_argument,		0, '2'},549			{"with-zeros",		no_argument,		0, '3'},550			{"trigger",		required_argument,	0, '4'},551			{"filter",		required_argument,	0, '5'},552			{"warm-up",		required_argument,	0, '6'},553			{"trace-buffer-size",	required_argument,	0, '7'},554			{0, 0, 0, 0}555		};556 557		/* getopt_long stores the option index here. */558		int option_index = 0;559 560		c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:p:P:r:s:S:t::T:01234:5:6:7:",561				 long_options, &option_index);562 563		/* detect the end of the options. */564		if (c == -1)565			break;566 567		switch (c) {568		case 'a':569			/* set sample stop to auto_thresh */570			params->stop_us = get_llong_from_str(optarg);571 572			/* set sample threshold to 1 */573			params->threshold = 1;574 575			/* set trace */576			params->trace_output = "osnoise_trace.txt";577 578			break;579		case 'b':580			params->bucket_size = get_llong_from_str(optarg);581			if ((params->bucket_size == 0) || (params->bucket_size >= 1000000))582				osnoise_hist_usage("Bucket size needs to be > 0 and <= 1000000\n");583			break;584		case 'c':585			retval = parse_cpu_set(optarg, &params->monitored_cpus);586			if (retval)587				osnoise_hist_usage("\nInvalid -c cpu list\n");588			params->cpus = optarg;589			break;590		case 'C':591			params->cgroup = 1;592			if (!optarg) {593				/* will inherit this cgroup */594				params->cgroup_name = NULL;595			} else if (*optarg == '=') {596				/* skip the = */597				params->cgroup_name = ++optarg;598			}599			break;600		case 'D':601			config_debug = 1;602			break;603		case 'd':604			params->duration = parse_seconds_duration(optarg);605			if (!params->duration)606				osnoise_hist_usage("Invalid -D duration\n");607			break;608		case 'e':609			tevent = trace_event_alloc(optarg);610			if (!tevent) {611				err_msg("Error alloc trace event");612				exit(EXIT_FAILURE);613			}614 615			if (params->events)616				tevent->next = params->events;617 618			params->events = tevent;619			break;620		case 'E':621			params->entries = get_llong_from_str(optarg);622			if ((params->entries < 10) || (params->entries > 9999999))623				osnoise_hist_usage("Entries must be > 10 and < 9999999\n");624			break;625		case 'h':626		case '?':627			osnoise_hist_usage(NULL);628			break;629		case 'H':630			params->hk_cpus = 1;631			retval = parse_cpu_set(optarg, &params->hk_cpu_set);632			if (retval) {633				err_msg("Error parsing house keeping CPUs\n");634				exit(EXIT_FAILURE);635			}636			break;637		case 'p':638			params->period = get_llong_from_str(optarg);639			if (params->period > 10000000)640				osnoise_hist_usage("Period longer than 10 s\n");641			break;642		case 'P':643			retval = parse_prio(optarg, &params->sched_param);644			if (retval == -1)645				osnoise_hist_usage("Invalid -P priority");646			params->set_sched = 1;647			break;648		case 'r':649			params->runtime = get_llong_from_str(optarg);650			if (params->runtime < 100)651				osnoise_hist_usage("Runtime shorter than 100 us\n");652			break;653		case 's':654			params->stop_us = get_llong_from_str(optarg);655			break;656		case 'S':657			params->stop_total_us = get_llong_from_str(optarg);658			break;659		case 'T':660			params->threshold = get_llong_from_str(optarg);661			break;662		case 't':663			if (optarg) {664				if (optarg[0] == '=')665					params->trace_output = &optarg[1];666				else667					params->trace_output = &optarg[0];668			} else if (optind < argc && argv[optind][0] != '0')669				params->trace_output = argv[optind];670			else671				params->trace_output = "osnoise_trace.txt";672			break;673		case '0': /* no header */674			params->no_header = 1;675			break;676		case '1': /* no summary */677			params->no_summary = 1;678			break;679		case '2': /* no index */680			params->no_index = 1;681			break;682		case '3': /* with zeros */683			params->with_zeros = 1;684			break;685		case '4': /* trigger */686			if (params->events) {687				retval = trace_event_add_trigger(params->events, optarg);688				if (retval) {689					err_msg("Error adding trigger %s\n", optarg);690					exit(EXIT_FAILURE);691				}692			} else {693				osnoise_hist_usage("--trigger requires a previous -e\n");694			}695			break;696		case '5': /* filter */697			if (params->events) {698				retval = trace_event_add_filter(params->events, optarg);699				if (retval) {700					err_msg("Error adding filter %s\n", optarg);701					exit(EXIT_FAILURE);702				}703			} else {704				osnoise_hist_usage("--filter requires a previous -e\n");705			}706			break;707		case '6':708			params->warmup = get_llong_from_str(optarg);709			break;710		case '7':711			params->buffer_size = get_llong_from_str(optarg);712			break;713		default:714			osnoise_hist_usage("Invalid option");715		}716	}717 718	if (geteuid()) {719		err_msg("rtla needs root permission\n");720		exit(EXIT_FAILURE);721	}722 723	if (params->no_index && !params->with_zeros)724		osnoise_hist_usage("no-index set and with-zeros not set - it does not make sense");725 726	return params;727}728 729/*730 * osnoise_hist_apply_config - apply the hist configs to the initialized tool731 */732static int733osnoise_hist_apply_config(struct osnoise_tool *tool, struct osnoise_hist_params *params)734{735	int retval;736 737	if (!params->sleep_time)738		params->sleep_time = 1;739 740	if (params->cpus) {741		retval = osnoise_set_cpus(tool->context, params->cpus);742		if (retval) {743			err_msg("Failed to apply CPUs config\n");744			goto out_err;745		}746	}747 748	if (params->runtime || params->period) {749		retval = osnoise_set_runtime_period(tool->context,750						    params->runtime,751						    params->period);752		if (retval) {753			err_msg("Failed to set runtime and/or period\n");754			goto out_err;755		}756	}757 758	if (params->stop_us) {759		retval = osnoise_set_stop_us(tool->context, params->stop_us);760		if (retval) {761			err_msg("Failed to set stop us\n");762			goto out_err;763		}764	}765 766	if (params->stop_total_us) {767		retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);768		if (retval) {769			err_msg("Failed to set stop total us\n");770			goto out_err;771		}772	}773 774	if (params->threshold) {775		retval = osnoise_set_tracing_thresh(tool->context, params->threshold);776		if (retval) {777			err_msg("Failed to set tracing_thresh\n");778			goto out_err;779		}780	}781 782	if (params->hk_cpus) {783		retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),784					   &params->hk_cpu_set);785		if (retval == -1) {786			err_msg("Failed to set rtla to the house keeping CPUs\n");787			goto out_err;788		}789	} else if (params->cpus) {790		/*791		 * Even if the user do not set a house-keeping CPU, try to792		 * move rtla to a CPU set different to the one where the user793		 * set the workload to run.794		 *795		 * No need to check results as this is an automatic attempt.796		 */797		auto_house_keeping(&params->monitored_cpus);798	}799 800	return 0;801 802out_err:803	return -1;804}805 806/*807 * osnoise_init_hist - initialize a osnoise hist tool with parameters808 */809static struct osnoise_tool810*osnoise_init_hist(struct osnoise_hist_params *params)811{812	struct osnoise_tool *tool;813	int nr_cpus;814 815	nr_cpus = sysconf(_SC_NPROCESSORS_CONF);816 817	tool = osnoise_init_tool("osnoise_hist");818	if (!tool)819		return NULL;820 821	tool->data = osnoise_alloc_histogram(nr_cpus, params->entries, params->bucket_size);822	if (!tool->data)823		goto out_err;824 825	tool->params = params;826 827	return tool;828 829out_err:830	osnoise_destroy_tool(tool);831	return NULL;832}833 834static int stop_tracing;835static void stop_hist(int sig)836{837	stop_tracing = 1;838}839 840/*841 * osnoise_hist_set_signals - handles the signal to stop the tool842 */843static void844osnoise_hist_set_signals(struct osnoise_hist_params *params)845{846	signal(SIGINT, stop_hist);847	if (params->duration) {848		signal(SIGALRM, stop_hist);849		alarm(params->duration);850	}851}852 853int osnoise_hist_main(int argc, char *argv[])854{855	struct osnoise_hist_params *params;856	struct osnoise_tool *record = NULL;857	struct osnoise_tool *tool = NULL;858	struct trace_instance *trace;859	int return_value = 1;860	int retval;861 862	params = osnoise_hist_parse_args(argc, argv);863	if (!params)864		exit(1);865 866	tool = osnoise_init_hist(params);867	if (!tool) {868		err_msg("Could not init osnoise hist\n");869		goto out_exit;870	}871 872	retval = osnoise_hist_apply_config(tool, params);873	if (retval) {874		err_msg("Could not apply config\n");875		goto out_destroy;876	}877 878	trace = &tool->trace;879 880	retval = enable_osnoise(trace);881	if (retval) {882		err_msg("Failed to enable osnoise tracer\n");883		goto out_destroy;884	}885 886	retval = osnoise_init_trace_hist(tool);887	if (retval)888		goto out_destroy;889 890	if (params->set_sched) {891		retval = set_comm_sched_attr("osnoise/", &params->sched_param);892		if (retval) {893			err_msg("Failed to set sched parameters\n");894			goto out_free;895		}896	}897 898	if (params->cgroup) {899		retval = set_comm_cgroup("timerlat/", params->cgroup_name);900		if (!retval) {901			err_msg("Failed to move threads to cgroup\n");902			goto out_free;903		}904	}905 906	if (params->trace_output) {907		record = osnoise_init_trace_tool("osnoise");908		if (!record) {909			err_msg("Failed to enable the trace instance\n");910			goto out_free;911		}912 913		if (params->events) {914			retval = trace_events_enable(&record->trace, params->events);915			if (retval)916				goto out_hist;917		}918 919		if (params->buffer_size > 0) {920			retval = trace_set_buffer_size(&record->trace, params->buffer_size);921			if (retval)922				goto out_hist;923		}924	}925 926	/*927	 * Start the tracer here, after having set all instances.928	 *929	 * Let the trace instance start first for the case of hitting a stop930	 * tracing while enabling other instances. The trace instance is the931	 * one with most valuable information.932	 */933	if (params->trace_output)934		trace_instance_start(&record->trace);935	trace_instance_start(trace);936 937	if (params->warmup > 0) {938		debug_msg("Warming up for %d seconds\n", params->warmup);939		sleep(params->warmup);940		if (stop_tracing)941			goto out_hist;942 943		/*944		 * Clean up the buffer. The osnoise workload do not run945		 * with tracing off to avoid creating a performance penalty946		 * when not needed.947		 */948		retval = tracefs_instance_file_write(trace->inst, "trace", "");949		if (retval < 0) {950			debug_msg("Error cleaning up the buffer");951			goto out_hist;952		}953 954	}955 956	tool->start_time = time(NULL);957	osnoise_hist_set_signals(params);958 959	while (!stop_tracing) {960		sleep(params->sleep_time);961 962		retval = tracefs_iterate_raw_events(trace->tep,963						    trace->inst,964						    NULL,965						    0,966						    collect_registered_events,967						    trace);968		if (retval < 0) {969			err_msg("Error iterating on events\n");970			goto out_hist;971		}972 973		if (trace_is_off(&tool->trace, &record->trace))974			break;975	}976 977	osnoise_read_trace_hist(tool);978 979	osnoise_print_stats(params, tool);980 981	return_value = 0;982 983	if (trace_is_off(&tool->trace, &record->trace)) {984		printf("rtla osnoise hit stop tracing\n");985		if (params->trace_output) {986			printf("  Saving trace to %s\n", params->trace_output);987			save_trace_to_file(record->trace.inst, params->trace_output);988		}989	}990 991out_hist:992	trace_events_destroy(&record->trace, params->events);993	params->events = NULL;994out_free:995	osnoise_free_histogram(tool->data);996out_destroy:997	osnoise_destroy_tool(record);998	osnoise_destroy_tool(tool);999	free(params);1000out_exit:1001	exit(return_value);1002}1003