6977 lines · 150.8 KB · C
RawDownload
// SPDX-License-Identifier: LGPL-2.1/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * *  The parts for function graph printing was taken and modified from the *  Linux Kernel that were written by *    - Copyright (C) 2009  Frederic Weisbecker, *  Frederic Weisbecker gave his permission to relicense the code to *  the Lesser General Public License. */#include <inttypes.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>#include <ctype.h>#include <errno.h>#include <stdint.h>#include <limits.h>#include <linux/time64.h> #include <netinet/in.h>#include "event-parse.h" #include "event-parse-local.h"#include "event-utils.h"#include "trace-seq.h" static const char *input_buf;static unsigned long long input_buf_ptr;static unsigned long long input_buf_siz; static int is_flag_field;static int is_symbolic_field; static int show_warning = 1; #define do_warning(fmt, ...)				\	do {						\		if (show_warning)			\			warning(fmt, ##__VA_ARGS__);	\	} while (0) #define do_warning_event(event, fmt, ...)			\	do {							\		if (!show_warning)				\			continue;				\								\		if (event)					\			warning("[%s:%s] " fmt, event->system,	\				event->name, ##__VA_ARGS__);	\		else						\			warning(fmt, ##__VA_ARGS__);		\	} while (0) static void init_input_buf(const char *buf, unsigned long long size){	input_buf = buf;	input_buf_siz = size;	input_buf_ptr = 0;} const char *tep_get_input_buf(void){	return input_buf;} unsigned long long tep_get_input_buf_ptr(void){	return input_buf_ptr;} struct event_handler {	struct event_handler		*next;	int				id;	const char			*sys_name;	const char			*event_name;	tep_event_handler_func		func;	void				*context;}; struct func_params {	struct func_params	*next;	enum tep_func_arg_type	type;}; struct tep_function_handler {	struct tep_function_handler	*next;	enum tep_func_arg_type		ret_type;	char				*name;	tep_func_handler		func;	struct func_params		*params;	int				nr_args;}; static unsigned long longprocess_defined_func(struct trace_seq *s, void *data, int size,		     struct tep_event *event, struct tep_print_arg *arg); static void free_func_handle(struct tep_function_handler *func); /** * tep_buffer_init - init buffer for parsing * @buf: buffer to parse * @size: the size of the buffer * * For use with tep_read_token(), this initializes the internal * buffer that tep_read_token() will parse. */void tep_buffer_init(const char *buf, unsigned long long size){	init_input_buf(buf, size);} void breakpoint(void){	static int x;	x++;} struct tep_print_arg *alloc_arg(void){	return calloc(1, sizeof(struct tep_print_arg));} struct tep_cmdline {	char *comm;	int pid;}; static int cmdline_cmp(const void *a, const void *b){	const struct tep_cmdline *ca = a;	const struct tep_cmdline *cb = b; 	if (ca->pid < cb->pid)		return -1;	if (ca->pid > cb->pid)		return 1; 	return 0;} struct cmdline_list {	struct cmdline_list	*next;	char			*comm;	int			pid;}; static int cmdline_init(struct tep_handle *tep){	struct cmdline_list *cmdlist = tep->cmdlist;	struct cmdline_list *item;	struct tep_cmdline *cmdlines;	int i; 	cmdlines = malloc(sizeof(*cmdlines) * tep->cmdline_count);	if (!cmdlines)		return -1; 	i = 0;	while (cmdlist) {		cmdlines[i].pid = cmdlist->pid;		cmdlines[i].comm = cmdlist->comm;		i++;		item = cmdlist;		cmdlist = cmdlist->next;		free(item);	} 	qsort(cmdlines, tep->cmdline_count, sizeof(*cmdlines), cmdline_cmp); 	tep->cmdlines = cmdlines;	tep->cmdlist = NULL; 	return 0;} static const char *find_cmdline(struct tep_handle *tep, int pid){	const struct tep_cmdline *comm;	struct tep_cmdline key; 	if (!pid)		return "<idle>"; 	if (!tep->cmdlines && cmdline_init(tep))		return "<not enough memory for cmdlines!>"; 	key.pid = pid; 	comm = bsearch(&key, tep->cmdlines, tep->cmdline_count,		       sizeof(*tep->cmdlines), cmdline_cmp); 	if (comm)		return comm->comm;	return "<...>";} /** * tep_is_pid_registered - return if a pid has a cmdline registered * @tep: a handle to the trace event parser context * @pid: The pid to check if it has a cmdline registered with. * * Returns true if the pid has a cmdline mapped to it * false otherwise. */bool tep_is_pid_registered(struct tep_handle *tep, int pid){	const struct tep_cmdline *comm;	struct tep_cmdline key; 	if (!pid)		return true; 	if (!tep->cmdlines && cmdline_init(tep))		return false; 	key.pid = pid; 	comm = bsearch(&key, tep->cmdlines, tep->cmdline_count,		       sizeof(*tep->cmdlines), cmdline_cmp); 	if (comm)		return true;	return false;} /* * If the command lines have been converted to an array, then * we must add this pid. This is much slower than when cmdlines * are added before the array is initialized. */static int add_new_comm(struct tep_handle *tep,			const char *comm, int pid, bool override){	struct tep_cmdline *cmdlines = tep->cmdlines;	struct tep_cmdline *cmdline;	struct tep_cmdline key;	char *new_comm; 	if (!pid)		return 0; 	/* avoid duplicates */	key.pid = pid; 	cmdline = bsearch(&key, tep->cmdlines, tep->cmdline_count,			  sizeof(*tep->cmdlines), cmdline_cmp);	if (cmdline) {		if (!override) {			errno = EEXIST;			return -1;		}		new_comm = strdup(comm);		if (!new_comm) {			errno = ENOMEM;			return -1;		}		free(cmdline->comm);		cmdline->comm = new_comm; 		return 0;	} 	cmdlines = realloc(cmdlines, sizeof(*cmdlines) * (tep->cmdline_count + 1));	if (!cmdlines) {		errno = ENOMEM;		return -1;	} 	cmdlines[tep->cmdline_count].comm = strdup(comm);	if (!cmdlines[tep->cmdline_count].comm) {		free(cmdlines);		errno = ENOMEM;		return -1;	} 	cmdlines[tep->cmdline_count].pid = pid;			if (cmdlines[tep->cmdline_count].comm)		tep->cmdline_count++; 	qsort(cmdlines, tep->cmdline_count, sizeof(*cmdlines), cmdline_cmp);	tep->cmdlines = cmdlines; 	return 0;} static int _tep_register_comm(struct tep_handle *tep,			      const char *comm, int pid, bool override){	struct cmdline_list *item; 	if (tep->cmdlines)		return add_new_comm(tep, comm, pid, override); 	item = malloc(sizeof(*item));	if (!item)		return -1; 	if (comm)		item->comm = strdup(comm);	else		item->comm = strdup("<...>");	if (!item->comm) {		free(item);		return -1;	}	item->pid = pid;	item->next = tep->cmdlist; 	tep->cmdlist = item;	tep->cmdline_count++; 	return 0;} /** * tep_register_comm - register a pid / comm mapping * @tep: a handle to the trace event parser context * @comm: the command line to register * @pid: the pid to map the command line to * * This adds a mapping to search for command line names with * a given pid. The comm is duplicated. If a command with the same pid * already exist, -1 is returned and errno is set to EEXIST */int tep_register_comm(struct tep_handle *tep, const char *comm, int pid){	return _tep_register_comm(tep, comm, pid, false);} /** * tep_override_comm - register a pid / comm mapping * @tep: a handle to the trace event parser context * @comm: the command line to register * @pid: the pid to map the command line to * * This adds a mapping to search for command line names with * a given pid. The comm is duplicated. If a command with the same pid * already exist, the command string is udapted with the new one */int tep_override_comm(struct tep_handle *tep, const char *comm, int pid){	if (!tep->cmdlines && cmdline_init(tep)) {		errno = ENOMEM;		return -1;	}	return _tep_register_comm(tep, comm, pid, true);} int tep_register_trace_clock(struct tep_handle *tep, const char *trace_clock){	tep->trace_clock = strdup(trace_clock);	if (!tep->trace_clock) {		errno = ENOMEM;		return -1;	}	return 0;} struct func_map {	unsigned long long		addr;	char				*func;	char				*mod;}; struct func_list {	struct func_list	*next;	unsigned long long	addr;	char			*func;	char			*mod;}; static int func_cmp(const void *a, const void *b){	const struct func_map *fa = a;	const struct func_map *fb = b; 	if (fa->addr < fb->addr)		return -1;	if (fa->addr > fb->addr)		return 1; 	return 0;} /* * We are searching for a record in between, not an exact * match. */static int func_bcmp(const void *a, const void *b){	const struct func_map *fa = a;	const struct func_map *fb = b; 	if ((fa->addr == fb->addr) || 	    (fa->addr > fb->addr &&	     fa->addr < (fb+1)->addr))		return 0; 	if (fa->addr < fb->addr)		return -1; 	return 1;} static int func_map_init(struct tep_handle *tep){	struct func_list *funclist;	struct func_list *item;	struct func_map *func_map;	int i; 	func_map = malloc(sizeof(*func_map) * (tep->func_count + 1));	if (!func_map)		return -1; 	funclist = tep->funclist; 	i = 0;	while (funclist) {		func_map[i].func = funclist->func;		func_map[i].addr = funclist->addr;		func_map[i].mod = funclist->mod;		i++;		item = funclist;		funclist = funclist->next;		free(item);	} 	qsort(func_map, tep->func_count, sizeof(*func_map), func_cmp); 	/*	 * Add a special record at the end.	 */	func_map[tep->func_count].func = NULL;	func_map[tep->func_count].addr = 0;	func_map[tep->func_count].mod = NULL; 	tep->func_map = func_map;	tep->funclist = NULL; 	return 0;} static struct func_map *__find_func(struct tep_handle *tep, unsigned long long addr){	struct func_map *func;	struct func_map key; 	if (!tep->func_map)		func_map_init(tep); 	key.addr = addr; 	func = bsearch(&key, tep->func_map, tep->func_count,		       sizeof(*tep->func_map), func_bcmp); 	return func;} struct func_resolver {	tep_func_resolver_t	*func;	void			*priv;	struct func_map		map;}; /** * tep_set_function_resolver - set an alternative function resolver * @tep: a handle to the trace event parser context * @resolver: function to be used * @priv: resolver function private state. * * Some tools may have already a way to resolve kernel functions, allow them to * keep using it instead of duplicating all the entries inside tep->funclist. */int tep_set_function_resolver(struct tep_handle *tep,			      tep_func_resolver_t *func, void *priv){	struct func_resolver *resolver = malloc(sizeof(*resolver)); 	if (resolver == NULL)		return -1; 	resolver->func = func;	resolver->priv = priv; 	free(tep->func_resolver);	tep->func_resolver = resolver; 	return 0;} /** * tep_reset_function_resolver - reset alternative function resolver

Lines 1–500 of 6977.