Path: blob/master/tools/tracing/rtla/src/osnoise_hist.c
26285 views
// SPDX-License-Identifier: GPL-2.01/*2* Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <[email protected]>3*/45#define _GNU_SOURCE6#include <getopt.h>7#include <stdlib.h>8#include <string.h>9#include <signal.h>10#include <unistd.h>11#include <errno.h>12#include <stdio.h>13#include <time.h>1415#include "osnoise.h"1617struct osnoise_hist_cpu {18int *samples;19int count;2021unsigned long long min_sample;22unsigned long long sum_sample;23unsigned long long max_sample;2425};2627struct osnoise_hist_data {28struct tracefs_hist *trace_hist;29struct osnoise_hist_cpu *hist;30int entries;31int bucket_size;32int nr_cpus;33};3435/*36* osnoise_free_histogram - free runtime data37*/38static void39osnoise_free_histogram(struct osnoise_hist_data *data)40{41int cpu;4243/* one histogram for IRQ and one for thread, per CPU */44for (cpu = 0; cpu < data->nr_cpus; cpu++) {45if (data->hist[cpu].samples)46free(data->hist[cpu].samples);47}4849/* one set of histograms per CPU */50if (data->hist)51free(data->hist);5253free(data);54}5556/*57* osnoise_alloc_histogram - alloc runtime data58*/59static struct osnoise_hist_data60*osnoise_alloc_histogram(int nr_cpus, int entries, int bucket_size)61{62struct osnoise_hist_data *data;63int cpu;6465data = calloc(1, sizeof(*data));66if (!data)67return NULL;6869data->entries = entries;70data->bucket_size = bucket_size;71data->nr_cpus = nr_cpus;7273data->hist = calloc(1, sizeof(*data->hist) * nr_cpus);74if (!data->hist)75goto cleanup;7677for (cpu = 0; cpu < nr_cpus; cpu++) {78data->hist[cpu].samples = calloc(1, sizeof(*data->hist->samples) * (entries + 1));79if (!data->hist[cpu].samples)80goto cleanup;81}8283/* set the min to max */84for (cpu = 0; cpu < nr_cpus; cpu++)85data->hist[cpu].min_sample = ~0;8687return data;8889cleanup:90osnoise_free_histogram(data);91return NULL;92}9394static void osnoise_hist_update_multiple(struct osnoise_tool *tool, int cpu,95unsigned long long duration, int count)96{97struct osnoise_params *params = tool->params;98struct osnoise_hist_data *data = tool->data;99unsigned long long total_duration;100int entries = data->entries;101int bucket;102int *hist;103104if (params->output_divisor)105duration = duration / params->output_divisor;106107bucket = duration / data->bucket_size;108109total_duration = duration * count;110111hist = data->hist[cpu].samples;112data->hist[cpu].count += count;113update_min(&data->hist[cpu].min_sample, &duration);114update_sum(&data->hist[cpu].sum_sample, &total_duration);115update_max(&data->hist[cpu].max_sample, &duration);116117if (bucket < entries)118hist[bucket] += count;119else120hist[entries] += count;121}122123/*124* osnoise_destroy_trace_hist - disable events used to collect histogram125*/126static void osnoise_destroy_trace_hist(struct osnoise_tool *tool)127{128struct osnoise_hist_data *data = tool->data;129130tracefs_hist_pause(tool->trace.inst, data->trace_hist);131tracefs_hist_destroy(tool->trace.inst, data->trace_hist);132}133134/*135* osnoise_init_trace_hist - enable events used to collect histogram136*/137static int osnoise_init_trace_hist(struct osnoise_tool *tool)138{139struct osnoise_params *params = tool->params;140struct osnoise_hist_data *data = tool->data;141int bucket_size;142char buff[128];143int retval = 0;144145/*146* Set the size of the bucket.147*/148bucket_size = params->output_divisor * params->bucket_size;149snprintf(buff, sizeof(buff), "duration.buckets=%d", bucket_size);150151data->trace_hist = tracefs_hist_alloc(tool->trace.tep, "osnoise", "sample_threshold",152buff, TRACEFS_HIST_KEY_NORMAL);153if (!data->trace_hist)154return 1;155156retval = tracefs_hist_add_key(data->trace_hist, "cpu", 0);157if (retval)158goto out_err;159160retval = tracefs_hist_start(tool->trace.inst, data->trace_hist);161if (retval)162goto out_err;163164return 0;165166out_err:167osnoise_destroy_trace_hist(tool);168return 1;169}170171/*172* osnoise_read_trace_hist - parse histogram file and file osnoise histogram173*/174static void osnoise_read_trace_hist(struct osnoise_tool *tool)175{176struct osnoise_hist_data *data = tool->data;177long long cpu, counter, duration;178char *content, *position;179180tracefs_hist_pause(tool->trace.inst, data->trace_hist);181182content = tracefs_event_file_read(tool->trace.inst, "osnoise",183"sample_threshold",184"hist", NULL);185if (!content)186return;187188position = content;189while (true) {190position = strstr(position, "duration: ~");191if (!position)192break;193position += strlen("duration: ~");194duration = get_llong_from_str(position);195if (duration == -1)196err_msg("error reading duration from histogram\n");197198position = strstr(position, "cpu:");199if (!position)200break;201position += strlen("cpu: ");202cpu = get_llong_from_str(position);203if (cpu == -1)204err_msg("error reading cpu from histogram\n");205206position = strstr(position, "hitcount:");207if (!position)208break;209position += strlen("hitcount: ");210counter = get_llong_from_str(position);211if (counter == -1)212err_msg("error reading counter from histogram\n");213214osnoise_hist_update_multiple(tool, cpu, duration, counter);215}216free(content);217}218219/*220* osnoise_hist_header - print the header of the tracer to the output221*/222static void osnoise_hist_header(struct osnoise_tool *tool)223{224struct osnoise_params *params = tool->params;225struct osnoise_hist_data *data = tool->data;226struct trace_seq *s = tool->trace.seq;227char duration[26];228int cpu;229230if (params->no_header)231return;232233get_duration(tool->start_time, duration, sizeof(duration));234trace_seq_printf(s, "# RTLA osnoise histogram\n");235trace_seq_printf(s, "# Time unit is %s (%s)\n",236params->output_divisor == 1 ? "nanoseconds" : "microseconds",237params->output_divisor == 1 ? "ns" : "us");238239trace_seq_printf(s, "# Duration: %s\n", duration);240241if (!params->no_index)242trace_seq_printf(s, "Index");243244for (cpu = 0; cpu < data->nr_cpus; cpu++) {245if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus))246continue;247248if (!data->hist[cpu].count)249continue;250251trace_seq_printf(s, " CPU-%03d", cpu);252}253trace_seq_printf(s, "\n");254255trace_seq_do_printf(s);256trace_seq_reset(s);257}258259/*260* osnoise_print_summary - print the summary of the hist data to the output261*/262static void263osnoise_print_summary(struct osnoise_params *params,264struct trace_instance *trace,265struct osnoise_hist_data *data)266{267int cpu;268269if (params->no_summary)270return;271272if (!params->no_index)273trace_seq_printf(trace->seq, "count:");274275for (cpu = 0; cpu < data->nr_cpus; cpu++) {276if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus))277continue;278279if (!data->hist[cpu].count)280continue;281282trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].count);283}284trace_seq_printf(trace->seq, "\n");285286if (!params->no_index)287trace_seq_printf(trace->seq, "min: ");288289for (cpu = 0; cpu < data->nr_cpus; cpu++) {290if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus))291continue;292293if (!data->hist[cpu].count)294continue;295296trace_seq_printf(trace->seq, "%9llu ", data->hist[cpu].min_sample);297298}299trace_seq_printf(trace->seq, "\n");300301if (!params->no_index)302trace_seq_printf(trace->seq, "avg: ");303304for (cpu = 0; cpu < data->nr_cpus; cpu++) {305if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus))306continue;307308if (!data->hist[cpu].count)309continue;310311if (data->hist[cpu].count)312trace_seq_printf(trace->seq, "%9.2f ",313((double) data->hist[cpu].sum_sample) / data->hist[cpu].count);314else315trace_seq_printf(trace->seq, " - ");316}317trace_seq_printf(trace->seq, "\n");318319if (!params->no_index)320trace_seq_printf(trace->seq, "max: ");321322for (cpu = 0; cpu < data->nr_cpus; cpu++) {323if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus))324continue;325326if (!data->hist[cpu].count)327continue;328329trace_seq_printf(trace->seq, "%9llu ", data->hist[cpu].max_sample);330331}332trace_seq_printf(trace->seq, "\n");333trace_seq_do_printf(trace->seq);334trace_seq_reset(trace->seq);335}336337/*338* osnoise_print_stats - print data for all CPUs339*/340static void341osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool)342{343struct osnoise_hist_data *data = tool->data;344struct trace_instance *trace = &tool->trace;345int has_samples = 0;346int bucket, cpu;347int total;348349osnoise_hist_header(tool);350351for (bucket = 0; bucket < data->entries; bucket++) {352total = 0;353354if (!params->no_index)355trace_seq_printf(trace->seq, "%-6d",356bucket * data->bucket_size);357358for (cpu = 0; cpu < data->nr_cpus; cpu++) {359if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus))360continue;361362if (!data->hist[cpu].count)363continue;364365total += data->hist[cpu].samples[bucket];366trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].samples[bucket]);367}368369if (total == 0 && !params->with_zeros) {370trace_seq_reset(trace->seq);371continue;372}373374/* There are samples above the threshold */375has_samples = 1;376trace_seq_printf(trace->seq, "\n");377trace_seq_do_printf(trace->seq);378trace_seq_reset(trace->seq);379}380381/*382* If no samples were recorded, skip calculations, print zeroed statistics383* and return.384*/385if (!has_samples) {386trace_seq_reset(trace->seq);387trace_seq_printf(trace->seq, "over: 0\ncount: 0\nmin: 0\navg: 0\nmax: 0\n");388trace_seq_do_printf(trace->seq);389trace_seq_reset(trace->seq);390return;391}392393if (!params->no_index)394trace_seq_printf(trace->seq, "over: ");395396for (cpu = 0; cpu < data->nr_cpus; cpu++) {397if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus))398continue;399400if (!data->hist[cpu].count)401continue;402403trace_seq_printf(trace->seq, "%9d ",404data->hist[cpu].samples[data->entries]);405}406trace_seq_printf(trace->seq, "\n");407trace_seq_do_printf(trace->seq);408trace_seq_reset(trace->seq);409410osnoise_print_summary(params, trace, data);411osnoise_report_missed_events(tool);412}413414/*415* osnoise_hist_usage - prints osnoise hist usage message416*/417static void osnoise_hist_usage(char *usage)418{419int i;420421static const char * const msg[] = {422"",423" usage: rtla osnoise hist [-h] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",424" [-T us] [-t[file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",425" [-c cpu-list] [-H cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\",426" [--no-index] [--with-zeros] [-C[=cgroup_name]] [--warm-up]",427"",428" -h/--help: print this menu",429" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",430" -p/--period us: osnoise period in us",431" -r/--runtime us: osnoise runtime in us",432" -s/--stop us: stop trace if a single sample is higher than the argument in us",433" -S/--stop-total us: stop trace if the total sample is higher than the argument in us",434" -T/--threshold us: the minimum delta to be considered a noise",435" -c/--cpus cpu-list: list of cpus to run osnoise threads",436" -H/--house-keeping cpus: run rtla control threads only on the given cpus",437" -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",438" -d/--duration time[s|m|h|d]: duration of the session",439" -D/--debug: print debug info",440" -t/--trace[file]: save the stopped trace to [file|osnoise_trace.txt]",441" -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",442" --filter <filter>: enable a trace event filter to the previous -e event",443" --trigger <trigger>: enable a trace event trigger to the previous -e event",444" -b/--bucket-size N: set the histogram bucket size (default 1)",445" -E/--entries N: set the number of entries of the histogram (default 256)",446" --no-header: do not print header",447" --no-summary: do not print summary",448" --no-index: do not print index",449" --with-zeros: print zero only entries",450" -P/--priority o:prio|r:prio|f:prio|d:runtime:period: set scheduling parameters",451" o:prio - use SCHED_OTHER with prio",452" r:prio - use SCHED_RR with prio",453" f:prio - use SCHED_FIFO with prio",454" d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",455" in nanoseconds",456" --warm-up: let the workload run for s seconds before collecting data",457" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",458NULL,459};460461if (usage)462fprintf(stderr, "%s\n", usage);463464fprintf(stderr, "rtla osnoise hist: a per-cpu histogram of the OS noise (version %s)\n",465VERSION);466467for (i = 0; msg[i]; i++)468fprintf(stderr, "%s\n", msg[i]);469470if (usage)471exit(EXIT_FAILURE);472473exit(EXIT_SUCCESS);474}475476/*477* osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters478*/479static struct osnoise_params480*osnoise_hist_parse_args(int argc, char *argv[])481{482struct osnoise_params *params;483struct trace_events *tevent;484int retval;485int c;486487params = calloc(1, sizeof(*params));488if (!params)489exit(1);490491/* display data in microseconds */492params->output_divisor = 1000;493params->bucket_size = 1;494params->entries = 256;495496while (1) {497static struct option long_options[] = {498{"auto", required_argument, 0, 'a'},499{"bucket-size", required_argument, 0, 'b'},500{"entries", required_argument, 0, 'E'},501{"cpus", required_argument, 0, 'c'},502{"cgroup", optional_argument, 0, 'C'},503{"debug", no_argument, 0, 'D'},504{"duration", required_argument, 0, 'd'},505{"house-keeping", required_argument, 0, 'H'},506{"help", no_argument, 0, 'h'},507{"period", required_argument, 0, 'p'},508{"priority", required_argument, 0, 'P'},509{"runtime", required_argument, 0, 'r'},510{"stop", required_argument, 0, 's'},511{"stop-total", required_argument, 0, 'S'},512{"trace", optional_argument, 0, 't'},513{"event", required_argument, 0, 'e'},514{"threshold", required_argument, 0, 'T'},515{"no-header", no_argument, 0, '0'},516{"no-summary", no_argument, 0, '1'},517{"no-index", no_argument, 0, '2'},518{"with-zeros", no_argument, 0, '3'},519{"trigger", required_argument, 0, '4'},520{"filter", required_argument, 0, '5'},521{"warm-up", required_argument, 0, '6'},522{"trace-buffer-size", required_argument, 0, '7'},523{0, 0, 0, 0}524};525526/* getopt_long stores the option index here. */527int option_index = 0;528529c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:p:P:r:s:S:t::T:01234:5:6:7:",530long_options, &option_index);531532/* detect the end of the options. */533if (c == -1)534break;535536switch (c) {537case 'a':538/* set sample stop to auto_thresh */539params->stop_us = get_llong_from_str(optarg);540541/* set sample threshold to 1 */542params->threshold = 1;543544/* set trace */545params->trace_output = "osnoise_trace.txt";546547break;548case 'b':549params->bucket_size = get_llong_from_str(optarg);550if ((params->bucket_size == 0) || (params->bucket_size >= 1000000))551osnoise_hist_usage("Bucket size needs to be > 0 and <= 1000000\n");552break;553case 'c':554retval = parse_cpu_set(optarg, ¶ms->monitored_cpus);555if (retval)556osnoise_hist_usage("\nInvalid -c cpu list\n");557params->cpus = optarg;558break;559case 'C':560params->cgroup = 1;561if (!optarg) {562/* will inherit this cgroup */563params->cgroup_name = NULL;564} else if (*optarg == '=') {565/* skip the = */566params->cgroup_name = ++optarg;567}568break;569case 'D':570config_debug = 1;571break;572case 'd':573params->duration = parse_seconds_duration(optarg);574if (!params->duration)575osnoise_hist_usage("Invalid -D duration\n");576break;577case 'e':578tevent = trace_event_alloc(optarg);579if (!tevent) {580err_msg("Error alloc trace event");581exit(EXIT_FAILURE);582}583584if (params->events)585tevent->next = params->events;586587params->events = tevent;588break;589case 'E':590params->entries = get_llong_from_str(optarg);591if ((params->entries < 10) || (params->entries > 9999999))592osnoise_hist_usage("Entries must be > 10 and < 9999999\n");593break;594case 'h':595case '?':596osnoise_hist_usage(NULL);597break;598case 'H':599params->hk_cpus = 1;600retval = parse_cpu_set(optarg, ¶ms->hk_cpu_set);601if (retval) {602err_msg("Error parsing house keeping CPUs\n");603exit(EXIT_FAILURE);604}605break;606case 'p':607params->period = get_llong_from_str(optarg);608if (params->period > 10000000)609osnoise_hist_usage("Period longer than 10 s\n");610break;611case 'P':612retval = parse_prio(optarg, ¶ms->sched_param);613if (retval == -1)614osnoise_hist_usage("Invalid -P priority");615params->set_sched = 1;616break;617case 'r':618params->runtime = get_llong_from_str(optarg);619if (params->runtime < 100)620osnoise_hist_usage("Runtime shorter than 100 us\n");621break;622case 's':623params->stop_us = get_llong_from_str(optarg);624break;625case 'S':626params->stop_total_us = get_llong_from_str(optarg);627break;628case 'T':629params->threshold = get_llong_from_str(optarg);630break;631case 't':632if (optarg) {633if (optarg[0] == '=')634params->trace_output = &optarg[1];635else636params->trace_output = &optarg[0];637} else if (optind < argc && argv[optind][0] != '0')638params->trace_output = argv[optind];639else640params->trace_output = "osnoise_trace.txt";641break;642case '0': /* no header */643params->no_header = 1;644break;645case '1': /* no summary */646params->no_summary = 1;647break;648case '2': /* no index */649params->no_index = 1;650break;651case '3': /* with zeros */652params->with_zeros = 1;653break;654case '4': /* trigger */655if (params->events) {656retval = trace_event_add_trigger(params->events, optarg);657if (retval) {658err_msg("Error adding trigger %s\n", optarg);659exit(EXIT_FAILURE);660}661} else {662osnoise_hist_usage("--trigger requires a previous -e\n");663}664break;665case '5': /* filter */666if (params->events) {667retval = trace_event_add_filter(params->events, optarg);668if (retval) {669err_msg("Error adding filter %s\n", optarg);670exit(EXIT_FAILURE);671}672} else {673osnoise_hist_usage("--filter requires a previous -e\n");674}675break;676case '6':677params->warmup = get_llong_from_str(optarg);678break;679case '7':680params->buffer_size = get_llong_from_str(optarg);681break;682default:683osnoise_hist_usage("Invalid option");684}685}686687if (geteuid()) {688err_msg("rtla needs root permission\n");689exit(EXIT_FAILURE);690}691692if (params->no_index && !params->with_zeros)693osnoise_hist_usage("no-index set and with-zeros not set - it does not make sense");694695return params;696}697698/*699* osnoise_hist_apply_config - apply the hist configs to the initialized tool700*/701static int702osnoise_hist_apply_config(struct osnoise_tool *tool, struct osnoise_params *params)703{704int retval;705706retval = osnoise_apply_config(tool, params);707if (retval)708goto out_err;709710return 0;711712out_err:713return -1;714}715716/*717* osnoise_init_hist - initialize a osnoise hist tool with parameters718*/719static struct osnoise_tool720*osnoise_init_hist(struct osnoise_params *params)721{722struct osnoise_tool *tool;723int nr_cpus;724725nr_cpus = sysconf(_SC_NPROCESSORS_CONF);726727tool = osnoise_init_tool("osnoise_hist");728if (!tool)729return NULL;730731tool->data = osnoise_alloc_histogram(nr_cpus, params->entries, params->bucket_size);732if (!tool->data)733goto out_err;734735tool->params = params;736737return tool;738739out_err:740osnoise_destroy_tool(tool);741return NULL;742}743744static int stop_tracing;745static void stop_hist(int sig)746{747stop_tracing = 1;748}749750/*751* osnoise_hist_set_signals - handles the signal to stop the tool752*/753static void754osnoise_hist_set_signals(struct osnoise_params *params)755{756signal(SIGINT, stop_hist);757if (params->duration) {758signal(SIGALRM, stop_hist);759alarm(params->duration);760}761}762763int osnoise_hist_main(int argc, char *argv[])764{765struct osnoise_params *params;766struct osnoise_tool *record = NULL;767struct osnoise_tool *tool = NULL;768enum result return_value = ERROR;769struct trace_instance *trace;770int retval;771772params = osnoise_hist_parse_args(argc, argv);773if (!params)774exit(1);775776tool = osnoise_init_hist(params);777if (!tool) {778err_msg("Could not init osnoise hist\n");779goto out_exit;780}781782retval = osnoise_hist_apply_config(tool, params);783if (retval) {784err_msg("Could not apply config\n");785goto out_destroy;786}787788trace = &tool->trace;789790retval = enable_osnoise(trace);791if (retval) {792err_msg("Failed to enable osnoise tracer\n");793goto out_destroy;794}795796retval = osnoise_init_trace_hist(tool);797if (retval)798goto out_destroy;799800if (params->set_sched) {801retval = set_comm_sched_attr("osnoise/", ¶ms->sched_param);802if (retval) {803err_msg("Failed to set sched parameters\n");804goto out_free;805}806}807808if (params->cgroup) {809retval = set_comm_cgroup("timerlat/", params->cgroup_name);810if (!retval) {811err_msg("Failed to move threads to cgroup\n");812goto out_free;813}814}815816if (params->trace_output) {817record = osnoise_init_trace_tool("osnoise");818if (!record) {819err_msg("Failed to enable the trace instance\n");820goto out_free;821}822823if (params->events) {824retval = trace_events_enable(&record->trace, params->events);825if (retval)826goto out_hist;827}828829if (params->buffer_size > 0) {830retval = trace_set_buffer_size(&record->trace, params->buffer_size);831if (retval)832goto out_hist;833}834}835836/*837* Start the tracer here, after having set all instances.838*839* Let the trace instance start first for the case of hitting a stop840* tracing while enabling other instances. The trace instance is the841* one with most valuable information.842*/843if (params->trace_output)844trace_instance_start(&record->trace);845trace_instance_start(trace);846847if (params->warmup > 0) {848debug_msg("Warming up for %d seconds\n", params->warmup);849sleep(params->warmup);850if (stop_tracing)851goto out_hist;852853/*854* Clean up the buffer. The osnoise workload do not run855* with tracing off to avoid creating a performance penalty856* when not needed.857*/858retval = tracefs_instance_file_write(trace->inst, "trace", "");859if (retval < 0) {860debug_msg("Error cleaning up the buffer");861goto out_hist;862}863864}865866tool->start_time = time(NULL);867osnoise_hist_set_signals(params);868869while (!stop_tracing) {870sleep(params->sleep_time);871872retval = tracefs_iterate_raw_events(trace->tep,873trace->inst,874NULL,8750,876collect_registered_events,877trace);878if (retval < 0) {879err_msg("Error iterating on events\n");880goto out_hist;881}882883if (osnoise_trace_is_off(tool, record))884break;885}886887osnoise_read_trace_hist(tool);888889osnoise_print_stats(params, tool);890891return_value = PASSED;892893if (osnoise_trace_is_off(tool, record)) {894printf("rtla osnoise hit stop tracing\n");895save_trace_to_file(record ? record->trace.inst : NULL,896params->trace_output);897return_value = FAILED;898}899900out_hist:901trace_events_destroy(&record->trace, params->events);902params->events = NULL;903out_free:904osnoise_free_histogram(tool->data);905out_destroy:906osnoise_destroy_tool(record);907osnoise_destroy_tool(tool);908free(params);909out_exit:910exit(return_value);911}912913914