/*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <ctype.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "trace-cmd.h"
#define LOCAL_PLUGIN_DIR ".trace-cmd/plugins"
#define DEBUGFS_PATH "/sys/kernel/debug"
int tracecmd_disable_sys_plugins;
int tracecmd_disable_plugins;
#define _STR(x) #x
#define STR(x) _STR(x)
#ifndef MAX_PATH
# define MAX_PATH 1024
#endif
struct plugin_list {
struct plugin_list *next;
char *name;
void *handle;
};
void parse_cmdlines(struct pevent *pevent,
char *file, int size __unused)
{
char *comm;
char *line;
char *next = NULL;
int pid;
line = strtok_r(file, "\n", &next);
while (line) {
sscanf(line, "%d %as", &pid,
(float *)(void *)&comm); /* workaround gcc warning */
pevent_register_comm(pevent, comm, pid);
free(comm);
line = strtok_r(NULL, "\n", &next);
}
}
void parse_proc_kallsyms(struct pevent *pevent,
char *file, unsigned int size __unused)
{
unsigned long long addr;
char *func;
char *line;
char *next = NULL;
char *addr_str;
char *mod;
char ch;
int ret;
line = strtok_r(file, "\n", &next);
while (line) {
mod = NULL;
ret = sscanf(line, "%as %c %as\t[%as",
(float *)(void *)&addr_str, /* workaround gcc warning */
&ch,
(float *)(void *)&func,
(float *)(void *)&mod);
addr = strtoull(addr_str, NULL, 16);
free(addr_str);
/* truncate the extra ']' */
if (mod)
mod[strlen(mod) - 1] = 0;
pevent_register_function(pevent, func, addr, mod);
free(func);
free(mod);
line = strtok_r(NULL, "\n", &next);
}
}
void parse_ftrace_printk(char *file, unsigned int size __unused)
{
unsigned long long addr;
char *printk;
char *line;
char *next = NULL;
char *addr_str;
char *fmt;
line = strtok_r(file, "\n", &next);
while (line) {
addr_str = strtok_r(line, ":", &fmt);
if (!addr_str) {
warning("printk format with empty entry");
break;
}
addr = strtoull(addr_str, NULL, 16);
/* fmt still has a space, skip it */
printk = strdup(fmt+1);
line = strtok_r(NULL, "\n", &next);
}
}
static struct plugin_list *
load_plugin(struct pevent *pevent, struct plugin_list *plugin_list,
const char *path, const char *file)
{
pevent_plugin_load_func func;
struct plugin_list *list;
char *plugin;
void *handle;
int ret = -1;
plugin = malloc_or_die(strlen(path) + strlen(file) + 2);
strcpy(plugin, path);
strcat(plugin, "/");
strcat(plugin, file);
handle = dlopen(plugin, RTLD_NOW);
if (!handle) {
warning("cound not load plugin '%s'\n%s\n",
plugin, dlerror());
goto out_free;
}
func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME);
if (!func) {
warning("cound not find func '%s' in plugin '%s'\n%s\n",
PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror());
goto out_free;
}
list = malloc_or_die(sizeof(*list));
list->next = plugin_list;
list->handle = handle;
list->name = plugin;
plugin_list = list;
pr_stat("registering plugin: %s", plugin);
ret = func(pevent);
return plugin_list;
out_free:
free(plugin);
return plugin_list;
}
static int mount_debugfs(void)
{
struct stat st;
int ret;
/* make sure debugfs exists */
ret = stat(DEBUGFS_PATH, &st);
if (ret < 0)
die("debugfs is not configured on this kernel");
ret = mount("nodev", DEBUGFS_PATH,
"debugfs", 0, NULL);
return ret;
}
char *tracecmd_find_tracing_dir(void)
{
char debugfs[MAX_PATH+1];
char *tracing_dir;
char type[100];
FILE *fp;
if ((fp = fopen("/proc/mounts","r")) == NULL) {
warning("Can't open /proc/mounts for read");
return NULL;
}
while (fscanf(fp, "%*s %"
STR(MAX_PATH)
"s %99s %*s %*d %*d\n",
debugfs, type) == 2) {
if (strcmp(type, "debugfs") == 0)
break;
}
fclose(fp);
if (strcmp(type, "debugfs") != 0) {
/* If debugfs is not mounted, try to mount it */
if (mount_debugfs() < 0) {
warning("debugfs not mounted, please mount");
return NULL;
}
strcpy(debugfs, DEBUGFS_PATH);
}
tracing_dir = malloc_or_die(strlen(debugfs) + 9);
if (!tracing_dir)
return NULL;
sprintf(tracing_dir, "%s/tracing", debugfs);
return tracing_dir;
}
static int load_plugins(struct pevent *pevent, struct plugin_list **list,
char *path)
{
struct dirent *dent;
struct stat st;
DIR *dir;
int ret;
ret = stat(path, &st);
if (ret < 0)
return -1;
if (!S_ISDIR(st.st_mode))
return -1;
dir = opendir(path);
if (!dir)
return -1;
while ((dent = readdir(dir))) {
const char *name = dent->d_name;
if (strcmp(name, ".") == 0 ||
strcmp(name, "..") == 0)
continue;
*list = load_plugin(pevent, *list, path, name);
}
closedir(dir);
return 0;
}
struct plugin_list *tracecmd_load_plugins(struct pevent *pevent)
{
struct plugin_list *list = NULL;
char *home;
char *path;
if (tracecmd_disable_plugins)
return NULL;
/* If a system plugin directory was defined, check that first */
#ifdef PLUGIN_DIR
if (!tracecmd_disable_sys_plugins)
load_plugins(pevent, &list, MAKE_STR(PLUGIN_DIR));
#endif
/* Now let the home directory override the system defaults */
home = getenv("HOME");
if (!home)
return list;
path = malloc_or_die(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2);
strcpy(path, home);
strcat(path, "/");
strcat(path, LOCAL_PLUGIN_DIR);
load_plugins(pevent, &list, path);
free(path);
return list;
}
void tracecmd_unload_plugins(struct plugin_list *plugin_list)
{
pevent_plugin_unload_func func;
struct plugin_list *list;
while (plugin_list) {
list = plugin_list;
plugin_list = list->next;
func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME);
if (func)
func();
dlclose(list->handle);
free(list->name);
free(list);
}
}