597 lines · 13.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#define _GNU_SOURCE#include <sys/sendfile.h>#include <tracefs.h>#include <stdlib.h>#include <unistd.h>#include <errno.h> #include "trace.h"#include "utils.h" /* * enable_tracer_by_name - enable a tracer on the given instance */int enable_tracer_by_name(struct tracefs_instance *inst, const char *tracer_name){	enum tracefs_tracers tracer;	int retval; 	tracer = TRACEFS_TRACER_CUSTOM; 	debug_msg("Enabling %s tracer\n", tracer_name); 	retval = tracefs_tracer_set(inst, tracer, tracer_name);	if (retval < 0) {		if (errno == ENODEV)			err_msg("Tracer %s not found!\n", tracer_name); 		err_msg("Failed to enable the %s tracer\n", tracer_name);		return -1;	} 	return 0;} /* * disable_tracer - set nop tracer to the insta */void disable_tracer(struct tracefs_instance *inst){	enum tracefs_tracers t = TRACEFS_TRACER_NOP;	int retval; 	retval = tracefs_tracer_set(inst, t);	if (retval < 0)		err_msg("Oops, error disabling tracer\n");} /* * create_instance - create a trace instance with *instance_name */struct tracefs_instance *create_instance(char *instance_name){	return tracefs_instance_create(instance_name);} /* * destroy_instance - remove a trace instance and free the data */void destroy_instance(struct tracefs_instance *inst){	tracefs_instance_destroy(inst);	tracefs_instance_free(inst);} /* * save_trace_to_file - save the trace output of the instance to the file */int save_trace_to_file(struct tracefs_instance *inst, const char *filename){	const char *file = "trace";	mode_t mode = 0644;	char buffer[4096];	int out_fd, in_fd;	int retval = -1;	ssize_t n_read;	ssize_t n_written; 	if (!inst || !filename)		return 0; 	in_fd = tracefs_instance_file_open(inst, file, O_RDONLY);	if (in_fd < 0) {		err_msg("Failed to open trace file\n");		return -1;	} 	printf("  Saving trace to %s\n", filename);	out_fd = creat(filename, mode);	if (out_fd < 0) {		err_msg("Failed to create output file %s\n", filename);		goto out_close_in;	} 	for (;;) {		n_read = read(in_fd, buffer, sizeof(buffer));		if (n_read < 0) {			if (errno == EINTR)				continue;			err_msg("Error reading trace file: %s\n", strerror(errno));			goto out_close;		}		if (n_read == 0)			break; 		n_written = 0;		while (n_written < n_read) {			const ssize_t w = write(out_fd, buffer + n_written, n_read - n_written); 			if (w < 0) {				if (errno == EINTR)					continue;				err_msg("Error writing trace file: %s\n", strerror(errno));				goto out_close;			}			n_written += w;		}	} 	retval = 0;out_close:	close(out_fd);out_close_in:	close(in_fd);	return retval;} /* * collect_registered_events - call the existing callback function for the event * * If an event has a registered callback function, call it. * Otherwise, ignore the event. */intcollect_registered_events(struct tep_event *event, struct tep_record *record,			  int cpu, void *context){	struct trace_instance *trace = context;	struct trace_seq *s = trace->seq; 	trace->processed_events++; 	if (!event->handler)		return 0; 	event->handler(s, record, event, context); 	return 0;} /* * collect_missed_events - record number of missed events * * If rtla cannot keep up with events generated by tracer, events are going * to fall out of the ring buffer. * Collect how many events were missed so it can be reported to the user. */static intcollect_missed_events(struct tep_event *event, struct tep_record *record,		      int cpu, void *context){	struct trace_instance *trace = context; 	if (trace->missed_events == UINT64_MAX)		return 0; 	if (record->missed_events > 0)		trace->missed_events += record->missed_events;	else		/* Events missed but no data on how many */		trace->missed_events = UINT64_MAX; 	return 0;} /* * trace_instance_destroy - destroy and free a rtla trace instance */void trace_instance_destroy(struct trace_instance *trace){	if (trace->inst) {		disable_tracer(trace->inst);		destroy_instance(trace->inst);		trace->inst = NULL;	} 	if (trace->seq) {		free(trace->seq);		trace->seq = NULL;	} 	if (trace->tep) {		tep_free(trace->tep);		trace->tep = NULL;	}} /* * trace_instance_init - create an rtla trace instance * * It is more than the tracefs instance, as it contains other * things required for the tracing, such as the local events and * a seq file. * * Note that the trace instance is returned disabled. This allows * the tool to apply some other configs, like setting priority * to the kernel threads, before starting generating trace entries. */int trace_instance_init(struct trace_instance *trace, char *tool_name){	trace->seq = calloc_fatal(1, sizeof(*trace->seq)); 	trace_seq_init(trace->seq); 	trace->inst = create_instance(tool_name);	if (!trace->inst)		goto out_err; 	trace->tep = tracefs_local_events(NULL);	if (!trace->tep)		goto out_err; 	/*	 * Let the main enable the record after setting some other	 * things such as the priority of the tracer's threads.	 */	tracefs_trace_off(trace->inst); 	/*	 * Collect the number of events missed due to tracefs buffer	 * overflow.	 */	trace->missed_events = 0;	tracefs_follow_missed_events(trace->inst,				     collect_missed_events,				     trace); 	trace->processed_events = 0; 	return 0; out_err:	trace_instance_destroy(trace);	return 1;} /* * trace_instance_start - start tracing a given rtla instance */int trace_instance_start(struct trace_instance *trace){	return tracefs_trace_on(trace->inst);} /* * trace_instance_stop - stop tracing a given rtla instance */int trace_instance_stop(struct trace_instance *trace){	return tracefs_trace_off(trace->inst);} /* * trace_events_free - free a list of trace events */static void trace_events_free(struct trace_events *events){	struct trace_events *tevent = events;	struct trace_events *free_event; 	while (tevent) {		free_event = tevent; 		tevent = tevent->next; 		if (free_event->filter)			free(free_event->filter);		if (free_event->trigger)			free(free_event->trigger);		free(free_event->system);		free(free_event);	}} /* * trace_event_alloc - alloc and parse a single trace event */struct trace_events *trace_event_alloc(const char *event_string){	struct trace_events *tevent; 	tevent = calloc_fatal(1, sizeof(*tevent)); 	tevent->system = strdup_fatal(event_string); 	tevent->event = strstr(tevent->system, ":");	if (tevent->event) {		*tevent->event = '\0';		tevent->event = &tevent->event[1];	} 	return tevent;} /* * trace_event_add_filter - record an event filter */void trace_event_add_filter(struct trace_events *event, char *filter){	if (event->filter)		free(event->filter); 	event->filter = strdup_fatal(filter);} /* * trace_event_add_trigger - record an event trigger action */void trace_event_add_trigger(struct trace_events *event, char *trigger){	if (event->trigger)		free(event->trigger); 	event->trigger = strdup_fatal(trigger);} /* * trace_event_disable_filter - disable an event filter */static void trace_event_disable_filter(struct trace_instance *instance,				       struct trace_events *tevent){	char filter[MAX_PATH];	int retval; 	if (!tevent->filter)		return; 	if (!tevent->filter_enabled)		return; 	debug_msg("Disabling %s:%s filter %s\n", tevent->system,		  tevent->event ? : "*", tevent->filter); 	snprintf(filter, ARRAY_SIZE(filter), "!%s\n", tevent->filter); 	retval = tracefs_event_file_write(instance->inst, tevent->system,					  tevent->event, "filter", filter);	if (retval < 0)		err_msg("Error disabling %s:%s filter %s\n", tevent->system,			tevent->event ? : "*", tevent->filter);} /* * trace_event_save_hist - save the content of an event hist * * If the trigger is a hist: one, save the content of the hist file. */static void trace_event_save_hist(struct trace_instance *instance,				  struct trace_events *tevent){	size_t index, hist_len;	mode_t mode = 0644;	char path[MAX_PATH];	char *hist;	int out_fd; 	if (!tevent)		return; 	/* trigger enables hist */	if (!tevent->trigger)		return; 	/* is this a hist: trigger? */	if (!str_has_prefix(tevent->trigger, "hist:"))		return; 	snprintf(path, ARRAY_SIZE(path), "%s_%s_hist.txt", tevent->system, tevent->event); 	printf("  Saving event %s:%s hist to %s\n", tevent->system, tevent->event, path); 	out_fd = creat(path, mode);	if (out_fd < 0) {		err_msg("  Failed to create %s output file\n", path);		return;	} 	hist = tracefs_event_file_read(instance->inst, tevent->system, tevent->event, "hist", 0);	if (!hist) {		err_msg("  Failed to read %s:%s hist file\n", tevent->system, tevent->event);		goto out_close;	} 	index = 0;	hist_len = strlen(hist);	do {		const ssize_t written = write(out_fd, &hist[index], hist_len - index); 		if (written < 0) {			if (errno == EINTR)				continue;			err_msg("  Error writing hist file: %s\n", strerror(errno));			break;		}		index += written;	} while (index < hist_len); 	free(hist);out_close:	close(out_fd);} /* * trace_event_disable_trigger - disable an event trigger */static void trace_event_disable_trigger(struct trace_instance *instance,					struct trace_events *tevent){	char trigger[MAX_PATH];	int retval; 	if (!tevent->trigger)		return; 	if (!tevent->trigger_enabled)		return; 	debug_msg("Disabling %s:%s trigger %s\n", tevent->system,		  tevent->event ? : "*", tevent->trigger); 	trace_event_save_hist(instance, tevent); 	snprintf(trigger, ARRAY_SIZE(trigger), "!%s\n", tevent->trigger); 	retval = tracefs_event_file_write(instance->inst, tevent->system,					  tevent->event, "trigger", trigger);	if (retval < 0)		err_msg("Error disabling %s:%s trigger %s\n", tevent->system,			tevent->event ? : "*", tevent->trigger);} /* * trace_events_disable - disable all trace events */void trace_events_disable(struct trace_instance *instance,			  struct trace_events *events){	struct trace_events *tevent = events; 	if (!events)		return; 	while (tevent) {		debug_msg("Disabling event %s:%s\n", tevent->system, tevent->event ? : "*");		if (tevent->enabled) {			trace_event_disable_filter(instance, tevent);			trace_event_disable_trigger(instance, tevent);			tracefs_event_disable(instance->inst, tevent->system, tevent->event);		} 		tevent->enabled = 0;		tevent = tevent->next;	}} /* * trace_event_enable_filter - enable an event filter associated with an event */static int trace_event_enable_filter(struct trace_instance *instance,				     struct trace_events *tevent){	char filter[MAX_PATH];	int retval; 	if (!tevent->filter)		return 0; 	if (!tevent->event) {		err_msg("Filter %s applies only for single events, not for all %s:* events\n",			tevent->filter, tevent->system);		return 1;	} 	snprintf(filter, ARRAY_SIZE(filter), "%s\n", tevent->filter); 	debug_msg("Enabling %s:%s filter %s\n", tevent->system,		  tevent->event ? : "*", tevent->filter); 	retval = tracefs_event_file_write(instance->inst, tevent->system,					  tevent->event, "filter", filter);	if (retval < 0) {		err_msg("Error enabling %s:%s filter %s\n", tevent->system,			tevent->event ? : "*", tevent->filter);		return 1;	} 	tevent->filter_enabled = 1;	return 0;}

Lines 1–500 of 597.