6865 lines · 147.4 KB · C
RawDownload
/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License (not later!) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not,  see <http://www.gnu.org/licenses> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * *  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/string.h>#include <linux/time64.h> #include <netinet/in.h>#include "event-parse.h"#include "event-utils.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 *pevent_get_input_buf(void){	return input_buf;} unsigned long long pevent_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;	pevent_event_handler_func	func;	void				*context;}; struct pevent_func_params {	struct pevent_func_params	*next;	enum pevent_func_arg_type	type;}; struct pevent_function_handler {	struct pevent_function_handler	*next;	enum pevent_func_arg_type	ret_type;	char				*name;	pevent_func_handler		func;	struct pevent_func_params	*params;	int				nr_args;}; static unsigned long longprocess_defined_func(struct trace_seq *s, void *data, int size,		     struct event_format *event, struct print_arg *arg); static void free_func_handle(struct pevent_function_handler *func); /** * pevent_buffer_init - init buffer for parsing * @buf: buffer to parse * @size: the size of the buffer * * For use with pevent_read_token(), this initializes the internal * buffer that pevent_read_token() will parse. */void pevent_buffer_init(const char *buf, unsigned long long size){	init_input_buf(buf, size);} void breakpoint(void){	static int x;	x++;} struct print_arg *alloc_arg(void){	return calloc(1, sizeof(struct print_arg));} struct cmdline {	char *comm;	int pid;}; static int cmdline_cmp(const void *a, const void *b){	const struct cmdline *ca = a;	const struct 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 pevent *pevent){	struct cmdline_list *cmdlist = pevent->cmdlist;	struct cmdline_list *item;	struct cmdline *cmdlines;	int i; 	cmdlines = malloc(sizeof(*cmdlines) * pevent->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, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp); 	pevent->cmdlines = cmdlines;	pevent->cmdlist = NULL; 	return 0;} static const char *find_cmdline(struct pevent *pevent, int pid){	const struct cmdline *comm;	struct cmdline key; 	if (!pid)		return "<idle>"; 	if (!pevent->cmdlines && cmdline_init(pevent))		return "<not enough memory for cmdlines!>"; 	key.pid = pid; 	comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count,		       sizeof(*pevent->cmdlines), cmdline_cmp); 	if (comm)		return comm->comm;	return "<...>";} /** * pevent_pid_is_registered - return if a pid has a cmdline registered * @pevent: handle for the pevent * @pid: The pid to check if it has a cmdline registered with. * * Returns 1 if the pid has a cmdline mapped to it * 0 otherwise. */int pevent_pid_is_registered(struct pevent *pevent, int pid){	const struct cmdline *comm;	struct cmdline key; 	if (!pid)		return 1; 	if (!pevent->cmdlines && cmdline_init(pevent))		return 0; 	key.pid = pid; 	comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count,		       sizeof(*pevent->cmdlines), cmdline_cmp); 	if (comm)		return 1;	return 0;} /* * 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 pevent *pevent, const char *comm, int pid){	struct cmdline *cmdlines = pevent->cmdlines;	const struct cmdline *cmdline;	struct cmdline key; 	if (!pid)		return 0; 	/* avoid duplicates */	key.pid = pid; 	cmdline = bsearch(&key, pevent->cmdlines, pevent->cmdline_count,		       sizeof(*pevent->cmdlines), cmdline_cmp);	if (cmdline) {		errno = EEXIST;		return -1;	} 	cmdlines = realloc(cmdlines, sizeof(*cmdlines) * (pevent->cmdline_count + 1));	if (!cmdlines) {		errno = ENOMEM;		return -1;	} 	cmdlines[pevent->cmdline_count].comm = strdup(comm);	if (!cmdlines[pevent->cmdline_count].comm) {		free(cmdlines);		errno = ENOMEM;		return -1;	} 	cmdlines[pevent->cmdline_count].pid = pid;			if (cmdlines[pevent->cmdline_count].comm)		pevent->cmdline_count++; 	qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp);	pevent->cmdlines = cmdlines; 	return 0;} /** * pevent_register_comm - register a pid / comm mapping * @pevent: handle for the pevent * @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. */int pevent_register_comm(struct pevent *pevent, const char *comm, int pid){	struct cmdline_list *item; 	if (pevent->cmdlines)		return add_new_comm(pevent, comm, pid); 	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 = pevent->cmdlist; 	pevent->cmdlist = item;	pevent->cmdline_count++; 	return 0;} int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock){	pevent->trace_clock = strdup(trace_clock);	if (!pevent->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 pevent *pevent){	struct func_list *funclist;	struct func_list *item;	struct func_map *func_map;	int i; 	func_map = malloc(sizeof(*func_map) * (pevent->func_count + 1));	if (!func_map)		return -1; 	funclist = pevent->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, pevent->func_count, sizeof(*func_map), func_cmp); 	/*	 * Add a special record at the end.	 */	func_map[pevent->func_count].func = NULL;	func_map[pevent->func_count].addr = 0;	func_map[pevent->func_count].mod = NULL; 	pevent->func_map = func_map;	pevent->funclist = NULL; 	return 0;} static struct func_map *__find_func(struct pevent *pevent, unsigned long long addr){	struct func_map *func;	struct func_map key; 	if (!pevent->func_map)		func_map_init(pevent); 	key.addr = addr; 	func = bsearch(&key, pevent->func_map, pevent->func_count,		       sizeof(*pevent->func_map), func_bcmp); 	return func;} struct func_resolver {	pevent_func_resolver_t *func;	void		       *priv;	struct func_map	       map;}; /** * pevent_set_function_resolver - set an alternative function resolver * @pevent: handle for the pevent * @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 * pevent->funclist. */int pevent_set_function_resolver(struct pevent *pevent,				 pevent_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(pevent->func_resolver);	pevent->func_resolver = resolver; 	return 0;} /** * pevent_reset_function_resolver - reset alternative function resolver * @pevent: handle for the pevent * * Stop using whatever alternative resolver was set, use the default * one instead. */void pevent_reset_function_resolver(struct pevent *pevent){	free(pevent->func_resolver);	pevent->func_resolver = NULL;} static struct func_map *find_func(struct pevent *pevent, unsigned long long addr){	struct func_map *map; 	if (!pevent->func_resolver)		return __find_func(pevent, addr); 	map = &pevent->func_resolver->map;	map->mod  = NULL;	map->addr = addr;	map->func = pevent->func_resolver->func(pevent->func_resolver->priv,						&map->addr, &map->mod);	if (map->func == NULL)		return NULL;

Lines 1–500 of 6865.