Commit 5beeded1 authored by Jason Baron's avatar Jason Baron Committed by Peter Zijlstra

perf_counter: Detect debugfs location

If "/sys/kernel/debug" is not a debugfs mount point, search for the debugfs
filesystem in /proc/mounts, but also allows the user to specify
'--debugfs-dir=blah' or set the environment variable: 'PERF_DEBUGFS_DIR'
Signed-off-by: default avatarJason Baron <jbaron@redhat.com>
[ also made it probe "/debug" by default ]
Signed-off-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <20090721181629.GA3094@redhat.com>
parent f6bdafef
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#include "util/cache.h" #include "util/cache.h"
#include "util/quote.h" #include "util/quote.h"
#include "util/run-command.h" #include "util/run-command.h"
#include "util/parse-events.h"
#include "util/string.h"
const char perf_usage_string[] = const char perf_usage_string[] =
"perf [--version] [--help] COMMAND [ARGS]"; "perf [--version] [--help] COMMAND [ARGS]";
...@@ -25,6 +27,8 @@ struct pager_config { ...@@ -25,6 +27,8 @@ struct pager_config {
int val; int val;
}; };
static char debugfs_mntpt[MAXPATHLEN];
static int pager_command_config(const char *var, const char *value, void *data) static int pager_command_config(const char *var, const char *value, void *data)
{ {
struct pager_config *c = data; struct pager_config *c = data;
...@@ -56,6 +60,15 @@ static void commit_pager_choice(void) { ...@@ -56,6 +60,15 @@ static void commit_pager_choice(void) {
} }
} }
static void set_debugfs_path(void)
{
char *path;
path = getenv(PERF_DEBUGFS_ENVIRONMENT);
snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt,
"tracing/events");
}
static int handle_options(const char*** argv, int* argc, int* envchanged) static int handle_options(const char*** argv, int* argc, int* envchanged)
{ {
int handled = 0; int handled = 0;
...@@ -122,6 +135,22 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) ...@@ -122,6 +135,22 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1); setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
if (envchanged) if (envchanged)
*envchanged = 1; *envchanged = 1;
} else if (!strcmp(cmd, "--debugfs-dir")) {
if (*argc < 2) {
fprintf(stderr, "No directory given for --debugfs-dir.\n");
usage(perf_usage_string);
}
strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN);
debugfs_mntpt[MAXPATHLEN - 1] = '\0';
if (envchanged)
*envchanged = 1;
(*argv)++;
(*argc)--;
} else if (!prefixcmp(cmd, "--debugfs-dir=")) {
strncpy(debugfs_mntpt, cmd + 14, MAXPATHLEN);
debugfs_mntpt[MAXPATHLEN - 1] = '\0';
if (envchanged)
*envchanged = 1;
} else { } else {
fprintf(stderr, "Unknown option: %s\n", cmd); fprintf(stderr, "Unknown option: %s\n", cmd);
usage(perf_usage_string); usage(perf_usage_string);
...@@ -228,6 +257,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) ...@@ -228,6 +257,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
if (use_pager == -1 && p->option & USE_PAGER) if (use_pager == -1 && p->option & USE_PAGER)
use_pager = 1; use_pager = 1;
commit_pager_choice(); commit_pager_choice();
set_debugfs_path();
status = p->fn(argc, argv, prefix); status = p->fn(argc, argv, prefix);
if (status) if (status)
...@@ -346,6 +376,49 @@ static int run_argv(int *argcp, const char ***argv) ...@@ -346,6 +376,49 @@ static int run_argv(int *argcp, const char ***argv)
return done_alias; return done_alias;
} }
/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */
static void get_debugfs_mntpt(void)
{
FILE *file;
char fs_type[100];
char debugfs[MAXPATHLEN];
/*
* try the standard location
*/
if (valid_debugfs_mount("/sys/kernel/debug/") == 0) {
strcpy(debugfs_mntpt, "/sys/kernel/debug/");
return;
}
/*
* try the sane location
*/
if (valid_debugfs_mount("/debug/") == 0) {
strcpy(debugfs_mntpt, "/debug/");
return;
}
/*
* give up and parse /proc/mounts
*/
file = fopen("/proc/mounts", "r");
if (file == NULL)
return;
while (fscanf(file, "%*s %"
STR(MAXPATHLEN)
"s %99s %*s %*d %*d\n",
debugfs, fs_type) == 2) {
if (strcmp(fs_type, "debugfs") == 0)
break;
}
fclose(file);
if (strcmp(fs_type, "debugfs") == 0) {
strncpy(debugfs_mntpt, debugfs, MAXPATHLEN);
debugfs_mntpt[MAXPATHLEN - 1] = '\0';
}
}
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
...@@ -354,7 +427,8 @@ int main(int argc, const char **argv) ...@@ -354,7 +427,8 @@ int main(int argc, const char **argv)
cmd = perf_extract_argv0_path(argv[0]); cmd = perf_extract_argv0_path(argv[0]);
if (!cmd) if (!cmd)
cmd = "perf-help"; cmd = "perf-help";
/* get debugfs mount point from /proc/mounts */
get_debugfs_mntpt();
/* /*
* "perf-xxxx" is the same as "perf xxxx", but we obviously: * "perf-xxxx" is the same as "perf xxxx", but we obviously:
* *
...@@ -377,6 +451,7 @@ int main(int argc, const char **argv) ...@@ -377,6 +451,7 @@ int main(int argc, const char **argv)
argc--; argc--;
handle_options(&argv, &argc, NULL); handle_options(&argv, &argc, NULL);
commit_pager_choice(); commit_pager_choice();
set_debugfs_path();
if (argc > 0) { if (argc > 0) {
if (!prefixcmp(argv[0], "--")) if (!prefixcmp(argv[0], "--"))
argv[0] += 2; argv[0] += 2;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#define PERFATTRIBUTES_FILE ".perfattributes" #define PERFATTRIBUTES_FILE ".perfattributes"
#define INFOATTRIBUTES_FILE "info/attributes" #define INFOATTRIBUTES_FILE "info/attributes"
#define ATTRIBUTE_MACRO_PREFIX "[attr]" #define ATTRIBUTE_MACRO_PREFIX "[attr]"
#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
typedef int (*config_fn_t)(const char *, const char *, void *); typedef int (*config_fn_t)(const char *, const char *, void *);
extern int perf_default_config(const char *, const char *, void *); extern int perf_default_config(const char *, const char *, void *);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "parse-events.h" #include "parse-events.h"
#include "exec_cmd.h" #include "exec_cmd.h"
#include "string.h" #include "string.h"
#include "cache.h"
extern char *strcasestr(const char *haystack, const char *needle); extern char *strcasestr(const char *haystack, const char *needle);
...@@ -12,8 +13,6 @@ int nr_counters; ...@@ -12,8 +13,6 @@ int nr_counters;
struct perf_counter_attr attrs[MAX_COUNTERS]; struct perf_counter_attr attrs[MAX_COUNTERS];
static char default_debugfs_path[] = "/sys/kernel/debug/tracing/events";
struct event_symbol { struct event_symbol {
u8 type; u8 type;
u64 config; u64 config;
...@@ -21,6 +20,8 @@ struct event_symbol { ...@@ -21,6 +20,8 @@ struct event_symbol {
char *alias; char *alias;
}; };
char debugfs_path[MAXPATHLEN];
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
...@@ -114,27 +115,27 @@ static unsigned long hw_cache_stat[C(MAX)] = { ...@@ -114,27 +115,27 @@ static unsigned long hw_cache_stat[C(MAX)] = {
#define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st) \ #define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st) \
while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
if (snprintf(file, MAXPATHLEN, "%s/%s", default_debugfs_path, \ if (snprintf(file, MAXPATHLEN, "%s/%s", debugfs_path, \
sys_dirent.d_name) && \ sys_dirent.d_name) && \
(!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \
(strcmp(sys_dirent.d_name, ".")) && \ (strcmp(sys_dirent.d_name, ".")) && \
(strcmp(sys_dirent.d_name, ".."))) (strcmp(sys_dirent.d_name, "..")))
#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \ #define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \
while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \
if (snprintf(file, MAXPATHLEN, "%s/%s/%s", default_debugfs_path, \ if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path, \
sys_dirent.d_name, evt_dirent.d_name) && \ sys_dirent.d_name, evt_dirent.d_name) && \
(!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \
(strcmp(evt_dirent.d_name, ".")) && \ (strcmp(evt_dirent.d_name, ".")) && \
(strcmp(evt_dirent.d_name, ".."))) (strcmp(evt_dirent.d_name, "..")))
#define MAX_EVENT_LENGTH 30 #define MAX_EVENT_LENGTH 30
static int valid_debugfs_mount(void) int valid_debugfs_mount(const char *debugfs)
{ {
struct statfs st_fs; struct statfs st_fs;
if (statfs(default_debugfs_path, &st_fs) < 0) if (statfs(debugfs, &st_fs) < 0)
return -ENOENT; return -ENOENT;
else if (st_fs.f_type != (long) DEBUGFS_MAGIC) else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
return -ENOENT; return -ENOENT;
...@@ -152,10 +153,10 @@ static char *tracepoint_id_to_name(u64 config) ...@@ -152,10 +153,10 @@ static char *tracepoint_id_to_name(u64 config)
u64 id; u64 id;
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
if (valid_debugfs_mount()) if (valid_debugfs_mount(debugfs_path))
return "unkown"; return "unkown";
sys_dir = opendir(default_debugfs_path); sys_dir = opendir(debugfs_path);
if (!sys_dir) if (!sys_dir)
goto cleanup; goto cleanup;
...@@ -166,7 +167,7 @@ static char *tracepoint_id_to_name(u64 config) ...@@ -166,7 +167,7 @@ static char *tracepoint_id_to_name(u64 config)
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next,
evt_path, st) { evt_path, st) {
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id",
default_debugfs_path, sys_dirent.d_name, debugfs_path, sys_dirent.d_name,
evt_dirent.d_name); evt_dirent.d_name);
fd = open(evt_path, O_RDONLY); fd = open(evt_path, O_RDONLY);
if (fd < 0) if (fd < 0)
...@@ -363,7 +364,7 @@ static int parse_tracepoint_event(const char **strp, ...@@ -363,7 +364,7 @@ static int parse_tracepoint_event(const char **strp,
u64 id; u64 id;
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
if (valid_debugfs_mount()) if (valid_debugfs_mount(debugfs_path))
return 0; return 0;
evt_name = strchr(*strp, ':'); evt_name = strchr(*strp, ':');
...@@ -381,8 +382,8 @@ static int parse_tracepoint_event(const char **strp, ...@@ -381,8 +382,8 @@ static int parse_tracepoint_event(const char **strp,
if (evt_length >= MAX_EVENT_LENGTH) if (evt_length >= MAX_EVENT_LENGTH)
return 0; return 0;
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", default_debugfs_path, snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
sys_name, evt_name); sys_name, evt_name);
fd = open(evt_path, O_RDONLY); fd = open(evt_path, O_RDONLY);
if (fd < 0) if (fd < 0)
return 0; return 0;
...@@ -568,10 +569,10 @@ static void print_tracepoint_events(void) ...@@ -568,10 +569,10 @@ static void print_tracepoint_events(void)
struct stat st; struct stat st;
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
if (valid_debugfs_mount()) if (valid_debugfs_mount(debugfs_path))
return; return;
sys_dir = opendir(default_debugfs_path); sys_dir = opendir(debugfs_path);
if (!sys_dir) if (!sys_dir)
goto cleanup; goto cleanup;
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
* Parse symbolic events/counts passed in as options: * Parse symbolic events/counts passed in as options:
*/ */
struct option;
extern int nr_counters; extern int nr_counters;
extern struct perf_counter_attr attrs[MAX_COUNTERS]; extern struct perf_counter_attr attrs[MAX_COUNTERS];
...@@ -15,3 +17,6 @@ extern int parse_events(const struct option *opt, const char *str, int unset); ...@@ -15,3 +17,6 @@ extern int parse_events(const struct option *opt, const char *str, int unset);
extern void print_events(void); extern void print_events(void);
extern char debugfs_path[];
extern int valid_debugfs_mount(const char *debugfs);
...@@ -5,4 +5,7 @@ ...@@ -5,4 +5,7 @@
int hex2u64(const char *ptr, u64 *val); int hex2u64(const char *ptr, u64 *val);
#define _STR(x) #x
#define STR(x) _STR(x)
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment