summaryrefslogblamecommitdiffstats
path: root/tools/perf/util/trace-event-info.c
blob: 090e80d743dbedd99802b492e038b13b1a4edc90 (plain) (tree)



















                                                                             
                 
                   
                   









                      
                  
                    
                       
                         
 
                    
                        
                       
                  
 

                     


                                              
 

                                     
                                                    
 
                  
                                                                                
 
                    















                                         

                            
 


                                              















                                                


                                                          









                                              




                                                                        
                                          


                                  
                                                                    
                                                        

                                    


                                                      
                       



                                             
 
                                                                   



                                                              


                                          
                            
                                  

                                                          
                 
                        
 




                                                  
                                                                    
                                                  




                   

 
                                  
 
                   
                       
                       

                                                      


                                                            
                                

                                             









                                                            


                                                       


                                                             
                                

                                             











                                                             
                               
                   

 










                                                                   
                                                                          
 





                            
                





                                                      

                                                     

                                                        
                                 




                                                                         







                                                                   




                                                


                                       

                                                     

                                                        
                                 




                                                                         


                                                                   






                                                     

                             

                
                      
                   

 
                                                         

                   
                

                                                 

                                                       
 
                                           

                               

                   

 










                                                                     
                                                        







                            
                

                                          

                                                





                                                       

                                                     
                                                      

                                                          
                                 
                        

         




                                                


                                       

                                                     
                                                      

                                                          
                                 




                                                                      


                                                          







                                                                           


                          

                
                      
                               

                   

 
                                   
 
                          

                                            
                         




                               


                                                    
         
                                    

 
                                   
 
                          
                   
                       
                         

                                                  


                                                        



                               

                                                    
                         
         
                                   
 

                               
                   

 
                               
                                              

                                                    

                               
 

                                                           
                                 
                                 
                                                                      




                                                                           
                                                     
 
 












                                                 
                                               
 
                               
 

                                                           


                                    

 
                                    
 
                     
                     
 
                                                           




                                      

                                            
 


                                                    






                           

                                        

                                          


                               

                                          

                            



                                                 






                                                               
                





                                           
 



                                       





















                                                                    















                                     
 
    









                                                             




                             

                                  
 
 
                                                
 

                    
                          
                                                       

                                         
 
                    
                   
 
 

                                                       
                
                                   
 







                                                           

                                      
 
/*
 * Copyright (C) 2008,2009, Steven Rostedt <srostedt@redhat.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */
#include "util.h"
#include <dirent.h>
#include <mntent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <linux/list.h>
#include <linux/kernel.h>

#include "../perf.h"
#include "trace-event.h"
#include <lk/debugfs.h>
#include "evsel.h"

#define VERSION "0.5"

static const char *output_file = "trace.info";
static int output_fd;


static const char *find_debugfs(void)
{
	const char *path = perf_debugfs_mount(NULL);

	if (!path)
		pr_debug("Your kernel does not support the debugfs filesystem");

	return path;
}

/*
 * Finds the path to the debugfs/tracing
 * Allocates the string and stores it.
 */
static const char *find_tracing_dir(void)
{
	static char *tracing;
	static int tracing_found;
	const char *debugfs;

	if (tracing_found)
		return tracing;

	debugfs = find_debugfs();
	if (!debugfs)
		return NULL;

	tracing = malloc(strlen(debugfs) + 9);
	if (!tracing)
		return NULL;

	sprintf(tracing, "%s/tracing", debugfs);

	tracing_found = 1;
	return tracing;
}

static char *get_tracing_file(const char *name)
{
	const char *tracing;
	char *file;

	tracing = find_tracing_dir();
	if (!tracing)
		return NULL;

	file = malloc(strlen(tracing) + strlen(name) + 2);
	if (!file)
		return NULL;

	sprintf(file, "%s/%s", tracing, name);
	return file;
}

static void put_tracing_file(char *file)
{
	free(file);
}

int bigendian(void)
{
	unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0};
	unsigned int *ptr;

	ptr = (unsigned int *)(void *)str;
	return *ptr == 0x01020304;
}

/* unfortunately, you can not stat debugfs or proc files for size */
static int record_file(const char *file, ssize_t hdr_sz)
{
	unsigned long long size = 0;
	char buf[BUFSIZ], *sizep;
	off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR);
	int r, fd;
	int err = -EIO;

	fd = open(file, O_RDONLY);
	if (fd < 0)
		die("Can't read '%s'", file);

	/* put in zeros for file size, then fill true size later */
	if (hdr_sz) {
		if (write(output_fd, &size, hdr_sz) != hdr_sz)
			goto out;
	}

	do {
		r = read(fd, buf, BUFSIZ);
		if (r > 0) {
			size += r;
			if (write(output_fd, buf, r) != r)
				goto out;
		}
	} while (r > 0);

	/* ugh, handle big-endian hdr_size == 4 */
	sizep = (char*)&size;
	if (bigendian())
		sizep += sizeof(u64) - hdr_sz;

	if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0)
		die("writing to %s", output_file);

	err = 0;
out:
	close(fd);
	return err;
}

static int read_header_files(void)
{
	char *path;
	struct stat st;
	int err = -EIO;

	path = get_tracing_file("events/header_page");
	if (!path)
		die("can't get tracing/events/header_page");

	if (stat(path, &st) < 0)
		die("can't read '%s'", path);

	if (write(output_fd, "header_page", 12) != 12) {
		pr_debug("can't write header_page\n");
		goto out;
	}

	if (record_file(path, 8) < 0) {
		pr_debug("can't record header_page file\n");
		goto out;
	}

	put_tracing_file(path);

	path = get_tracing_file("events/header_event");
	if (!path)
		die("can't get tracing/events/header_event");

	if (stat(path, &st) < 0)
		die("can't read '%s'", path);

	if (write(output_fd, "header_event", 13) != 13) {
		pr_debug("can't write header_event\n");
		goto out;
	}

	if (record_file(path, 8) < 0) {
		pr_debug("can't record header_event file\n");
		goto out;
	}

	err = 0;
out:
	put_tracing_file(path);
	return err;
}

static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
{
	while (tps) {
		if (!strcmp(sys, tps->name))
			return true;
		tps = tps->next;
	}

	return false;
}

static int copy_event_system(const char *sys, struct tracepoint_path *tps)
{
	struct dirent *dent;
	struct stat st;
	char *format;
	DIR *dir;
	int count = 0;
	int ret;
	int err;

	dir = opendir(sys);
	if (!dir)
		die("can't read directory '%s'", sys);

	while ((dent = readdir(dir))) {
		if (dent->d_type != DT_DIR ||
		    strcmp(dent->d_name, ".") == 0 ||
		    strcmp(dent->d_name, "..") == 0 ||
		    !name_in_tp_list(dent->d_name, tps))
			continue;
		format = malloc(strlen(sys) + strlen(dent->d_name) + 10);
		if (!format) {
			err = -ENOMEM;
			goto out;
		}
		sprintf(format, "%s/%s/format", sys, dent->d_name);
		ret = stat(format, &st);
		free(format);
		if (ret < 0)
			continue;
		count++;
	}

	if (write(output_fd, &count, 4) != 4) {
		err = -EIO;
		pr_debug("can't write count\n");
		goto out;
	}

	rewinddir(dir);
	while ((dent = readdir(dir))) {
		if (dent->d_type != DT_DIR ||
		    strcmp(dent->d_name, ".") == 0 ||
		    strcmp(dent->d_name, "..") == 0 ||
		    !name_in_tp_list(dent->d_name, tps))
			continue;
		format = malloc(strlen(sys) + strlen(dent->d_name) + 10);
		if (!format) {
			err = -ENOMEM;
			goto out;
		}
		sprintf(format, "%s/%s/format", sys, dent->d_name);
		ret = stat(format, &st);

		if (ret >= 0) {
			err = record_file(format, 8);
			if (err) {
				free(format);
				goto out;
			}
		}
		free(format);
	}
	err = 0;
out:
	closedir(dir);
	return err;
}

static int read_ftrace_files(struct tracepoint_path *tps)
{
	char *path;
	int ret;

	path = get_tracing_file("events/ftrace");
	if (!path)
		die("can't get tracing/events/ftrace");

	ret = copy_event_system(path, tps);

	put_tracing_file(path);

	return ret;
}

static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
{
	while (tps) {
		if (!strcmp(sys, tps->system))
			return true;
		tps = tps->next;
	}

	return false;
}

static int read_event_files(struct tracepoint_path *tps)
{
	struct dirent *dent;
	struct stat st;
	char *path;
	char *sys;
	DIR *dir;
	int count = 0;
	int ret;
	int err;

	path = get_tracing_file("events");
	if (!path)
		die("can't get tracing/events");

	dir = opendir(path);
	if (!dir)
		die("can't read directory '%s'", path);

	while ((dent = readdir(dir))) {
		if (dent->d_type != DT_DIR ||
		    strcmp(dent->d_name, ".") == 0 ||
		    strcmp(dent->d_name, "..") == 0 ||
		    strcmp(dent->d_name, "ftrace") == 0 ||
		    !system_in_tp_list(dent->d_name, tps))
			continue;
		count++;
	}

	if (write(output_fd, &count, 4) != 4) {
		err = -EIO;
		pr_debug("can't write count\n");
		goto out;
	}

	rewinddir(dir);
	while ((dent = readdir(dir))) {
		if (dent->d_type != DT_DIR ||
		    strcmp(dent->d_name, ".") == 0 ||
		    strcmp(dent->d_name, "..") == 0 ||
		    strcmp(dent->d_name, "ftrace") == 0 ||
		    !system_in_tp_list(dent->d_name, tps))
			continue;
		sys = malloc(strlen(path) + strlen(dent->d_name) + 2);
		if (!sys) {
			err = -ENOMEM;
			goto out;
		}
		sprintf(sys, "%s/%s", path, dent->d_name);
		ret = stat(sys, &st);
		if (ret >= 0) {
			ssize_t size = strlen(dent->d_name) + 1;

			if (write(output_fd, dent->d_name, size) != size ||
			    copy_event_system(sys, tps) < 0) {
				err = -EIO;
				free(sys);
				goto out;
			}
		}
		free(sys);
	}
	err = 0;
out:
	closedir(dir);
	put_tracing_file(path);

	return err;
}

static int read_proc_kallsyms(void)
{
	unsigned int size;
	const char *path = "/proc/kallsyms";
	struct stat st;
	int ret, err = 0;

	ret = stat(path, &st);
	if (ret < 0) {
		/* not found */
		size = 0;
		if (write(output_fd, &size, 4) != 4)
			err = -EIO;
		return err;
	}
	return record_file(path, 4);
}

static int read_ftrace_printk(void)
{
	unsigned int size;
	char *path;
	struct stat st;
	int ret, err = 0;

	path = get_tracing_file("printk_formats");
	if (!path)
		die("can't get tracing/printk_formats");

	ret = stat(path, &st);
	if (ret < 0) {
		/* not found */
		size = 0;
		if (write(output_fd, &size, 4) != 4)
			err = -EIO;
		goto out;
	}
	err = record_file(path, 4);

out:
	put_tracing_file(path);
	return err;
}

static struct tracepoint_path *
get_tracepoints_path(struct list_head *pattrs)
{
	struct tracepoint_path path, *ppath = &path;
	struct perf_evsel *pos;
	int nr_tracepoints = 0;

	list_for_each_entry(pos, pattrs, node) {
		if (pos->attr.type != PERF_TYPE_TRACEPOINT)
			continue;
		++nr_tracepoints;
		ppath->next = tracepoint_id_to_path(pos->attr.config);
		if (!ppath->next)
			die("%s\n", "No memory to alloc tracepoints list");
		ppath = ppath->next;
	}

	return nr_tracepoints > 0 ? path.next : NULL;
}

static void
put_tracepoints_path(struct tracepoint_path *tps)
{
	while (tps) {
		struct tracepoint_path *t = tps;

		tps = tps->next;
		free(t->name);
		free(t->system);
		free(t);
	}
}

bool have_tracepoints(struct list_head *pattrs)
{
	struct perf_evsel *pos;

	list_for_each_entry(pos, pattrs, node)
		if (pos->attr.type == PERF_TYPE_TRACEPOINT)
			return true;

	return false;
}

static int tracing_data_header(void)
{
	char buf[20];
	ssize_t size;

	/* just guessing this is someone's birthday.. ;) */
	buf[0] = 23;
	buf[1] = 8;
	buf[2] = 68;
	memcpy(buf + 3, "tracing", 7);

	if (write(output_fd, buf, 10) != 10)
		return -1;

	size = strlen(VERSION) + 1;
	if (write(output_fd, VERSION, size) != size)
		return -1;

	/* save endian */
	if (bigendian())
		buf[0] = 1;
	else
		buf[0] = 0;

	read_trace_init(buf[0], buf[0]);

	if (write(output_fd, buf, 1) != 1)
		return -1;

	/* save size of long */
	buf[0] = sizeof(long);
	if (write(output_fd, buf, 1) != 1)
		return -1;

	/* save page_size */
	if (write(output_fd, &page_size, 4) != 4)
		return -1;

	return 0;
}

struct tracing_data *tracing_data_get(struct list_head *pattrs,
				      int fd, bool temp)
{
	struct tracepoint_path *tps;
	struct tracing_data *tdata;
	int err;

	output_fd = fd;

	tps = get_tracepoints_path(pattrs);
	if (!tps)
		return NULL;

	tdata = malloc(sizeof(*tdata));
	if (!tdata)
		return NULL;

	tdata->temp = temp;
	tdata->size = 0;

	if (temp) {
		int temp_fd;

		snprintf(tdata->temp_file, sizeof(tdata->temp_file),
			 "/tmp/perf-XXXXXX");
		if (!mkstemp(tdata->temp_file))
			die("Can't make temp file");

		temp_fd = open(tdata->temp_file, O_RDWR);
		if (temp_fd < 0)
			die("Can't read '%s'", tdata->temp_file);

		/*
		 * Set the temp file the default output, so all the
		 * tracing data are stored into it.
		 */
		output_fd = temp_fd;
	}

	err = tracing_data_header();
	if (err)
		goto out;
	err = read_header_files();
	if (err)
		goto out;
	err = read_ftrace_files(tps);
	if (err)
		goto out;
	err = read_event_files(tps);
	if (err)
		goto out;
	err = read_proc_kallsyms();
	if (err)
		goto out;
	err = read_ftrace_printk();

out:
	/*
	 * All tracing data are stored by now, we can restore
	 * the default output file in case we used temp file.
	 */
	if (temp) {
		tdata->size = lseek(output_fd, 0, SEEK_CUR);
		close(output_fd);
		output_fd = fd;
	}

	if (err) {
		free(tdata);
		tdata = NULL;
	}

	put_tracepoints_path(tps);
	return tdata;
}

int tracing_data_put(struct tracing_data *tdata)
{
	int err = 0;

	if (tdata->temp) {
		err = record_file(tdata->temp_file, 0);
		unlink(tdata->temp_file);
	}

	free(tdata);
	return err;
}

int read_tracing_data(int fd, struct list_head *pattrs)
{
	int err;
	struct tracing_data *tdata;

	/*
	 * We work over the real file, so we can write data
	 * directly, no temp file is needed.
	 */
	tdata = tracing_data_get(pattrs, fd, false);
	if (!tdata)
		return -ENOMEM;

	err = tracing_data_put(tdata);
	return err;
}