diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2009-09-14 00:16:56 -0400 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2009-09-14 00:16:56 -0400 |
| commit | fc8e1ead9314cf0e0f1922e661428b93d3a50d88 (patch) | |
| tree | f3cb97c4769b74f6627a59769f1ed5c92a13c58a /tools/perf/util | |
| parent | 2bcaa6a4238094c5695d5b1943078388d82d3004 (diff) | |
| parent | 9de48cc300fb10f7d9faa978670becf5e352462a (diff) | |
Merge branch 'next' into for-linus
Diffstat (limited to 'tools/perf/util')
53 files changed, 8912 insertions, 0 deletions
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN new file mode 100755 index 000000000000..c561d1538c03 --- /dev/null +++ b/tools/perf/util/PERF-VERSION-GEN | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | GVF=PERF-VERSION-FILE | ||
| 4 | DEF_VER=v0.0.1.PERF | ||
| 5 | |||
| 6 | LF=' | ||
| 7 | ' | ||
| 8 | |||
| 9 | # First see if there is a version file (included in release tarballs), | ||
| 10 | # then try git-describe, then default. | ||
| 11 | if test -f version | ||
| 12 | then | ||
| 13 | VN=$(cat version) || VN="$DEF_VER" | ||
| 14 | elif test -d .git -o -f .git && | ||
| 15 | VN=$(git describe --abbrev=4 HEAD 2>/dev/null) && | ||
| 16 | case "$VN" in | ||
| 17 | *$LF*) (exit 1) ;; | ||
| 18 | v[0-9]*) | ||
| 19 | git update-index -q --refresh | ||
| 20 | test -z "$(git diff-index --name-only HEAD --)" || | ||
| 21 | VN="$VN-dirty" ;; | ||
| 22 | esac | ||
| 23 | then | ||
| 24 | VN=$(echo "$VN" | sed -e 's/-/./g'); | ||
| 25 | else | ||
| 26 | VN="$DEF_VER" | ||
| 27 | fi | ||
| 28 | |||
| 29 | VN=$(expr "$VN" : v*'\(.*\)') | ||
| 30 | |||
| 31 | if test -r $GVF | ||
| 32 | then | ||
| 33 | VC=$(sed -e 's/^PERF_VERSION = //' <$GVF) | ||
| 34 | else | ||
| 35 | VC=unset | ||
| 36 | fi | ||
| 37 | test "$VN" = "$VC" || { | ||
| 38 | echo >&2 "PERF_VERSION = $VN" | ||
| 39 | echo "PERF_VERSION = $VN" >$GVF | ||
| 40 | } | ||
| 41 | |||
| 42 | |||
diff --git a/tools/perf/util/abspath.c b/tools/perf/util/abspath.c new file mode 100644 index 000000000000..61d33b81fc97 --- /dev/null +++ b/tools/perf/util/abspath.c | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | #include "cache.h" | ||
| 2 | |||
| 3 | /* | ||
| 4 | * Do not use this for inspecting *tracked* content. When path is a | ||
| 5 | * symlink to a directory, we do not want to say it is a directory when | ||
| 6 | * dealing with tracked content in the working tree. | ||
| 7 | */ | ||
| 8 | static int is_directory(const char *path) | ||
| 9 | { | ||
| 10 | struct stat st; | ||
| 11 | return (!stat(path, &st) && S_ISDIR(st.st_mode)); | ||
| 12 | } | ||
| 13 | |||
| 14 | /* We allow "recursive" symbolic links. Only within reason, though. */ | ||
| 15 | #define MAXDEPTH 5 | ||
| 16 | |||
| 17 | const char *make_absolute_path(const char *path) | ||
| 18 | { | ||
| 19 | static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; | ||
| 20 | char cwd[1024] = ""; | ||
| 21 | int buf_index = 1, len; | ||
| 22 | |||
| 23 | int depth = MAXDEPTH; | ||
| 24 | char *last_elem = NULL; | ||
| 25 | struct stat st; | ||
| 26 | |||
| 27 | if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) | ||
| 28 | die ("Too long path: %.*s", 60, path); | ||
| 29 | |||
| 30 | while (depth--) { | ||
| 31 | if (!is_directory(buf)) { | ||
| 32 | char *last_slash = strrchr(buf, '/'); | ||
| 33 | if (last_slash) { | ||
| 34 | *last_slash = '\0'; | ||
| 35 | last_elem = xstrdup(last_slash + 1); | ||
| 36 | } else { | ||
| 37 | last_elem = xstrdup(buf); | ||
| 38 | *buf = '\0'; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | if (*buf) { | ||
| 43 | if (!*cwd && !getcwd(cwd, sizeof(cwd))) | ||
| 44 | die ("Could not get current working directory"); | ||
| 45 | |||
| 46 | if (chdir(buf)) | ||
| 47 | die ("Could not switch to '%s'", buf); | ||
| 48 | } | ||
| 49 | if (!getcwd(buf, PATH_MAX)) | ||
| 50 | die ("Could not get current working directory"); | ||
| 51 | |||
| 52 | if (last_elem) { | ||
| 53 | int len = strlen(buf); | ||
| 54 | if (len + strlen(last_elem) + 2 > PATH_MAX) | ||
| 55 | die ("Too long path name: '%s/%s'", | ||
| 56 | buf, last_elem); | ||
| 57 | buf[len] = '/'; | ||
| 58 | strcpy(buf + len + 1, last_elem); | ||
| 59 | free(last_elem); | ||
| 60 | last_elem = NULL; | ||
| 61 | } | ||
| 62 | |||
| 63 | if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) { | ||
| 64 | len = readlink(buf, next_buf, PATH_MAX); | ||
| 65 | if (len < 0) | ||
| 66 | die ("Invalid symlink: %s", buf); | ||
| 67 | if (PATH_MAX <= len) | ||
| 68 | die("symbolic link too long: %s", buf); | ||
| 69 | next_buf[len] = '\0'; | ||
| 70 | buf = next_buf; | ||
| 71 | buf_index = 1 - buf_index; | ||
| 72 | next_buf = bufs[buf_index]; | ||
| 73 | } else | ||
| 74 | break; | ||
| 75 | } | ||
| 76 | |||
| 77 | if (*cwd && chdir(cwd)) | ||
| 78 | die ("Could not change back to '%s'", cwd); | ||
| 79 | |||
| 80 | return buf; | ||
| 81 | } | ||
| 82 | |||
| 83 | static const char *get_pwd_cwd(void) | ||
| 84 | { | ||
| 85 | static char cwd[PATH_MAX + 1]; | ||
| 86 | char *pwd; | ||
| 87 | struct stat cwd_stat, pwd_stat; | ||
| 88 | if (getcwd(cwd, PATH_MAX) == NULL) | ||
| 89 | return NULL; | ||
| 90 | pwd = getenv("PWD"); | ||
| 91 | if (pwd && strcmp(pwd, cwd)) { | ||
| 92 | stat(cwd, &cwd_stat); | ||
| 93 | if (!stat(pwd, &pwd_stat) && | ||
| 94 | pwd_stat.st_dev == cwd_stat.st_dev && | ||
| 95 | pwd_stat.st_ino == cwd_stat.st_ino) { | ||
| 96 | strlcpy(cwd, pwd, PATH_MAX); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | return cwd; | ||
| 100 | } | ||
| 101 | |||
| 102 | const char *make_nonrelative_path(const char *path) | ||
| 103 | { | ||
| 104 | static char buf[PATH_MAX + 1]; | ||
| 105 | |||
| 106 | if (is_absolute_path(path)) { | ||
| 107 | if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) | ||
| 108 | die("Too long path: %.*s", 60, path); | ||
| 109 | } else { | ||
| 110 | const char *cwd = get_pwd_cwd(); | ||
| 111 | if (!cwd) | ||
| 112 | die("Cannot determine the current working directory"); | ||
| 113 | if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) | ||
| 114 | die("Too long path: %.*s", 60, path); | ||
| 115 | } | ||
| 116 | return buf; | ||
| 117 | } | ||
diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c new file mode 100644 index 000000000000..b8144e80bb1e --- /dev/null +++ b/tools/perf/util/alias.c | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | #include "cache.h" | ||
| 2 | |||
| 3 | static const char *alias_key; | ||
| 4 | static char *alias_val; | ||
| 5 | |||
| 6 | static int alias_lookup_cb(const char *k, const char *v, void *cb __used) | ||
| 7 | { | ||
| 8 | if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) { | ||
| 9 | if (!v) | ||
| 10 | return config_error_nonbool(k); | ||
| 11 | alias_val = strdup(v); | ||
| 12 | return 0; | ||
| 13 | } | ||
| 14 | return 0; | ||
| 15 | } | ||
| 16 | |||
| 17 | char *alias_lookup(const char *alias) | ||
| 18 | { | ||
| 19 | alias_key = alias; | ||
| 20 | alias_val = NULL; | ||
| 21 | perf_config(alias_lookup_cb, NULL); | ||
| 22 | return alias_val; | ||
| 23 | } | ||
| 24 | |||
| 25 | int split_cmdline(char *cmdline, const char ***argv) | ||
| 26 | { | ||
| 27 | int src, dst, count = 0, size = 16; | ||
| 28 | char quoted = 0; | ||
| 29 | |||
| 30 | *argv = malloc(sizeof(char*) * size); | ||
| 31 | |||
| 32 | /* split alias_string */ | ||
| 33 | (*argv)[count++] = cmdline; | ||
| 34 | for (src = dst = 0; cmdline[src];) { | ||
| 35 | char c = cmdline[src]; | ||
| 36 | if (!quoted && isspace(c)) { | ||
| 37 | cmdline[dst++] = 0; | ||
| 38 | while (cmdline[++src] | ||
| 39 | && isspace(cmdline[src])) | ||
| 40 | ; /* skip */ | ||
| 41 | if (count >= size) { | ||
| 42 | size += 16; | ||
| 43 | *argv = realloc(*argv, sizeof(char*) * size); | ||
| 44 | } | ||
| 45 | (*argv)[count++] = cmdline + dst; | ||
| 46 | } else if (!quoted && (c == '\'' || c == '"')) { | ||
| 47 | quoted = c; | ||
| 48 | src++; | ||
| 49 | } else if (c == quoted) { | ||
| 50 | quoted = 0; | ||
| 51 | src++; | ||
| 52 | } else { | ||
| 53 | if (c == '\\' && quoted != '\'') { | ||
| 54 | src++; | ||
| 55 | c = cmdline[src]; | ||
| 56 | if (!c) { | ||
| 57 | free(*argv); | ||
| 58 | *argv = NULL; | ||
| 59 | return error("cmdline ends with \\"); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | cmdline[dst++] = c; | ||
| 63 | src++; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | cmdline[dst] = 0; | ||
| 68 | |||
| 69 | if (quoted) { | ||
| 70 | free(*argv); | ||
| 71 | *argv = NULL; | ||
| 72 | return error("unclosed quote"); | ||
| 73 | } | ||
| 74 | |||
| 75 | return count; | ||
| 76 | } | ||
| 77 | |||
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h new file mode 100644 index 000000000000..4b50c412b9c5 --- /dev/null +++ b/tools/perf/util/cache.h | |||
| @@ -0,0 +1,121 @@ | |||
| 1 | #ifndef CACHE_H | ||
| 2 | #define CACHE_H | ||
| 3 | |||
| 4 | #include "util.h" | ||
| 5 | #include "strbuf.h" | ||
| 6 | #include "../perf.h" | ||
| 7 | |||
| 8 | #define PERF_DIR_ENVIRONMENT "PERF_DIR" | ||
| 9 | #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" | ||
| 10 | #define DEFAULT_PERF_DIR_ENVIRONMENT ".perf" | ||
| 11 | #define DB_ENVIRONMENT "PERF_OBJECT_DIRECTORY" | ||
| 12 | #define INDEX_ENVIRONMENT "PERF_INDEX_FILE" | ||
| 13 | #define GRAFT_ENVIRONMENT "PERF_GRAFT_FILE" | ||
| 14 | #define TEMPLATE_DIR_ENVIRONMENT "PERF_TEMPLATE_DIR" | ||
| 15 | #define CONFIG_ENVIRONMENT "PERF_CONFIG" | ||
| 16 | #define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH" | ||
| 17 | #define CEILING_DIRECTORIES_ENVIRONMENT "PERF_CEILING_DIRECTORIES" | ||
| 18 | #define PERFATTRIBUTES_FILE ".perfattributes" | ||
| 19 | #define INFOATTRIBUTES_FILE "info/attributes" | ||
| 20 | #define ATTRIBUTE_MACRO_PREFIX "[attr]" | ||
| 21 | #define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" | ||
| 22 | |||
| 23 | typedef int (*config_fn_t)(const char *, const char *, void *); | ||
| 24 | extern int perf_default_config(const char *, const char *, void *); | ||
| 25 | extern int perf_config_from_file(config_fn_t fn, const char *, void *); | ||
| 26 | extern int perf_config(config_fn_t fn, void *); | ||
| 27 | extern int perf_parse_ulong(const char *, unsigned long *); | ||
| 28 | extern int perf_config_int(const char *, const char *); | ||
| 29 | extern unsigned long perf_config_ulong(const char *, const char *); | ||
| 30 | extern int perf_config_bool_or_int(const char *, const char *, int *); | ||
| 31 | extern int perf_config_bool(const char *, const char *); | ||
| 32 | extern int perf_config_string(const char **, const char *, const char *); | ||
| 33 | extern int perf_config_set(const char *, const char *); | ||
| 34 | extern int perf_config_set_multivar(const char *, const char *, const char *, int); | ||
| 35 | extern int perf_config_rename_section(const char *, const char *); | ||
| 36 | extern const char *perf_etc_perfconfig(void); | ||
| 37 | extern int check_repository_format_version(const char *var, const char *value, void *cb); | ||
| 38 | extern int perf_config_system(void); | ||
| 39 | extern int perf_config_global(void); | ||
| 40 | extern int config_error_nonbool(const char *); | ||
| 41 | extern const char *config_exclusive_filename; | ||
| 42 | |||
| 43 | #define MAX_PERFNAME (1000) | ||
| 44 | extern char perf_default_email[MAX_PERFNAME]; | ||
| 45 | extern char perf_default_name[MAX_PERFNAME]; | ||
| 46 | extern int user_ident_explicitly_given; | ||
| 47 | |||
| 48 | extern const char *perf_log_output_encoding; | ||
| 49 | extern const char *perf_mailmap_file; | ||
| 50 | |||
| 51 | /* IO helper functions */ | ||
| 52 | extern void maybe_flush_or_die(FILE *, const char *); | ||
| 53 | extern int copy_fd(int ifd, int ofd); | ||
| 54 | extern int copy_file(const char *dst, const char *src, int mode); | ||
| 55 | extern ssize_t read_in_full(int fd, void *buf, size_t count); | ||
| 56 | extern ssize_t write_in_full(int fd, const void *buf, size_t count); | ||
| 57 | extern void write_or_die(int fd, const void *buf, size_t count); | ||
| 58 | extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg); | ||
| 59 | extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg); | ||
| 60 | extern void fsync_or_die(int fd, const char *); | ||
| 61 | |||
| 62 | /* pager.c */ | ||
| 63 | extern void setup_pager(void); | ||
| 64 | extern const char *pager_program; | ||
| 65 | extern int pager_in_use(void); | ||
| 66 | extern int pager_use_color; | ||
| 67 | |||
| 68 | extern const char *editor_program; | ||
| 69 | extern const char *excludes_file; | ||
| 70 | |||
| 71 | char *alias_lookup(const char *alias); | ||
| 72 | int split_cmdline(char *cmdline, const char ***argv); | ||
| 73 | |||
| 74 | #define alloc_nr(x) (((x)+16)*3/2) | ||
| 75 | |||
| 76 | /* | ||
| 77 | * Realloc the buffer pointed at by variable 'x' so that it can hold | ||
| 78 | * at least 'nr' entries; the number of entries currently allocated | ||
| 79 | * is 'alloc', using the standard growing factor alloc_nr() macro. | ||
| 80 | * | ||
| 81 | * DO NOT USE any expression with side-effect for 'x' or 'alloc'. | ||
| 82 | */ | ||
| 83 | #define ALLOC_GROW(x, nr, alloc) \ | ||
| 84 | do { \ | ||
| 85 | if ((nr) > alloc) { \ | ||
| 86 | if (alloc_nr(alloc) < (nr)) \ | ||
| 87 | alloc = (nr); \ | ||
| 88 | else \ | ||
| 89 | alloc = alloc_nr(alloc); \ | ||
| 90 | x = xrealloc((x), alloc * sizeof(*(x))); \ | ||
| 91 | } \ | ||
| 92 | } while(0) | ||
| 93 | |||
| 94 | |||
| 95 | static inline int is_absolute_path(const char *path) | ||
| 96 | { | ||
| 97 | return path[0] == '/'; | ||
| 98 | } | ||
| 99 | |||
| 100 | const char *make_absolute_path(const char *path); | ||
| 101 | const char *make_nonrelative_path(const char *path); | ||
| 102 | const char *make_relative_path(const char *abs, const char *base); | ||
| 103 | int normalize_path_copy(char *dst, const char *src); | ||
| 104 | int longest_ancestor_length(const char *path, const char *prefix_list); | ||
| 105 | char *strip_path_suffix(const char *path, const char *suffix); | ||
| 106 | |||
| 107 | extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); | ||
| 108 | extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); | ||
| 109 | /* perf_mkstemp() - create tmp file honoring TMPDIR variable */ | ||
| 110 | extern int perf_mkstemp(char *path, size_t len, const char *template); | ||
| 111 | |||
| 112 | extern char *mksnpath(char *buf, size_t n, const char *fmt, ...) | ||
| 113 | __attribute__((format (printf, 3, 4))); | ||
| 114 | extern char *perf_snpath(char *buf, size_t n, const char *fmt, ...) | ||
| 115 | __attribute__((format (printf, 3, 4))); | ||
| 116 | extern char *perf_pathdup(const char *fmt, ...) | ||
| 117 | __attribute__((format (printf, 1, 2))); | ||
| 118 | |||
| 119 | extern size_t strlcpy(char *dest, const char *src, size_t size); | ||
| 120 | |||
| 121 | #endif /* CACHE_H */ | ||
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c new file mode 100644 index 000000000000..011473411642 --- /dev/null +++ b/tools/perf/util/callchain.c | |||
| @@ -0,0 +1,343 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2009, Frederic Weisbecker <fweisbec@gmail.com> | ||
| 3 | * | ||
| 4 | * Handle the callchains from the stream in an ad-hoc radix tree and then | ||
| 5 | * sort them in an rbtree. | ||
| 6 | * | ||
| 7 | * Using a radix for code path provides a fast retrieval and factorizes | ||
| 8 | * memory use. Also that lets us use the paths in a hierarchical graph view. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <stdlib.h> | ||
| 13 | #include <stdio.h> | ||
| 14 | #include <stdbool.h> | ||
| 15 | #include <errno.h> | ||
| 16 | #include <math.h> | ||
| 17 | |||
| 18 | #include "callchain.h" | ||
| 19 | |||
| 20 | #define chain_for_each_child(child, parent) \ | ||
| 21 | list_for_each_entry(child, &parent->children, brothers) | ||
| 22 | |||
| 23 | static void | ||
| 24 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | ||
| 25 | enum chain_mode mode) | ||
| 26 | { | ||
| 27 | struct rb_node **p = &root->rb_node; | ||
| 28 | struct rb_node *parent = NULL; | ||
| 29 | struct callchain_node *rnode; | ||
| 30 | u64 chain_cumul = cumul_hits(chain); | ||
| 31 | |||
| 32 | while (*p) { | ||
| 33 | u64 rnode_cumul; | ||
| 34 | |||
| 35 | parent = *p; | ||
| 36 | rnode = rb_entry(parent, struct callchain_node, rb_node); | ||
| 37 | rnode_cumul = cumul_hits(rnode); | ||
| 38 | |||
| 39 | switch (mode) { | ||
| 40 | case CHAIN_FLAT: | ||
| 41 | if (rnode->hit < chain->hit) | ||
| 42 | p = &(*p)->rb_left; | ||
| 43 | else | ||
| 44 | p = &(*p)->rb_right; | ||
| 45 | break; | ||
| 46 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
| 47 | case CHAIN_GRAPH_REL: | ||
| 48 | if (rnode_cumul < chain_cumul) | ||
| 49 | p = &(*p)->rb_left; | ||
| 50 | else | ||
| 51 | p = &(*p)->rb_right; | ||
| 52 | break; | ||
| 53 | default: | ||
| 54 | break; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | rb_link_node(&chain->rb_node, parent, p); | ||
| 59 | rb_insert_color(&chain->rb_node, root); | ||
| 60 | } | ||
| 61 | |||
| 62 | static void | ||
| 63 | __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, | ||
| 64 | u64 min_hit) | ||
| 65 | { | ||
| 66 | struct callchain_node *child; | ||
| 67 | |||
| 68 | chain_for_each_child(child, node) | ||
| 69 | __sort_chain_flat(rb_root, child, min_hit); | ||
| 70 | |||
| 71 | if (node->hit && node->hit >= min_hit) | ||
| 72 | rb_insert_callchain(rb_root, node, CHAIN_FLAT); | ||
| 73 | } | ||
| 74 | |||
| 75 | /* | ||
| 76 | * Once we get every callchains from the stream, we can now | ||
| 77 | * sort them by hit | ||
| 78 | */ | ||
| 79 | static void | ||
| 80 | sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, | ||
| 81 | u64 min_hit, struct callchain_param *param __used) | ||
| 82 | { | ||
| 83 | __sort_chain_flat(rb_root, node, min_hit); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void __sort_chain_graph_abs(struct callchain_node *node, | ||
| 87 | u64 min_hit) | ||
| 88 | { | ||
| 89 | struct callchain_node *child; | ||
| 90 | |||
| 91 | node->rb_root = RB_ROOT; | ||
| 92 | |||
| 93 | chain_for_each_child(child, node) { | ||
| 94 | __sort_chain_graph_abs(child, min_hit); | ||
| 95 | if (cumul_hits(child) >= min_hit) | ||
| 96 | rb_insert_callchain(&node->rb_root, child, | ||
| 97 | CHAIN_GRAPH_ABS); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | static void | ||
| 102 | sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_node *chain_root, | ||
| 103 | u64 min_hit, struct callchain_param *param __used) | ||
| 104 | { | ||
| 105 | __sort_chain_graph_abs(chain_root, min_hit); | ||
| 106 | rb_root->rb_node = chain_root->rb_root.rb_node; | ||
| 107 | } | ||
| 108 | |||
| 109 | static void __sort_chain_graph_rel(struct callchain_node *node, | ||
| 110 | double min_percent) | ||
| 111 | { | ||
| 112 | struct callchain_node *child; | ||
| 113 | u64 min_hit; | ||
| 114 | |||
| 115 | node->rb_root = RB_ROOT; | ||
| 116 | min_hit = ceil(node->children_hit * min_percent); | ||
| 117 | |||
| 118 | chain_for_each_child(child, node) { | ||
| 119 | __sort_chain_graph_rel(child, min_percent); | ||
| 120 | if (cumul_hits(child) >= min_hit) | ||
| 121 | rb_insert_callchain(&node->rb_root, child, | ||
| 122 | CHAIN_GRAPH_REL); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | static void | ||
| 127 | sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root, | ||
| 128 | u64 min_hit __used, struct callchain_param *param) | ||
| 129 | { | ||
| 130 | __sort_chain_graph_rel(chain_root, param->min_percent / 100.0); | ||
| 131 | rb_root->rb_node = chain_root->rb_root.rb_node; | ||
| 132 | } | ||
| 133 | |||
| 134 | int register_callchain_param(struct callchain_param *param) | ||
| 135 | { | ||
| 136 | switch (param->mode) { | ||
| 137 | case CHAIN_GRAPH_ABS: | ||
| 138 | param->sort = sort_chain_graph_abs; | ||
| 139 | break; | ||
| 140 | case CHAIN_GRAPH_REL: | ||
| 141 | param->sort = sort_chain_graph_rel; | ||
| 142 | break; | ||
| 143 | case CHAIN_FLAT: | ||
| 144 | param->sort = sort_chain_flat; | ||
| 145 | break; | ||
| 146 | default: | ||
| 147 | return -1; | ||
| 148 | } | ||
| 149 | return 0; | ||
| 150 | } | ||
| 151 | |||
| 152 | /* | ||
| 153 | * Create a child for a parent. If inherit_children, then the new child | ||
| 154 | * will become the new parent of it's parent children | ||
| 155 | */ | ||
| 156 | static struct callchain_node * | ||
| 157 | create_child(struct callchain_node *parent, bool inherit_children) | ||
| 158 | { | ||
| 159 | struct callchain_node *new; | ||
| 160 | |||
| 161 | new = malloc(sizeof(*new)); | ||
| 162 | if (!new) { | ||
| 163 | perror("not enough memory to create child for code path tree"); | ||
| 164 | return NULL; | ||
| 165 | } | ||
| 166 | new->parent = parent; | ||
| 167 | INIT_LIST_HEAD(&new->children); | ||
| 168 | INIT_LIST_HEAD(&new->val); | ||
| 169 | |||
| 170 | if (inherit_children) { | ||
| 171 | struct callchain_node *next; | ||
| 172 | |||
| 173 | list_splice(&parent->children, &new->children); | ||
| 174 | INIT_LIST_HEAD(&parent->children); | ||
| 175 | |||
| 176 | chain_for_each_child(next, new) | ||
| 177 | next->parent = new; | ||
| 178 | } | ||
| 179 | list_add_tail(&new->brothers, &parent->children); | ||
| 180 | |||
| 181 | return new; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* | ||
| 185 | * Fill the node with callchain values | ||
| 186 | */ | ||
| 187 | static void | ||
| 188 | fill_node(struct callchain_node *node, struct ip_callchain *chain, | ||
| 189 | int start, struct symbol **syms) | ||
| 190 | { | ||
| 191 | unsigned int i; | ||
| 192 | |||
| 193 | for (i = start; i < chain->nr; i++) { | ||
| 194 | struct callchain_list *call; | ||
| 195 | |||
| 196 | call = malloc(sizeof(*call)); | ||
| 197 | if (!call) { | ||
| 198 | perror("not enough memory for the code path tree"); | ||
| 199 | return; | ||
| 200 | } | ||
| 201 | call->ip = chain->ips[i]; | ||
| 202 | call->sym = syms[i]; | ||
| 203 | list_add_tail(&call->list, &node->val); | ||
| 204 | } | ||
| 205 | node->val_nr = chain->nr - start; | ||
| 206 | if (!node->val_nr) | ||
| 207 | printf("Warning: empty node in callchain tree\n"); | ||
| 208 | } | ||
| 209 | |||
| 210 | static void | ||
| 211 | add_child(struct callchain_node *parent, struct ip_callchain *chain, | ||
| 212 | int start, struct symbol **syms) | ||
| 213 | { | ||
| 214 | struct callchain_node *new; | ||
| 215 | |||
| 216 | new = create_child(parent, false); | ||
| 217 | fill_node(new, chain, start, syms); | ||
| 218 | |||
| 219 | new->children_hit = 0; | ||
| 220 | new->hit = 1; | ||
| 221 | } | ||
| 222 | |||
| 223 | /* | ||
| 224 | * Split the parent in two parts (a new child is created) and | ||
| 225 | * give a part of its callchain to the created child. | ||
| 226 | * Then create another child to host the given callchain of new branch | ||
| 227 | */ | ||
| 228 | static void | ||
| 229 | split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | ||
| 230 | struct callchain_list *to_split, int idx_parents, int idx_local, | ||
| 231 | struct symbol **syms) | ||
| 232 | { | ||
| 233 | struct callchain_node *new; | ||
| 234 | struct list_head *old_tail; | ||
| 235 | unsigned int idx_total = idx_parents + idx_local; | ||
| 236 | |||
| 237 | /* split */ | ||
| 238 | new = create_child(parent, true); | ||
| 239 | |||
| 240 | /* split the callchain and move a part to the new child */ | ||
| 241 | old_tail = parent->val.prev; | ||
| 242 | list_del_range(&to_split->list, old_tail); | ||
| 243 | new->val.next = &to_split->list; | ||
| 244 | new->val.prev = old_tail; | ||
| 245 | to_split->list.prev = &new->val; | ||
| 246 | old_tail->next = &new->val; | ||
| 247 | |||
| 248 | /* split the hits */ | ||
| 249 | new->hit = parent->hit; | ||
| 250 | new->children_hit = parent->children_hit; | ||
| 251 | parent->children_hit = cumul_hits(new); | ||
| 252 | new->val_nr = parent->val_nr - idx_local; | ||
| 253 | parent->val_nr = idx_local; | ||
| 254 | |||
| 255 | /* create a new child for the new branch if any */ | ||
| 256 | if (idx_total < chain->nr) { | ||
| 257 | parent->hit = 0; | ||
| 258 | add_child(parent, chain, idx_total, syms); | ||
| 259 | parent->children_hit++; | ||
| 260 | } else { | ||
| 261 | parent->hit = 1; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | static int | ||
| 266 | __append_chain(struct callchain_node *root, struct ip_callchain *chain, | ||
| 267 | unsigned int start, struct symbol **syms); | ||
| 268 | |||
| 269 | static void | ||
| 270 | __append_chain_children(struct callchain_node *root, struct ip_callchain *chain, | ||
| 271 | struct symbol **syms, unsigned int start) | ||
| 272 | { | ||
| 273 | struct callchain_node *rnode; | ||
| 274 | |||
| 275 | /* lookup in childrens */ | ||
| 276 | chain_for_each_child(rnode, root) { | ||
| 277 | unsigned int ret = __append_chain(rnode, chain, start, syms); | ||
| 278 | |||
| 279 | if (!ret) | ||
| 280 | goto inc_children_hit; | ||
| 281 | } | ||
| 282 | /* nothing in children, add to the current node */ | ||
| 283 | add_child(root, chain, start, syms); | ||
| 284 | |||
| 285 | inc_children_hit: | ||
| 286 | root->children_hit++; | ||
| 287 | } | ||
| 288 | |||
| 289 | static int | ||
| 290 | __append_chain(struct callchain_node *root, struct ip_callchain *chain, | ||
| 291 | unsigned int start, struct symbol **syms) | ||
| 292 | { | ||
| 293 | struct callchain_list *cnode; | ||
| 294 | unsigned int i = start; | ||
| 295 | bool found = false; | ||
| 296 | |||
| 297 | /* | ||
| 298 | * Lookup in the current node | ||
| 299 | * If we have a symbol, then compare the start to match | ||
| 300 | * anywhere inside a function. | ||
| 301 | */ | ||
| 302 | list_for_each_entry(cnode, &root->val, list) { | ||
| 303 | if (i == chain->nr) | ||
| 304 | break; | ||
| 305 | if (cnode->sym && syms[i]) { | ||
| 306 | if (cnode->sym->start != syms[i]->start) | ||
| 307 | break; | ||
| 308 | } else if (cnode->ip != chain->ips[i]) | ||
| 309 | break; | ||
| 310 | if (!found) | ||
| 311 | found = true; | ||
| 312 | i++; | ||
| 313 | } | ||
| 314 | |||
| 315 | /* matches not, relay on the parent */ | ||
| 316 | if (!found) | ||
| 317 | return -1; | ||
| 318 | |||
| 319 | /* we match only a part of the node. Split it and add the new chain */ | ||
| 320 | if (i - start < root->val_nr) { | ||
| 321 | split_add_child(root, chain, cnode, start, i - start, syms); | ||
| 322 | return 0; | ||
| 323 | } | ||
| 324 | |||
| 325 | /* we match 100% of the path, increment the hit */ | ||
| 326 | if (i - start == root->val_nr && i == chain->nr) { | ||
| 327 | root->hit++; | ||
| 328 | return 0; | ||
| 329 | } | ||
| 330 | |||
| 331 | /* We match the node and still have a part remaining */ | ||
| 332 | __append_chain_children(root, chain, syms, i); | ||
| 333 | |||
| 334 | return 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | ||
| 338 | struct symbol **syms) | ||
| 339 | { | ||
| 340 | if (!chain->nr) | ||
| 341 | return; | ||
| 342 | __append_chain_children(root, chain, syms, 0); | ||
| 343 | } | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h new file mode 100644 index 000000000000..a926ae4f5a16 --- /dev/null +++ b/tools/perf/util/callchain.h | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | #ifndef __PERF_CALLCHAIN_H | ||
| 2 | #define __PERF_CALLCHAIN_H | ||
| 3 | |||
| 4 | #include "../perf.h" | ||
| 5 | #include <linux/list.h> | ||
| 6 | #include <linux/rbtree.h> | ||
| 7 | #include "symbol.h" | ||
| 8 | |||
| 9 | enum chain_mode { | ||
| 10 | CHAIN_NONE, | ||
| 11 | CHAIN_FLAT, | ||
| 12 | CHAIN_GRAPH_ABS, | ||
| 13 | CHAIN_GRAPH_REL | ||
| 14 | }; | ||
| 15 | |||
| 16 | struct callchain_node { | ||
| 17 | struct callchain_node *parent; | ||
| 18 | struct list_head brothers; | ||
| 19 | struct list_head children; | ||
| 20 | struct list_head val; | ||
| 21 | struct rb_node rb_node; /* to sort nodes in an rbtree */ | ||
| 22 | struct rb_root rb_root; /* sorted tree of children */ | ||
| 23 | unsigned int val_nr; | ||
| 24 | u64 hit; | ||
| 25 | u64 children_hit; | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct callchain_param; | ||
| 29 | |||
| 30 | typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *, | ||
| 31 | u64, struct callchain_param *); | ||
| 32 | |||
| 33 | struct callchain_param { | ||
| 34 | enum chain_mode mode; | ||
| 35 | double min_percent; | ||
| 36 | sort_chain_func_t sort; | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct callchain_list { | ||
| 40 | u64 ip; | ||
| 41 | struct symbol *sym; | ||
| 42 | struct list_head list; | ||
| 43 | }; | ||
| 44 | |||
| 45 | static inline void callchain_init(struct callchain_node *node) | ||
| 46 | { | ||
| 47 | INIT_LIST_HEAD(&node->brothers); | ||
| 48 | INIT_LIST_HEAD(&node->children); | ||
| 49 | INIT_LIST_HEAD(&node->val); | ||
| 50 | } | ||
| 51 | |||
| 52 | static inline u64 cumul_hits(struct callchain_node *node) | ||
| 53 | { | ||
| 54 | return node->hit + node->children_hit; | ||
| 55 | } | ||
| 56 | |||
| 57 | int register_callchain_param(struct callchain_param *param); | ||
| 58 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | ||
| 59 | struct symbol **syms); | ||
| 60 | #endif | ||
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c new file mode 100644 index 000000000000..90a044d1fe7d --- /dev/null +++ b/tools/perf/util/color.c | |||
| @@ -0,0 +1,272 @@ | |||
| 1 | #include "cache.h" | ||
| 2 | #include "color.h" | ||
| 3 | |||
| 4 | int perf_use_color_default = -1; | ||
| 5 | |||
| 6 | static int parse_color(const char *name, int len) | ||
| 7 | { | ||
| 8 | static const char * const color_names[] = { | ||
| 9 | "normal", "black", "red", "green", "yellow", | ||
| 10 | "blue", "magenta", "cyan", "white" | ||
| 11 | }; | ||
| 12 | char *end; | ||
| 13 | int i; | ||
| 14 | |||
| 15 | for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) { | ||
| 16 | const char *str = color_names[i]; | ||
| 17 | if (!strncasecmp(name, str, len) && !str[len]) | ||
| 18 | return i - 1; | ||
| 19 | } | ||
| 20 | i = strtol(name, &end, 10); | ||
| 21 | if (end - name == len && i >= -1 && i <= 255) | ||
| 22 | return i; | ||
| 23 | return -2; | ||
| 24 | } | ||
| 25 | |||
| 26 | static int parse_attr(const char *name, int len) | ||
| 27 | { | ||
| 28 | static const int attr_values[] = { 1, 2, 4, 5, 7 }; | ||
| 29 | static const char * const attr_names[] = { | ||
| 30 | "bold", "dim", "ul", "blink", "reverse" | ||
| 31 | }; | ||
| 32 | unsigned int i; | ||
| 33 | |||
| 34 | for (i = 0; i < ARRAY_SIZE(attr_names); i++) { | ||
| 35 | const char *str = attr_names[i]; | ||
| 36 | if (!strncasecmp(name, str, len) && !str[len]) | ||
| 37 | return attr_values[i]; | ||
| 38 | } | ||
| 39 | return -1; | ||
| 40 | } | ||
| 41 | |||
| 42 | void color_parse(const char *value, const char *var, char *dst) | ||
| 43 | { | ||
| 44 | color_parse_mem(value, strlen(value), var, dst); | ||
| 45 | } | ||
| 46 | |||
| 47 | void color_parse_mem(const char *value, int value_len, const char *var, | ||
| 48 | char *dst) | ||
| 49 | { | ||
| 50 | const char *ptr = value; | ||
| 51 | int len = value_len; | ||
| 52 | int attr = -1; | ||
| 53 | int fg = -2; | ||
| 54 | int bg = -2; | ||
| 55 | |||
| 56 | if (!strncasecmp(value, "reset", len)) { | ||
| 57 | strcpy(dst, PERF_COLOR_RESET); | ||
| 58 | return; | ||
| 59 | } | ||
| 60 | |||
| 61 | /* [fg [bg]] [attr] */ | ||
| 62 | while (len > 0) { | ||
| 63 | const char *word = ptr; | ||
| 64 | int val, wordlen = 0; | ||
| 65 | |||
| 66 | while (len > 0 && !isspace(word[wordlen])) { | ||
| 67 | wordlen++; | ||
| 68 | len--; | ||
| 69 | } | ||
| 70 | |||
| 71 | ptr = word + wordlen; | ||
| 72 | while (len > 0 && isspace(*ptr)) { | ||
| 73 | ptr++; | ||
| 74 | len--; | ||
| 75 | } | ||
| 76 | |||
| 77 | val = parse_color(word, wordlen); | ||
| 78 | if (val >= -1) { | ||
| 79 | if (fg == -2) { | ||
| 80 | fg = val; | ||
| 81 | continue; | ||
| 82 | } | ||
| 83 | if (bg == -2) { | ||
| 84 | bg = val; | ||
| 85 | continue; | ||
| 86 | } | ||
| 87 | goto bad; | ||
| 88 | } | ||
| 89 | val = parse_attr(word, wordlen); | ||
| 90 | if (val < 0 || attr != -1) | ||
| 91 | goto bad; | ||
| 92 | attr = val; | ||
| 93 | } | ||
| 94 | |||
| 95 | if (attr >= 0 || fg >= 0 || bg >= 0) { | ||
| 96 | int sep = 0; | ||
| 97 | |||
| 98 | *dst++ = '\033'; | ||
| 99 | *dst++ = '['; | ||
| 100 | if (attr >= 0) { | ||
| 101 | *dst++ = '0' + attr; | ||
| 102 | sep++; | ||
| 103 | } | ||
| 104 | if (fg >= 0) { | ||
| 105 | if (sep++) | ||
| 106 | *dst++ = ';'; | ||
| 107 | if (fg < 8) { | ||
| 108 | *dst++ = '3'; | ||
| 109 | *dst++ = '0' + fg; | ||
| 110 | } else { | ||
| 111 | dst += sprintf(dst, "38;5;%d", fg); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | if (bg >= 0) { | ||
| 115 | if (sep++) | ||
| 116 | *dst++ = ';'; | ||
| 117 | if (bg < 8) { | ||
| 118 | *dst++ = '4'; | ||
| 119 | *dst++ = '0' + bg; | ||
| 120 | } else { | ||
| 121 | dst += sprintf(dst, "48;5;%d", bg); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | *dst++ = 'm'; | ||
| 125 | } | ||
| 126 | *dst = 0; | ||
| 127 | return; | ||
| 128 | bad: | ||
| 129 | die("bad color value '%.*s' for variable '%s'", value_len, value, var); | ||
| 130 | } | ||
| 131 | |||
| 132 | int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty) | ||
| 133 | { | ||
| 134 | if (value) { | ||
| 135 | if (!strcasecmp(value, "never")) | ||
| 136 | return 0; | ||
| 137 | if (!strcasecmp(value, "always")) | ||
| 138 | return 1; | ||
| 139 | if (!strcasecmp(value, "auto")) | ||
| 140 | goto auto_color; | ||
| 141 | } | ||
| 142 | |||
| 143 | /* Missing or explicit false to turn off colorization */ | ||
| 144 | if (!perf_config_bool(var, value)) | ||
| 145 | return 0; | ||
| 146 | |||
| 147 | /* any normal truth value defaults to 'auto' */ | ||
| 148 | auto_color: | ||
| 149 | if (stdout_is_tty < 0) | ||
| 150 | stdout_is_tty = isatty(1); | ||
| 151 | if (stdout_is_tty || (pager_in_use() && pager_use_color)) { | ||
| 152 | char *term = getenv("TERM"); | ||
| 153 | if (term && strcmp(term, "dumb")) | ||
| 154 | return 1; | ||
| 155 | } | ||
| 156 | return 0; | ||
| 157 | } | ||
| 158 | |||
| 159 | int perf_color_default_config(const char *var, const char *value, void *cb) | ||
| 160 | { | ||
| 161 | if (!strcmp(var, "color.ui")) { | ||
| 162 | perf_use_color_default = perf_config_colorbool(var, value, -1); | ||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | |||
| 166 | return perf_default_config(var, value, cb); | ||
| 167 | } | ||
| 168 | |||
| 169 | static int color_vfprintf(FILE *fp, const char *color, const char *fmt, | ||
| 170 | va_list args, const char *trail) | ||
| 171 | { | ||
| 172 | int r = 0; | ||
| 173 | |||
| 174 | /* | ||
| 175 | * Auto-detect: | ||
| 176 | */ | ||
| 177 | if (perf_use_color_default < 0) { | ||
| 178 | if (isatty(1) || pager_in_use()) | ||
| 179 | perf_use_color_default = 1; | ||
| 180 | else | ||
| 181 | perf_use_color_default = 0; | ||
| 182 | } | ||
| 183 | |||
| 184 | if (perf_use_color_default && *color) | ||
| 185 | r += fprintf(fp, "%s", color); | ||
| 186 | r += vfprintf(fp, fmt, args); | ||
| 187 | if (perf_use_color_default && *color) | ||
| 188 | r += fprintf(fp, "%s", PERF_COLOR_RESET); | ||
| 189 | if (trail) | ||
| 190 | r += fprintf(fp, "%s", trail); | ||
| 191 | return r; | ||
| 192 | } | ||
| 193 | |||
| 194 | |||
| 195 | |||
| 196 | int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) | ||
| 197 | { | ||
| 198 | va_list args; | ||
| 199 | int r; | ||
| 200 | |||
| 201 | va_start(args, fmt); | ||
| 202 | r = color_vfprintf(fp, color, fmt, args, NULL); | ||
| 203 | va_end(args); | ||
| 204 | return r; | ||
| 205 | } | ||
| 206 | |||
| 207 | int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...) | ||
| 208 | { | ||
| 209 | va_list args; | ||
| 210 | int r; | ||
| 211 | va_start(args, fmt); | ||
| 212 | r = color_vfprintf(fp, color, fmt, args, "\n"); | ||
| 213 | va_end(args); | ||
| 214 | return r; | ||
| 215 | } | ||
| 216 | |||
| 217 | /* | ||
| 218 | * This function splits the buffer by newlines and colors the lines individually. | ||
| 219 | * | ||
| 220 | * Returns 0 on success. | ||
| 221 | */ | ||
| 222 | int color_fwrite_lines(FILE *fp, const char *color, | ||
| 223 | size_t count, const char *buf) | ||
| 224 | { | ||
| 225 | if (!*color) | ||
| 226 | return fwrite(buf, count, 1, fp) != 1; | ||
| 227 | |||
| 228 | while (count) { | ||
| 229 | char *p = memchr(buf, '\n', count); | ||
| 230 | |||
| 231 | if (p != buf && (fputs(color, fp) < 0 || | ||
| 232 | fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 || | ||
| 233 | fputs(PERF_COLOR_RESET, fp) < 0)) | ||
| 234 | return -1; | ||
| 235 | if (!p) | ||
| 236 | return 0; | ||
| 237 | if (fputc('\n', fp) < 0) | ||
| 238 | return -1; | ||
| 239 | count -= p + 1 - buf; | ||
| 240 | buf = p + 1; | ||
| 241 | } | ||
| 242 | return 0; | ||
| 243 | } | ||
| 244 | |||
| 245 | char *get_percent_color(double percent) | ||
| 246 | { | ||
| 247 | char *color = PERF_COLOR_NORMAL; | ||
| 248 | |||
| 249 | /* | ||
| 250 | * We color high-overhead entries in red, mid-overhead | ||
| 251 | * entries in green - and keep the low overhead places | ||
| 252 | * normal: | ||
| 253 | */ | ||
| 254 | if (percent >= MIN_RED) | ||
| 255 | color = PERF_COLOR_RED; | ||
| 256 | else { | ||
| 257 | if (percent > MIN_GREEN) | ||
| 258 | color = PERF_COLOR_GREEN; | ||
| 259 | } | ||
| 260 | return color; | ||
| 261 | } | ||
| 262 | |||
| 263 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent) | ||
| 264 | { | ||
| 265 | int r; | ||
| 266 | char *color; | ||
| 267 | |||
| 268 | color = get_percent_color(percent); | ||
| 269 | r = color_fprintf(fp, color, fmt, percent); | ||
| 270 | |||
| 271 | return r; | ||
| 272 | } | ||
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h new file mode 100644 index 000000000000..706cec50bd25 --- /dev/null +++ b/tools/perf/util/color.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | #ifndef COLOR_H | ||
| 2 | #define COLOR_H | ||
| 3 | |||
| 4 | /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ | ||
| 5 | #define COLOR_MAXLEN 24 | ||
| 6 | |||
| 7 | #define PERF_COLOR_NORMAL "" | ||
| 8 | #define PERF_COLOR_RESET "\033[m" | ||
| 9 | #define PERF_COLOR_BOLD "\033[1m" | ||
| 10 | #define PERF_COLOR_RED "\033[31m" | ||
| 11 | #define PERF_COLOR_GREEN "\033[32m" | ||
| 12 | #define PERF_COLOR_YELLOW "\033[33m" | ||
| 13 | #define PERF_COLOR_BLUE "\033[34m" | ||
| 14 | #define PERF_COLOR_MAGENTA "\033[35m" | ||
| 15 | #define PERF_COLOR_CYAN "\033[36m" | ||
| 16 | #define PERF_COLOR_BG_RED "\033[41m" | ||
| 17 | |||
| 18 | #define MIN_GREEN 0.5 | ||
| 19 | #define MIN_RED 5.0 | ||
| 20 | |||
| 21 | /* | ||
| 22 | * This variable stores the value of color.ui | ||
| 23 | */ | ||
| 24 | extern int perf_use_color_default; | ||
| 25 | |||
| 26 | |||
| 27 | /* | ||
| 28 | * Use this instead of perf_default_config if you need the value of color.ui. | ||
| 29 | */ | ||
| 30 | int perf_color_default_config(const char *var, const char *value, void *cb); | ||
| 31 | |||
| 32 | int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty); | ||
| 33 | void color_parse(const char *value, const char *var, char *dst); | ||
| 34 | void color_parse_mem(const char *value, int len, const char *var, char *dst); | ||
| 35 | int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); | ||
| 36 | int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); | ||
| 37 | int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); | ||
| 38 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); | ||
| 39 | char *get_percent_color(double percent); | ||
| 40 | |||
| 41 | #endif /* COLOR_H */ | ||
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c new file mode 100644 index 000000000000..780df541006d --- /dev/null +++ b/tools/perf/util/config.c | |||
| @@ -0,0 +1,875 @@ | |||
| 1 | /* | ||
| 2 | * GIT - The information manager from hell | ||
| 3 | * | ||
| 4 | * Copyright (C) Linus Torvalds, 2005 | ||
| 5 | * Copyright (C) Johannes Schindelin, 2005 | ||
| 6 | * | ||
| 7 | */ | ||
| 8 | #include "util.h" | ||
| 9 | #include "cache.h" | ||
| 10 | #include "exec_cmd.h" | ||
| 11 | |||
| 12 | #define MAXNAME (256) | ||
| 13 | |||
| 14 | static FILE *config_file; | ||
| 15 | static const char *config_file_name; | ||
| 16 | static int config_linenr; | ||
| 17 | static int config_file_eof; | ||
| 18 | |||
| 19 | const char *config_exclusive_filename = NULL; | ||
| 20 | |||
| 21 | static int get_next_char(void) | ||
| 22 | { | ||
| 23 | int c; | ||
| 24 | FILE *f; | ||
| 25 | |||
| 26 | c = '\n'; | ||
| 27 | if ((f = config_file) != NULL) { | ||
| 28 | c = fgetc(f); | ||
| 29 | if (c == '\r') { | ||
| 30 | /* DOS like systems */ | ||
| 31 | c = fgetc(f); | ||
| 32 | if (c != '\n') { | ||
| 33 | ungetc(c, f); | ||
| 34 | c = '\r'; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | if (c == '\n') | ||
| 38 | config_linenr++; | ||
| 39 | if (c == EOF) { | ||
| 40 | config_file_eof = 1; | ||
| 41 | c = '\n'; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | return c; | ||
| 45 | } | ||
| 46 | |||
| 47 | static char *parse_value(void) | ||
| 48 | { | ||
| 49 | static char value[1024]; | ||
| 50 | int quote = 0, comment = 0, space = 0; | ||
| 51 | size_t len = 0; | ||
| 52 | |||
| 53 | for (;;) { | ||
| 54 | int c = get_next_char(); | ||
| 55 | |||
| 56 | if (len >= sizeof(value) - 1) | ||
| 57 | return NULL; | ||
| 58 | if (c == '\n') { | ||
| 59 | if (quote) | ||
| 60 | return NULL; | ||
| 61 | value[len] = 0; | ||
| 62 | return value; | ||
| 63 | } | ||
| 64 | if (comment) | ||
| 65 | continue; | ||
| 66 | if (isspace(c) && !quote) { | ||
| 67 | space = 1; | ||
| 68 | continue; | ||
| 69 | } | ||
| 70 | if (!quote) { | ||
| 71 | if (c == ';' || c == '#') { | ||
| 72 | comment = 1; | ||
| 73 | continue; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | if (space) { | ||
| 77 | if (len) | ||
| 78 | value[len++] = ' '; | ||
| 79 | space = 0; | ||
| 80 | } | ||
| 81 | if (c == '\\') { | ||
| 82 | c = get_next_char(); | ||
| 83 | switch (c) { | ||
| 84 | case '\n': | ||
| 85 | continue; | ||
| 86 | case 't': | ||
| 87 | c = '\t'; | ||
| 88 | break; | ||
| 89 | case 'b': | ||
| 90 | c = '\b'; | ||
| 91 | break; | ||
| 92 | case 'n': | ||
| 93 | c = '\n'; | ||
| 94 | break; | ||
| 95 | /* Some characters escape as themselves */ | ||
| 96 | case '\\': case '"': | ||
| 97 | break; | ||
| 98 | /* Reject unknown escape sequences */ | ||
| 99 | default: | ||
| 100 | return NULL; | ||
| 101 | } | ||
| 102 | value[len++] = c; | ||
| 103 | continue; | ||
| 104 | } | ||
| 105 | if (c == '"') { | ||
| 106 | quote = 1-quote; | ||
| 107 | continue; | ||
| 108 | } | ||
| 109 | value[len++] = c; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | static inline int iskeychar(int c) | ||
| 114 | { | ||
| 115 | return isalnum(c) || c == '-'; | ||
| 116 | } | ||
| 117 | |||
| 118 | static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) | ||
| 119 | { | ||
| 120 | int c; | ||
| 121 | char *value; | ||
| 122 | |||
| 123 | /* Get the full name */ | ||
| 124 | for (;;) { | ||
| 125 | c = get_next_char(); | ||
| 126 | if (config_file_eof) | ||
| 127 | break; | ||
| 128 | if (!iskeychar(c)) | ||
| 129 | break; | ||
| 130 | name[len++] = tolower(c); | ||
| 131 | if (len >= MAXNAME) | ||
| 132 | return -1; | ||
| 133 | } | ||
| 134 | name[len] = 0; | ||
| 135 | while (c == ' ' || c == '\t') | ||
| 136 | c = get_next_char(); | ||
| 137 | |||
| 138 | value = NULL; | ||
| 139 | if (c != '\n') { | ||
| 140 | if (c != '=') | ||
| 141 | return -1; | ||
| 142 | value = parse_value(); | ||
| 143 | if (!value) | ||
| 144 | return -1; | ||
| 145 | } | ||
| 146 | return fn(name, value, data); | ||
| 147 | } | ||
| 148 | |||
| 149 | static int get_extended_base_var(char *name, int baselen, int c) | ||
| 150 | { | ||
| 151 | do { | ||
| 152 | if (c == '\n') | ||
| 153 | return -1; | ||
| 154 | c = get_next_char(); | ||
| 155 | } while (isspace(c)); | ||
| 156 | |||
| 157 | /* We require the format to be '[base "extension"]' */ | ||
| 158 | if (c != '"') | ||
| 159 | return -1; | ||
| 160 | name[baselen++] = '.'; | ||
| 161 | |||
| 162 | for (;;) { | ||
| 163 | int c = get_next_char(); | ||
| 164 | if (c == '\n') | ||
| 165 | return -1; | ||
| 166 | if (c == '"') | ||
| 167 | break; | ||
| 168 | if (c == '\\') { | ||
| 169 | c = get_next_char(); | ||
| 170 | if (c == '\n') | ||
| 171 | return -1; | ||
| 172 | } | ||
| 173 | name[baselen++] = c; | ||
| 174 | if (baselen > MAXNAME / 2) | ||
| 175 | return -1; | ||
| 176 | } | ||
| 177 | |||
| 178 | /* Final ']' */ | ||
| 179 | if (get_next_char() != ']') | ||
| 180 | return -1; | ||
| 181 | return baselen; | ||
| 182 | } | ||
| 183 | |||
| 184 | static int get_base_var(char *name) | ||
| 185 | { | ||
| 186 | int baselen = 0; | ||
| 187 | |||
| 188 | for (;;) { | ||
| 189 | int c = get_next_char(); | ||
| 190 | if (config_file_eof) | ||
| 191 | return -1; | ||
| 192 | if (c == ']') | ||
| 193 | return baselen; | ||
| 194 | if (isspace(c)) | ||
| 195 | return get_extended_base_var(name, baselen, c); | ||
| 196 | if (!iskeychar(c) && c != '.') | ||
| 197 | return -1; | ||
| 198 | if (baselen > MAXNAME / 2) | ||
| 199 | return -1; | ||
| 200 | name[baselen++] = tolower(c); | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | static int perf_parse_file(config_fn_t fn, void *data) | ||
| 205 | { | ||
| 206 | int comment = 0; | ||
| 207 | int baselen = 0; | ||
| 208 | static char var[MAXNAME]; | ||
| 209 | |||
| 210 | /* U+FEFF Byte Order Mark in UTF8 */ | ||
| 211 | static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf"; | ||
| 212 | const unsigned char *bomptr = utf8_bom; | ||
| 213 | |||
| 214 | for (;;) { | ||
| 215 | int c = get_next_char(); | ||
| 216 | if (bomptr && *bomptr) { | ||
| 217 | /* We are at the file beginning; skip UTF8-encoded BOM | ||
| 218 | * if present. Sane editors won't put this in on their | ||
| 219 | * own, but e.g. Windows Notepad will do it happily. */ | ||
| 220 | if ((unsigned char) c == *bomptr) { | ||
| 221 | bomptr++; | ||
| 222 | continue; | ||
| 223 | } else { | ||
| 224 | /* Do not tolerate partial BOM. */ | ||
| 225 | if (bomptr != utf8_bom) | ||
| 226 | break; | ||
| 227 | /* No BOM at file beginning. Cool. */ | ||
| 228 | bomptr = NULL; | ||
| 229 | } | ||
| 230 | } | ||
| 231 | if (c == '\n') { | ||
| 232 | if (config_file_eof) | ||
| 233 | return 0; | ||
| 234 | comment = 0; | ||
| 235 | continue; | ||
| 236 | } | ||
| 237 | if (comment || isspace(c)) | ||
| 238 | continue; | ||
| 239 | if (c == '#' || c == ';') { | ||
| 240 | comment = 1; | ||
| 241 | continue; | ||
| 242 | } | ||
| 243 | if (c == '[') { | ||
| 244 | baselen = get_base_var(var); | ||
| 245 | if (baselen <= 0) | ||
| 246 | break; | ||
| 247 | var[baselen++] = '.'; | ||
| 248 | var[baselen] = 0; | ||
| 249 | continue; | ||
| 250 | } | ||
| 251 | if (!isalpha(c)) | ||
| 252 | break; | ||
| 253 | var[baselen] = tolower(c); | ||
| 254 | if (get_value(fn, data, var, baselen+1) < 0) | ||
| 255 | break; | ||
| 256 | } | ||
| 257 | die("bad config file line %d in %s", config_linenr, config_file_name); | ||
| 258 | } | ||
| 259 | |||
| 260 | static int parse_unit_factor(const char *end, unsigned long *val) | ||
| 261 | { | ||
| 262 | if (!*end) | ||
| 263 | return 1; | ||
| 264 | else if (!strcasecmp(end, "k")) { | ||
| 265 | *val *= 1024; | ||
| 266 | return 1; | ||
| 267 | } | ||
| 268 | else if (!strcasecmp(end, "m")) { | ||
| 269 | *val *= 1024 * 1024; | ||
| 270 | return 1; | ||
| 271 | } | ||
| 272 | else if (!strcasecmp(end, "g")) { | ||
| 273 | *val *= 1024 * 1024 * 1024; | ||
| 274 | return 1; | ||
| 275 | } | ||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | |||
| 279 | static int perf_parse_long(const char *value, long *ret) | ||
| 280 | { | ||
| 281 | if (value && *value) { | ||
| 282 | char *end; | ||
| 283 | long val = strtol(value, &end, 0); | ||
| 284 | unsigned long factor = 1; | ||
| 285 | if (!parse_unit_factor(end, &factor)) | ||
| 286 | return 0; | ||
| 287 | *ret = val * factor; | ||
| 288 | return 1; | ||
| 289 | } | ||
| 290 | return 0; | ||
| 291 | } | ||
| 292 | |||
| 293 | int perf_parse_ulong(const char *value, unsigned long *ret) | ||
| 294 | { | ||
| 295 | if (value && *value) { | ||
| 296 | char *end; | ||
| 297 | unsigned long val = strtoul(value, &end, 0); | ||
| 298 | if (!parse_unit_factor(end, &val)) | ||
| 299 | return 0; | ||
| 300 | *ret = val; | ||
| 301 | return 1; | ||
| 302 | } | ||
| 303 | return 0; | ||
| 304 | } | ||
| 305 | |||
| 306 | static void die_bad_config(const char *name) | ||
| 307 | { | ||
| 308 | if (config_file_name) | ||
| 309 | die("bad config value for '%s' in %s", name, config_file_name); | ||
| 310 | die("bad config value for '%s'", name); | ||
| 311 | } | ||
| 312 | |||
| 313 | int perf_config_int(const char *name, const char *value) | ||
| 314 | { | ||
| 315 | long ret = 0; | ||
| 316 | if (!perf_parse_long(value, &ret)) | ||
| 317 | die_bad_config(name); | ||
| 318 | return ret; | ||
| 319 | } | ||
| 320 | |||
| 321 | unsigned long perf_config_ulong(const char *name, const char *value) | ||
| 322 | { | ||
| 323 | unsigned long ret; | ||
| 324 | if (!perf_parse_ulong(value, &ret)) | ||
| 325 | die_bad_config(name); | ||
| 326 | return ret; | ||
| 327 | } | ||
| 328 | |||
| 329 | int perf_config_bool_or_int(const char *name, const char *value, int *is_bool) | ||
| 330 | { | ||
| 331 | *is_bool = 1; | ||
| 332 | if (!value) | ||
| 333 | return 1; | ||
| 334 | if (!*value) | ||
| 335 | return 0; | ||
| 336 | if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on")) | ||
| 337 | return 1; | ||
| 338 | if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off")) | ||
| 339 | return 0; | ||
| 340 | *is_bool = 0; | ||
| 341 | return perf_config_int(name, value); | ||
| 342 | } | ||
| 343 | |||
| 344 | int perf_config_bool(const char *name, const char *value) | ||
| 345 | { | ||
| 346 | int discard; | ||
| 347 | return !!perf_config_bool_or_int(name, value, &discard); | ||
| 348 | } | ||
| 349 | |||
| 350 | int perf_config_string(const char **dest, const char *var, const char *value) | ||
| 351 | { | ||
| 352 | if (!value) | ||
| 353 | return config_error_nonbool(var); | ||
| 354 | *dest = strdup(value); | ||
| 355 | return 0; | ||
| 356 | } | ||
| 357 | |||
| 358 | static int perf_default_core_config(const char *var __used, const char *value __used) | ||
| 359 | { | ||
| 360 | /* Add other config variables here and to Documentation/config.txt. */ | ||
| 361 | return 0; | ||
| 362 | } | ||
| 363 | |||
| 364 | int perf_default_config(const char *var, const char *value, void *dummy __used) | ||
| 365 | { | ||
| 366 | if (!prefixcmp(var, "core.")) | ||
| 367 | return perf_default_core_config(var, value); | ||
| 368 | |||
| 369 | /* Add other config variables here and to Documentation/config.txt. */ | ||
| 370 | return 0; | ||
| 371 | } | ||
| 372 | |||
| 373 | int perf_config_from_file(config_fn_t fn, const char *filename, void *data) | ||
| 374 | { | ||
| 375 | int ret; | ||
| 376 | FILE *f = fopen(filename, "r"); | ||
| 377 | |||
| 378 | ret = -1; | ||
| 379 | if (f) { | ||
| 380 | config_file = f; | ||
| 381 | config_file_name = filename; | ||
| 382 | config_linenr = 1; | ||
| 383 | config_file_eof = 0; | ||
| 384 | ret = perf_parse_file(fn, data); | ||
| 385 | fclose(f); | ||
| 386 | config_file_name = NULL; | ||
| 387 | } | ||
| 388 | return ret; | ||
| 389 | } | ||
| 390 | |||
| 391 | const char *perf_etc_perfconfig(void) | ||
| 392 | { | ||
| 393 | static const char *system_wide; | ||
| 394 | if (!system_wide) | ||
| 395 | system_wide = system_path(ETC_PERFCONFIG); | ||
| 396 | return system_wide; | ||
| 397 | } | ||
| 398 | |||
| 399 | static int perf_env_bool(const char *k, int def) | ||
| 400 | { | ||
| 401 | const char *v = getenv(k); | ||
| 402 | return v ? perf_config_bool(k, v) : def; | ||
| 403 | } | ||
| 404 | |||
| 405 | int perf_config_system(void) | ||
| 406 | { | ||
| 407 | return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); | ||
| 408 | } | ||
| 409 | |||
| 410 | int perf_config_global(void) | ||
| 411 | { | ||
| 412 | return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); | ||
| 413 | } | ||
| 414 | |||
| 415 | int perf_config(config_fn_t fn, void *data) | ||
| 416 | { | ||
| 417 | int ret = 0, found = 0; | ||
| 418 | char *repo_config = NULL; | ||
| 419 | const char *home = NULL; | ||
| 420 | |||
| 421 | /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ | ||
| 422 | if (config_exclusive_filename) | ||
| 423 | return perf_config_from_file(fn, config_exclusive_filename, data); | ||
| 424 | if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { | ||
| 425 | ret += perf_config_from_file(fn, perf_etc_perfconfig(), | ||
| 426 | data); | ||
| 427 | found += 1; | ||
| 428 | } | ||
| 429 | |||
| 430 | home = getenv("HOME"); | ||
| 431 | if (perf_config_global() && home) { | ||
| 432 | char *user_config = strdup(mkpath("%s/.perfconfig", home)); | ||
| 433 | if (!access(user_config, R_OK)) { | ||
| 434 | ret += perf_config_from_file(fn, user_config, data); | ||
| 435 | found += 1; | ||
| 436 | } | ||
| 437 | free(user_config); | ||
| 438 | } | ||
| 439 | |||
| 440 | repo_config = perf_pathdup("config"); | ||
| 441 | if (!access(repo_config, R_OK)) { | ||
| 442 | ret += perf_config_from_file(fn, repo_config, data); | ||
| 443 | found += 1; | ||
| 444 | } | ||
| 445 | free(repo_config); | ||
| 446 | if (found == 0) | ||
| 447 | return -1; | ||
| 448 | return ret; | ||
| 449 | } | ||
| 450 | |||
| 451 | /* | ||
| 452 | * Find all the stuff for perf_config_set() below. | ||
| 453 | */ | ||
| 454 | |||
| 455 | #define MAX_MATCHES 512 | ||
| 456 | |||
| 457 | static struct { | ||
| 458 | int baselen; | ||
| 459 | char* key; | ||
| 460 | int do_not_match; | ||
| 461 | regex_t* value_regex; | ||
| 462 | int multi_replace; | ||
| 463 | size_t offset[MAX_MATCHES]; | ||
| 464 | enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state; | ||
| 465 | int seen; | ||
| 466 | } store; | ||
| 467 | |||
| 468 | static int matches(const char* key, const char* value) | ||
| 469 | { | ||
| 470 | return !strcmp(key, store.key) && | ||
| 471 | (store.value_regex == NULL || | ||
| 472 | (store.do_not_match ^ | ||
| 473 | !regexec(store.value_regex, value, 0, NULL, 0))); | ||
| 474 | } | ||
| 475 | |||
| 476 | static int store_aux(const char* key, const char* value, void *cb __used) | ||
| 477 | { | ||
| 478 | int section_len; | ||
| 479 | const char *ep; | ||
| 480 | |||
| 481 | switch (store.state) { | ||
| 482 | case KEY_SEEN: | ||
| 483 | if (matches(key, value)) { | ||
| 484 | if (store.seen == 1 && store.multi_replace == 0) { | ||
| 485 | warning("%s has multiple values", key); | ||
| 486 | } else if (store.seen >= MAX_MATCHES) { | ||
| 487 | error("too many matches for %s", key); | ||
| 488 | return 1; | ||
| 489 | } | ||
| 490 | |||
| 491 | store.offset[store.seen] = ftell(config_file); | ||
| 492 | store.seen++; | ||
| 493 | } | ||
| 494 | break; | ||
| 495 | case SECTION_SEEN: | ||
| 496 | /* | ||
| 497 | * What we are looking for is in store.key (both | ||
| 498 | * section and var), and its section part is baselen | ||
| 499 | * long. We found key (again, both section and var). | ||
| 500 | * We would want to know if this key is in the same | ||
| 501 | * section as what we are looking for. We already | ||
| 502 | * know we are in the same section as what should | ||
| 503 | * hold store.key. | ||
| 504 | */ | ||
| 505 | ep = strrchr(key, '.'); | ||
| 506 | section_len = ep - key; | ||
| 507 | |||
| 508 | if ((section_len != store.baselen) || | ||
| 509 | memcmp(key, store.key, section_len+1)) { | ||
| 510 | store.state = SECTION_END_SEEN; | ||
| 511 | break; | ||
| 512 | } | ||
| 513 | |||
| 514 | /* | ||
| 515 | * Do not increment matches: this is no match, but we | ||
| 516 | * just made sure we are in the desired section. | ||
| 517 | */ | ||
| 518 | store.offset[store.seen] = ftell(config_file); | ||
| 519 | /* fallthru */ | ||
| 520 | case SECTION_END_SEEN: | ||
| 521 | case START: | ||
| 522 | if (matches(key, value)) { | ||
| 523 | store.offset[store.seen] = ftell(config_file); | ||
| 524 | store.state = KEY_SEEN; | ||
| 525 | store.seen++; | ||
| 526 | } else { | ||
| 527 | if (strrchr(key, '.') - key == store.baselen && | ||
| 528 | !strncmp(key, store.key, store.baselen)) { | ||
| 529 | store.state = SECTION_SEEN; | ||
| 530 | store.offset[store.seen] = ftell(config_file); | ||
| 531 | } | ||
| 532 | } | ||
| 533 | } | ||
| 534 | return 0; | ||
| 535 | } | ||
| 536 | |||
| 537 | static int store_write_section(int fd, const char* key) | ||
| 538 | { | ||
| 539 | const char *dot; | ||
| 540 | int i, success; | ||
| 541 | struct strbuf sb = STRBUF_INIT; | ||
| 542 | |||
| 543 | dot = memchr(key, '.', store.baselen); | ||
| 544 | if (dot) { | ||
| 545 | strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key); | ||
| 546 | for (i = dot - key + 1; i < store.baselen; i++) { | ||
| 547 | if (key[i] == '"' || key[i] == '\\') | ||
| 548 | strbuf_addch(&sb, '\\'); | ||
| 549 | strbuf_addch(&sb, key[i]); | ||
| 550 | } | ||
| 551 | strbuf_addstr(&sb, "\"]\n"); | ||
| 552 | } else { | ||
| 553 | strbuf_addf(&sb, "[%.*s]\n", store.baselen, key); | ||
| 554 | } | ||
| 555 | |||
| 556 | success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len); | ||
| 557 | strbuf_release(&sb); | ||
| 558 | |||
| 559 | return success; | ||
| 560 | } | ||
| 561 | |||
| 562 | static int store_write_pair(int fd, const char* key, const char* value) | ||
| 563 | { | ||
| 564 | int i, success; | ||
| 565 | int length = strlen(key + store.baselen + 1); | ||
| 566 | const char *quote = ""; | ||
| 567 | struct strbuf sb = STRBUF_INIT; | ||
| 568 | |||
| 569 | /* | ||
| 570 | * Check to see if the value needs to be surrounded with a dq pair. | ||
| 571 | * Note that problematic characters are always backslash-quoted; this | ||
| 572 | * check is about not losing leading or trailing SP and strings that | ||
| 573 | * follow beginning-of-comment characters (i.e. ';' and '#') by the | ||
| 574 | * configuration parser. | ||
| 575 | */ | ||
| 576 | if (value[0] == ' ') | ||
| 577 | quote = "\""; | ||
| 578 | for (i = 0; value[i]; i++) | ||
| 579 | if (value[i] == ';' || value[i] == '#') | ||
| 580 | quote = "\""; | ||
| 581 | if (i && value[i - 1] == ' ') | ||
| 582 | quote = "\""; | ||
| 583 | |||
| 584 | strbuf_addf(&sb, "\t%.*s = %s", | ||
| 585 | length, key + store.baselen + 1, quote); | ||
| 586 | |||
| 587 | for (i = 0; value[i]; i++) | ||
| 588 | switch (value[i]) { | ||
| 589 | case '\n': | ||
| 590 | strbuf_addstr(&sb, "\\n"); | ||
| 591 | break; | ||
| 592 | case '\t': | ||
| 593 | strbuf_addstr(&sb, "\\t"); | ||
| 594 | break; | ||
| 595 | case '"': | ||
| 596 | case '\\': | ||
| 597 | strbuf_addch(&sb, '\\'); | ||
| 598 | default: | ||
| 599 | strbuf_addch(&sb, value[i]); | ||
| 600 | break; | ||
| 601 | } | ||
| 602 | strbuf_addf(&sb, "%s\n", quote); | ||
| 603 | |||
| 604 | success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len); | ||
| 605 | strbuf_release(&sb); | ||
| 606 | |||
| 607 | return success; | ||
| 608 | } | ||
| 609 | |||
| 610 | static ssize_t find_beginning_of_line(const char* contents, size_t size, | ||
| 611 | size_t offset_, int* found_bracket) | ||
| 612 | { | ||
| 613 | size_t equal_offset = size, bracket_offset = size; | ||
| 614 | ssize_t offset; | ||
| 615 | |||
| 616 | contline: | ||
| 617 | for (offset = offset_-2; offset > 0 | ||
| 618 | && contents[offset] != '\n'; offset--) | ||
| 619 | switch (contents[offset]) { | ||
| 620 | case '=': equal_offset = offset; break; | ||
| 621 | case ']': bracket_offset = offset; break; | ||
| 622 | } | ||
| 623 | if (offset > 0 && contents[offset-1] == '\\') { | ||
| 624 | offset_ = offset; | ||
| 625 | goto contline; | ||
| 626 | } | ||
| 627 | if (bracket_offset < equal_offset) { | ||
| 628 | *found_bracket = 1; | ||
| 629 | offset = bracket_offset+1; | ||
| 630 | } else | ||
| 631 | offset++; | ||
| 632 | |||
| 633 | return offset; | ||
| 634 | } | ||
| 635 | |||
| 636 | int perf_config_set(const char* key, const char* value) | ||
| 637 | { | ||
| 638 | return perf_config_set_multivar(key, value, NULL, 0); | ||
| 639 | } | ||
| 640 | |||
| 641 | /* | ||
| 642 | * If value==NULL, unset in (remove from) config, | ||
| 643 | * if value_regex!=NULL, disregard key/value pairs where value does not match. | ||
| 644 | * if multi_replace==0, nothing, or only one matching key/value is replaced, | ||
| 645 | * else all matching key/values (regardless how many) are removed, | ||
| 646 | * before the new pair is written. | ||
| 647 | * | ||
| 648 | * Returns 0 on success. | ||
| 649 | * | ||
| 650 | * This function does this: | ||
| 651 | * | ||
| 652 | * - it locks the config file by creating ".perf/config.lock" | ||
| 653 | * | ||
| 654 | * - it then parses the config using store_aux() as validator to find | ||
| 655 | * the position on the key/value pair to replace. If it is to be unset, | ||
| 656 | * it must be found exactly once. | ||
| 657 | * | ||
| 658 | * - the config file is mmap()ed and the part before the match (if any) is | ||
| 659 | * written to the lock file, then the changed part and the rest. | ||
| 660 | * | ||
| 661 | * - the config file is removed and the lock file rename()d to it. | ||
| 662 | * | ||
| 663 | */ | ||
| 664 | int perf_config_set_multivar(const char* key, const char* value, | ||
| 665 | const char* value_regex, int multi_replace) | ||
| 666 | { | ||
| 667 | int i, dot; | ||
| 668 | int fd = -1, in_fd; | ||
| 669 | int ret = 0; | ||
| 670 | char* config_filename; | ||
| 671 | const char* last_dot = strrchr(key, '.'); | ||
| 672 | |||
| 673 | if (config_exclusive_filename) | ||
| 674 | config_filename = strdup(config_exclusive_filename); | ||
| 675 | else | ||
| 676 | config_filename = perf_pathdup("config"); | ||
| 677 | |||
| 678 | /* | ||
| 679 | * Since "key" actually contains the section name and the real | ||
| 680 | * key name separated by a dot, we have to know where the dot is. | ||
| 681 | */ | ||
| 682 | |||
| 683 | if (last_dot == NULL) { | ||
| 684 | error("key does not contain a section: %s", key); | ||
| 685 | ret = 2; | ||
| 686 | goto out_free; | ||
| 687 | } | ||
| 688 | store.baselen = last_dot - key; | ||
| 689 | |||
| 690 | store.multi_replace = multi_replace; | ||
| 691 | |||
| 692 | /* | ||
| 693 | * Validate the key and while at it, lower case it for matching. | ||
| 694 | */ | ||
| 695 | store.key = malloc(strlen(key) + 1); | ||
| 696 | dot = 0; | ||
| 697 | for (i = 0; key[i]; i++) { | ||
| 698 | unsigned char c = key[i]; | ||
| 699 | if (c == '.') | ||
| 700 | dot = 1; | ||
| 701 | /* Leave the extended basename untouched.. */ | ||
| 702 | if (!dot || i > store.baselen) { | ||
| 703 | if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) { | ||
| 704 | error("invalid key: %s", key); | ||
| 705 | free(store.key); | ||
| 706 | ret = 1; | ||
| 707 | goto out_free; | ||
| 708 | } | ||
| 709 | c = tolower(c); | ||
| 710 | } else if (c == '\n') { | ||
| 711 | error("invalid key (newline): %s", key); | ||
| 712 | free(store.key); | ||
| 713 | ret = 1; | ||
| 714 | goto out_free; | ||
| 715 | } | ||
| 716 | store.key[i] = c; | ||
| 717 | } | ||
| 718 | store.key[i] = 0; | ||
| 719 | |||
| 720 | /* | ||
| 721 | * If .perf/config does not exist yet, write a minimal version. | ||
| 722 | */ | ||
| 723 | in_fd = open(config_filename, O_RDONLY); | ||
| 724 | if ( in_fd < 0 ) { | ||
| 725 | free(store.key); | ||
| 726 | |||
| 727 | if ( ENOENT != errno ) { | ||
| 728 | error("opening %s: %s", config_filename, | ||
| 729 | strerror(errno)); | ||
| 730 | ret = 3; /* same as "invalid config file" */ | ||
| 731 | goto out_free; | ||
| 732 | } | ||
| 733 | /* if nothing to unset, error out */ | ||
| 734 | if (value == NULL) { | ||
| 735 | ret = 5; | ||
| 736 | goto out_free; | ||
| 737 | } | ||
| 738 | |||
| 739 | store.key = (char*)key; | ||
| 740 | if (!store_write_section(fd, key) || | ||
| 741 | !store_write_pair(fd, key, value)) | ||
| 742 | goto write_err_out; | ||
| 743 | } else { | ||
| 744 | struct stat st; | ||
| 745 | char* contents; | ||
| 746 | ssize_t contents_sz, copy_begin, copy_end; | ||
| 747 | int i, new_line = 0; | ||
| 748 | |||
| 749 | if (value_regex == NULL) | ||
| 750 | store.value_regex = NULL; | ||
| 751 | else { | ||
| 752 | if (value_regex[0] == '!') { | ||
| 753 | store.do_not_match = 1; | ||
| 754 | value_regex++; | ||
| 755 | } else | ||
| 756 | store.do_not_match = 0; | ||
| 757 | |||
| 758 | store.value_regex = (regex_t*)malloc(sizeof(regex_t)); | ||
| 759 | if (regcomp(store.value_regex, value_regex, | ||
| 760 | REG_EXTENDED)) { | ||
| 761 | error("invalid pattern: %s", value_regex); | ||
| 762 | free(store.value_regex); | ||
| 763 | ret = 6; | ||
| 764 | goto out_free; | ||
| 765 | } | ||
| 766 | } | ||
| 767 | |||
| 768 | store.offset[0] = 0; | ||
| 769 | store.state = START; | ||
| 770 | store.seen = 0; | ||
| 771 | |||
| 772 | /* | ||
| 773 | * After this, store.offset will contain the *end* offset | ||
| 774 | * of the last match, or remain at 0 if no match was found. | ||
| 775 | * As a side effect, we make sure to transform only a valid | ||
| 776 | * existing config file. | ||
| 777 | */ | ||
| 778 | if (perf_config_from_file(store_aux, config_filename, NULL)) { | ||
| 779 | error("invalid config file %s", config_filename); | ||
| 780 | free(store.key); | ||
| 781 | if (store.value_regex != NULL) { | ||
| 782 | regfree(store.value_regex); | ||
| 783 | free(store.value_regex); | ||
| 784 | } | ||
| 785 | ret = 3; | ||
| 786 | goto out_free; | ||
| 787 | } | ||
| 788 | |||
| 789 | free(store.key); | ||
| 790 | if (store.value_regex != NULL) { | ||
| 791 | regfree(store.value_regex); | ||
| 792 | free(store.value_regex); | ||
| 793 | } | ||
| 794 | |||
| 795 | /* if nothing to unset, or too many matches, error out */ | ||
| 796 | if ((store.seen == 0 && value == NULL) || | ||
| 797 | (store.seen > 1 && multi_replace == 0)) { | ||
| 798 | ret = 5; | ||
| 799 | goto out_free; | ||
| 800 | } | ||
| 801 | |||
| 802 | fstat(in_fd, &st); | ||
| 803 | contents_sz = xsize_t(st.st_size); | ||
| 804 | contents = mmap(NULL, contents_sz, PROT_READ, | ||
| 805 | MAP_PRIVATE, in_fd, 0); | ||
| 806 | close(in_fd); | ||
| 807 | |||
| 808 | if (store.seen == 0) | ||
| 809 | store.seen = 1; | ||
| 810 | |||
| 811 | for (i = 0, copy_begin = 0; i < store.seen; i++) { | ||
| 812 | if (store.offset[i] == 0) { | ||
| 813 | store.offset[i] = copy_end = contents_sz; | ||
| 814 | } else if (store.state != KEY_SEEN) { | ||
| 815 | copy_end = store.offset[i]; | ||
| 816 | } else | ||
| 817 | copy_end = find_beginning_of_line( | ||
| 818 | contents, contents_sz, | ||
| 819 | store.offset[i]-2, &new_line); | ||
| 820 | |||
| 821 | if (copy_end > 0 && contents[copy_end-1] != '\n') | ||
| 822 | new_line = 1; | ||
| 823 | |||
| 824 | /* write the first part of the config */ | ||
| 825 | if (copy_end > copy_begin) { | ||
| 826 | if (write_in_full(fd, contents + copy_begin, | ||
| 827 | copy_end - copy_begin) < | ||
| 828 | copy_end - copy_begin) | ||
| 829 | goto write_err_out; | ||
| 830 | if (new_line && | ||
| 831 | write_in_full(fd, "\n", 1) != 1) | ||
| 832 | goto write_err_out; | ||
| 833 | } | ||
| 834 | copy_begin = store.offset[i]; | ||
| 835 | } | ||
| 836 | |||
| 837 | /* write the pair (value == NULL means unset) */ | ||
| 838 | if (value != NULL) { | ||
| 839 | if (store.state == START) { | ||
| 840 | if (!store_write_section(fd, key)) | ||
| 841 | goto write_err_out; | ||
| 842 | } | ||
| 843 | if (!store_write_pair(fd, key, value)) | ||
| 844 | goto write_err_out; | ||
| 845 | } | ||
| 846 | |||
| 847 | /* write the rest of the config */ | ||
| 848 | if (copy_begin < contents_sz) | ||
| 849 | if (write_in_full(fd, contents + copy_begin, | ||
| 850 | contents_sz - copy_begin) < | ||
| 851 | contents_sz - copy_begin) | ||
| 852 | goto write_err_out; | ||
| 853 | |||
| 854 | munmap(contents, contents_sz); | ||
| 855 | } | ||
| 856 | |||
| 857 | ret = 0; | ||
| 858 | |||
| 859 | out_free: | ||
| 860 | free(config_filename); | ||
| 861 | return ret; | ||
| 862 | |||
| 863 | write_err_out: | ||
| 864 | goto out_free; | ||
| 865 | |||
| 866 | } | ||
| 867 | |||
| 868 | /* | ||
| 869 | * Call this to report error for your variable that should not | ||
| 870 | * get a boolean value (i.e. "[my] var" means "true"). | ||
| 871 | */ | ||
| 872 | int config_error_nonbool(const char *var) | ||
| 873 | { | ||
| 874 | return error("Missing value for '%s'", var); | ||
| 875 | } | ||
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c new file mode 100644 index 000000000000..0b791bd346bc --- /dev/null +++ b/tools/perf/util/ctype.c | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* | ||
| 2 | * Sane locale-independent, ASCII ctype. | ||
| 3 | * | ||
| 4 | * No surprises, and works with signed and unsigned chars. | ||
| 5 | */ | ||
| 6 | #include "cache.h" | ||
| 7 | |||
| 8 | enum { | ||
| 9 | S = GIT_SPACE, | ||
| 10 | A = GIT_ALPHA, | ||
| 11 | D = GIT_DIGIT, | ||
| 12 | G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */ | ||
| 13 | R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | * */ | ||
| 14 | P = GIT_PRINT_EXTRA, /* printable - alpha - digit - glob - regex */ | ||
| 15 | |||
| 16 | PS = GIT_SPACE | GIT_PRINT_EXTRA, | ||
| 17 | }; | ||
| 18 | |||
| 19 | unsigned char sane_ctype[256] = { | ||
| 20 | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ | ||
| 21 | |||
| 22 | 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */ | ||
| 23 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */ | ||
| 24 | PS,P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */ | ||
| 25 | D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */ | ||
| 26 | P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */ | ||
| 27 | A, A, A, A, A, A, A, A, A, A, A, G, G, P, R, P, /* 80.. 95 */ | ||
| 28 | P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */ | ||
| 29 | A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */ | ||
| 30 | /* Nothing in the 128.. range */ | ||
| 31 | }; | ||
diff --git a/tools/perf/util/environment.c b/tools/perf/util/environment.c new file mode 100644 index 000000000000..275b0ee345f5 --- /dev/null +++ b/tools/perf/util/environment.c | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | /* | ||
| 2 | * We put all the perf config variables in this same object | ||
| 3 | * file, so that programs can link against the config parser | ||
| 4 | * without having to link against all the rest of perf. | ||
| 5 | */ | ||
| 6 | #include "cache.h" | ||
| 7 | |||
| 8 | const char *pager_program; | ||
| 9 | int pager_use_color = 1; | ||
diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c new file mode 100644 index 000000000000..34a352867382 --- /dev/null +++ b/tools/perf/util/exec_cmd.c | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | #include "cache.h" | ||
| 2 | #include "exec_cmd.h" | ||
| 3 | #include "quote.h" | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | |||
| 7 | #define MAX_ARGS 32 | ||
| 8 | |||
| 9 | extern char **environ; | ||
| 10 | static const char *argv_exec_path; | ||
| 11 | static const char *argv0_path; | ||
| 12 | |||
| 13 | const char *system_path(const char *path) | ||
| 14 | { | ||
| 15 | #ifdef RUNTIME_PREFIX | ||
| 16 | static const char *prefix; | ||
| 17 | #else | ||
| 18 | static const char *prefix = PREFIX; | ||
| 19 | #endif | ||
| 20 | struct strbuf d = STRBUF_INIT; | ||
| 21 | |||
| 22 | if (is_absolute_path(path)) | ||
| 23 | return path; | ||
| 24 | |||
| 25 | #ifdef RUNTIME_PREFIX | ||
| 26 | assert(argv0_path); | ||
| 27 | assert(is_absolute_path(argv0_path)); | ||
| 28 | |||
| 29 | if (!prefix && | ||
| 30 | !(prefix = strip_path_suffix(argv0_path, PERF_EXEC_PATH)) && | ||
| 31 | !(prefix = strip_path_suffix(argv0_path, BINDIR)) && | ||
| 32 | !(prefix = strip_path_suffix(argv0_path, "perf"))) { | ||
| 33 | prefix = PREFIX; | ||
| 34 | fprintf(stderr, "RUNTIME_PREFIX requested, " | ||
| 35 | "but prefix computation failed. " | ||
| 36 | "Using static fallback '%s'.\n", prefix); | ||
| 37 | } | ||
| 38 | #endif | ||
| 39 | |||
| 40 | strbuf_addf(&d, "%s/%s", prefix, path); | ||
| 41 | path = strbuf_detach(&d, NULL); | ||
| 42 | return path; | ||
| 43 | } | ||
| 44 | |||
| 45 | const char *perf_extract_argv0_path(const char *argv0) | ||
| 46 | { | ||
| 47 | const char *slash; | ||
| 48 | |||
| 49 | if (!argv0 || !*argv0) | ||
| 50 | return NULL; | ||
| 51 | slash = argv0 + strlen(argv0); | ||
| 52 | |||
| 53 | while (argv0 <= slash && !is_dir_sep(*slash)) | ||
| 54 | slash--; | ||
| 55 | |||
| 56 | if (slash >= argv0) { | ||
| 57 | argv0_path = xstrndup(argv0, slash - argv0); | ||
| 58 | return slash + 1; | ||
| 59 | } | ||
| 60 | |||
| 61 | return argv0; | ||
| 62 | } | ||
| 63 | |||
| 64 | void perf_set_argv_exec_path(const char *exec_path) | ||
| 65 | { | ||
| 66 | argv_exec_path = exec_path; | ||
| 67 | /* | ||
| 68 | * Propagate this setting to external programs. | ||
| 69 | */ | ||
| 70 | setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1); | ||
| 71 | } | ||
| 72 | |||
| 73 | |||
| 74 | /* Returns the highest-priority, location to look for perf programs. */ | ||
| 75 | const char *perf_exec_path(void) | ||
| 76 | { | ||
| 77 | const char *env; | ||
| 78 | |||
| 79 | if (argv_exec_path) | ||
| 80 | return argv_exec_path; | ||
| 81 | |||
| 82 | env = getenv(EXEC_PATH_ENVIRONMENT); | ||
| 83 | if (env && *env) { | ||
| 84 | return env; | ||
| 85 | } | ||
| 86 | |||
| 87 | return system_path(PERF_EXEC_PATH); | ||
| 88 | } | ||
| 89 | |||
| 90 | static void add_path(struct strbuf *out, const char *path) | ||
| 91 | { | ||
| 92 | if (path && *path) { | ||
| 93 | if (is_absolute_path(path)) | ||
| 94 | strbuf_addstr(out, path); | ||
| 95 | else | ||
| 96 | strbuf_addstr(out, make_nonrelative_path(path)); | ||
| 97 | |||
| 98 | strbuf_addch(out, PATH_SEP); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | void setup_path(void) | ||
| 103 | { | ||
| 104 | const char *old_path = getenv("PATH"); | ||
| 105 | struct strbuf new_path = STRBUF_INIT; | ||
| 106 | |||
| 107 | add_path(&new_path, perf_exec_path()); | ||
| 108 | add_path(&new_path, argv0_path); | ||
| 109 | |||
| 110 | if (old_path) | ||
| 111 | strbuf_addstr(&new_path, old_path); | ||
| 112 | else | ||
| 113 | strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin"); | ||
| 114 | |||
| 115 | setenv("PATH", new_path.buf, 1); | ||
| 116 | |||
| 117 | strbuf_release(&new_path); | ||
| 118 | } | ||
| 119 | |||
| 120 | const char **prepare_perf_cmd(const char **argv) | ||
| 121 | { | ||
| 122 | int argc; | ||
| 123 | const char **nargv; | ||
| 124 | |||
| 125 | for (argc = 0; argv[argc]; argc++) | ||
| 126 | ; /* just counting */ | ||
| 127 | nargv = malloc(sizeof(*nargv) * (argc + 2)); | ||
| 128 | |||
| 129 | nargv[0] = "perf"; | ||
| 130 | for (argc = 0; argv[argc]; argc++) | ||
| 131 | nargv[argc + 1] = argv[argc]; | ||
| 132 | nargv[argc + 1] = NULL; | ||
| 133 | return nargv; | ||
| 134 | } | ||
| 135 | |||
| 136 | int execv_perf_cmd(const char **argv) { | ||
| 137 | const char **nargv = prepare_perf_cmd(argv); | ||
| 138 | |||
| 139 | /* execvp() can only ever return if it fails */ | ||
| 140 | execvp("perf", (char **)nargv); | ||
| 141 | |||
| 142 | free(nargv); | ||
| 143 | return -1; | ||
| 144 | } | ||
| 145 | |||
| 146 | |||
| 147 | int execl_perf_cmd(const char *cmd,...) | ||
| 148 | { | ||
| 149 | int argc; | ||
| 150 | const char *argv[MAX_ARGS + 1]; | ||
| 151 | const char *arg; | ||
| 152 | va_list param; | ||
| 153 | |||
| 154 | va_start(param, cmd); | ||
| 155 | argv[0] = cmd; | ||
| 156 | argc = 1; | ||
| 157 | while (argc < MAX_ARGS) { | ||
| 158 | arg = argv[argc++] = va_arg(param, char *); | ||
| 159 | if (!arg) | ||
| 160 | break; | ||
| 161 | } | ||
| 162 | va_end(param); | ||
| 163 | if (MAX_ARGS <= argc) | ||
| 164 | return error("too many args to run %s", cmd); | ||
| 165 | |||
| 166 | argv[argc] = NULL; | ||
| 167 | return execv_perf_cmd(argv); | ||
| 168 | } | ||
diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h new file mode 100644 index 000000000000..effe25eb1545 --- /dev/null +++ b/tools/perf/util/exec_cmd.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #ifndef PERF_EXEC_CMD_H | ||
| 2 | #define PERF_EXEC_CMD_H | ||
| 3 | |||
| 4 | extern void perf_set_argv_exec_path(const char *exec_path); | ||
| 5 | extern const char *perf_extract_argv0_path(const char *path); | ||
| 6 | extern const char *perf_exec_path(void); | ||
| 7 | extern void setup_path(void); | ||
| 8 | extern const char **prepare_perf_cmd(const char **argv); | ||
| 9 | extern int execv_perf_cmd(const char **argv); /* NULL terminated */ | ||
| 10 | extern int execl_perf_cmd(const char *cmd, ...); | ||
| 11 | extern const char *system_path(const char *path); | ||
| 12 | |||
| 13 | #endif /* PERF_EXEC_CMD_H */ | ||
diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh new file mode 100755 index 000000000000..f06f6fd148f8 --- /dev/null +++ b/tools/perf/util/generate-cmdlist.sh | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | echo "/* Automatically generated by $0 */ | ||
| 4 | struct cmdname_help | ||
| 5 | { | ||
| 6 | char name[16]; | ||
| 7 | char help[80]; | ||
| 8 | }; | ||
| 9 | |||
| 10 | static struct cmdname_help common_cmds[] = {" | ||
| 11 | |||
| 12 | sed -n -e 's/^perf-\([^ ]*\)[ ].* common.*/\1/p' command-list.txt | | ||
| 13 | sort | | ||
| 14 | while read cmd | ||
| 15 | do | ||
| 16 | sed -n ' | ||
| 17 | /^NAME/,/perf-'"$cmd"'/H | ||
| 18 | ${ | ||
| 19 | x | ||
| 20 | s/.*perf-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/ | ||
| 21 | p | ||
| 22 | }' "Documentation/perf-$cmd.txt" | ||
| 23 | done | ||
| 24 | echo "};" | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c new file mode 100644 index 000000000000..b92a457ca32e --- /dev/null +++ b/tools/perf/util/header.c | |||
| @@ -0,0 +1,245 @@ | |||
| 1 | #include <sys/types.h> | ||
| 2 | #include <unistd.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <stdlib.h> | ||
| 5 | |||
| 6 | #include "util.h" | ||
| 7 | #include "header.h" | ||
| 8 | |||
| 9 | /* | ||
| 10 | * | ||
| 11 | */ | ||
| 12 | |||
| 13 | struct perf_header_attr *perf_header_attr__new(struct perf_counter_attr *attr) | ||
| 14 | { | ||
| 15 | struct perf_header_attr *self = malloc(sizeof(*self)); | ||
| 16 | |||
| 17 | if (!self) | ||
| 18 | die("nomem"); | ||
| 19 | |||
| 20 | self->attr = *attr; | ||
| 21 | self->ids = 0; | ||
| 22 | self->size = 1; | ||
| 23 | self->id = malloc(sizeof(u64)); | ||
| 24 | |||
| 25 | if (!self->id) | ||
| 26 | die("nomem"); | ||
| 27 | |||
| 28 | return self; | ||
| 29 | } | ||
| 30 | |||
| 31 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | ||
| 32 | { | ||
| 33 | int pos = self->ids; | ||
| 34 | |||
| 35 | self->ids++; | ||
| 36 | if (self->ids > self->size) { | ||
| 37 | self->size *= 2; | ||
| 38 | self->id = realloc(self->id, self->size * sizeof(u64)); | ||
| 39 | if (!self->id) | ||
| 40 | die("nomem"); | ||
| 41 | } | ||
| 42 | self->id[pos] = id; | ||
| 43 | } | ||
| 44 | |||
| 45 | /* | ||
| 46 | * | ||
| 47 | */ | ||
| 48 | |||
| 49 | struct perf_header *perf_header__new(void) | ||
| 50 | { | ||
| 51 | struct perf_header *self = malloc(sizeof(*self)); | ||
| 52 | |||
| 53 | if (!self) | ||
| 54 | die("nomem"); | ||
| 55 | |||
| 56 | self->frozen = 0; | ||
| 57 | |||
| 58 | self->attrs = 0; | ||
| 59 | self->size = 1; | ||
| 60 | self->attr = malloc(sizeof(void *)); | ||
| 61 | |||
| 62 | if (!self->attr) | ||
| 63 | die("nomem"); | ||
| 64 | |||
| 65 | self->data_offset = 0; | ||
| 66 | self->data_size = 0; | ||
| 67 | |||
| 68 | return self; | ||
| 69 | } | ||
| 70 | |||
| 71 | void perf_header__add_attr(struct perf_header *self, | ||
| 72 | struct perf_header_attr *attr) | ||
| 73 | { | ||
| 74 | int pos = self->attrs; | ||
| 75 | |||
| 76 | if (self->frozen) | ||
| 77 | die("frozen"); | ||
| 78 | |||
| 79 | self->attrs++; | ||
| 80 | if (self->attrs > self->size) { | ||
| 81 | self->size *= 2; | ||
| 82 | self->attr = realloc(self->attr, self->size * sizeof(void *)); | ||
| 83 | if (!self->attr) | ||
| 84 | die("nomem"); | ||
| 85 | } | ||
| 86 | self->attr[pos] = attr; | ||
| 87 | } | ||
| 88 | |||
| 89 | static const char *__perf_magic = "PERFFILE"; | ||
| 90 | |||
| 91 | #define PERF_MAGIC (*(u64 *)__perf_magic) | ||
| 92 | |||
| 93 | struct perf_file_section { | ||
| 94 | u64 offset; | ||
| 95 | u64 size; | ||
| 96 | }; | ||
| 97 | |||
| 98 | struct perf_file_attr { | ||
| 99 | struct perf_counter_attr attr; | ||
| 100 | struct perf_file_section ids; | ||
| 101 | }; | ||
| 102 | |||
| 103 | struct perf_file_header { | ||
| 104 | u64 magic; | ||
| 105 | u64 size; | ||
| 106 | u64 attr_size; | ||
| 107 | struct perf_file_section attrs; | ||
| 108 | struct perf_file_section data; | ||
| 109 | }; | ||
| 110 | |||
| 111 | static void do_write(int fd, void *buf, size_t size) | ||
| 112 | { | ||
| 113 | while (size) { | ||
| 114 | int ret = write(fd, buf, size); | ||
| 115 | |||
| 116 | if (ret < 0) | ||
| 117 | die("failed to write"); | ||
| 118 | |||
| 119 | size -= ret; | ||
| 120 | buf += ret; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | void perf_header__write(struct perf_header *self, int fd) | ||
| 125 | { | ||
| 126 | struct perf_file_header f_header; | ||
| 127 | struct perf_file_attr f_attr; | ||
| 128 | struct perf_header_attr *attr; | ||
| 129 | int i; | ||
| 130 | |||
| 131 | lseek(fd, sizeof(f_header), SEEK_SET); | ||
| 132 | |||
| 133 | |||
| 134 | for (i = 0; i < self->attrs; i++) { | ||
| 135 | attr = self->attr[i]; | ||
| 136 | |||
| 137 | attr->id_offset = lseek(fd, 0, SEEK_CUR); | ||
| 138 | do_write(fd, attr->id, attr->ids * sizeof(u64)); | ||
| 139 | } | ||
| 140 | |||
| 141 | |||
| 142 | self->attr_offset = lseek(fd, 0, SEEK_CUR); | ||
| 143 | |||
| 144 | for (i = 0; i < self->attrs; i++) { | ||
| 145 | attr = self->attr[i]; | ||
| 146 | |||
| 147 | f_attr = (struct perf_file_attr){ | ||
| 148 | .attr = attr->attr, | ||
| 149 | .ids = { | ||
| 150 | .offset = attr->id_offset, | ||
| 151 | .size = attr->ids * sizeof(u64), | ||
| 152 | } | ||
| 153 | }; | ||
| 154 | do_write(fd, &f_attr, sizeof(f_attr)); | ||
| 155 | } | ||
| 156 | |||
| 157 | |||
| 158 | self->data_offset = lseek(fd, 0, SEEK_CUR); | ||
| 159 | |||
| 160 | f_header = (struct perf_file_header){ | ||
| 161 | .magic = PERF_MAGIC, | ||
| 162 | .size = sizeof(f_header), | ||
| 163 | .attr_size = sizeof(f_attr), | ||
| 164 | .attrs = { | ||
| 165 | .offset = self->attr_offset, | ||
| 166 | .size = self->attrs * sizeof(f_attr), | ||
| 167 | }, | ||
| 168 | .data = { | ||
| 169 | .offset = self->data_offset, | ||
| 170 | .size = self->data_size, | ||
| 171 | }, | ||
| 172 | }; | ||
| 173 | |||
| 174 | lseek(fd, 0, SEEK_SET); | ||
| 175 | do_write(fd, &f_header, sizeof(f_header)); | ||
| 176 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | ||
| 177 | |||
| 178 | self->frozen = 1; | ||
| 179 | } | ||
| 180 | |||
| 181 | static void do_read(int fd, void *buf, size_t size) | ||
| 182 | { | ||
| 183 | while (size) { | ||
| 184 | int ret = read(fd, buf, size); | ||
| 185 | |||
| 186 | if (ret < 0) | ||
| 187 | die("failed to read"); | ||
| 188 | if (ret == 0) | ||
| 189 | die("failed to read: missing data"); | ||
| 190 | |||
| 191 | size -= ret; | ||
| 192 | buf += ret; | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | struct perf_header *perf_header__read(int fd) | ||
| 197 | { | ||
| 198 | struct perf_header *self = perf_header__new(); | ||
| 199 | struct perf_file_header f_header; | ||
| 200 | struct perf_file_attr f_attr; | ||
| 201 | u64 f_id; | ||
| 202 | |||
| 203 | int nr_attrs, nr_ids, i, j; | ||
| 204 | |||
| 205 | lseek(fd, 0, SEEK_SET); | ||
| 206 | do_read(fd, &f_header, sizeof(f_header)); | ||
| 207 | |||
| 208 | if (f_header.magic != PERF_MAGIC || | ||
| 209 | f_header.size != sizeof(f_header) || | ||
| 210 | f_header.attr_size != sizeof(f_attr)) | ||
| 211 | die("incompatible file format"); | ||
| 212 | |||
| 213 | nr_attrs = f_header.attrs.size / sizeof(f_attr); | ||
| 214 | lseek(fd, f_header.attrs.offset, SEEK_SET); | ||
| 215 | |||
| 216 | for (i = 0; i < nr_attrs; i++) { | ||
| 217 | struct perf_header_attr *attr; | ||
| 218 | off_t tmp; | ||
| 219 | |||
| 220 | do_read(fd, &f_attr, sizeof(f_attr)); | ||
| 221 | tmp = lseek(fd, 0, SEEK_CUR); | ||
| 222 | |||
| 223 | attr = perf_header_attr__new(&f_attr.attr); | ||
| 224 | |||
| 225 | nr_ids = f_attr.ids.size / sizeof(u64); | ||
| 226 | lseek(fd, f_attr.ids.offset, SEEK_SET); | ||
| 227 | |||
| 228 | for (j = 0; j < nr_ids; j++) { | ||
| 229 | do_read(fd, &f_id, sizeof(f_id)); | ||
| 230 | |||
| 231 | perf_header_attr__add_id(attr, f_id); | ||
| 232 | } | ||
| 233 | perf_header__add_attr(self, attr); | ||
| 234 | lseek(fd, tmp, SEEK_SET); | ||
| 235 | } | ||
| 236 | |||
| 237 | self->data_offset = f_header.data.offset; | ||
| 238 | self->data_size = f_header.data.size; | ||
| 239 | |||
| 240 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | ||
| 241 | |||
| 242 | self->frozen = 1; | ||
| 243 | |||
| 244 | return self; | ||
| 245 | } | ||
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h new file mode 100644 index 000000000000..bf280449fcfd --- /dev/null +++ b/tools/perf/util/header.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #ifndef _PERF_HEADER_H | ||
| 2 | #define _PERF_HEADER_H | ||
| 3 | |||
| 4 | #include "../../../include/linux/perf_counter.h" | ||
| 5 | #include <sys/types.h> | ||
| 6 | #include "types.h" | ||
| 7 | |||
| 8 | struct perf_header_attr { | ||
| 9 | struct perf_counter_attr attr; | ||
| 10 | int ids, size; | ||
| 11 | u64 *id; | ||
| 12 | off_t id_offset; | ||
| 13 | }; | ||
| 14 | |||
| 15 | struct perf_header { | ||
| 16 | int frozen; | ||
| 17 | int attrs, size; | ||
| 18 | struct perf_header_attr **attr; | ||
| 19 | s64 attr_offset; | ||
| 20 | u64 data_offset; | ||
| 21 | u64 data_size; | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct perf_header *perf_header__read(int fd); | ||
| 25 | void perf_header__write(struct perf_header *self, int fd); | ||
| 26 | |||
| 27 | void perf_header__add_attr(struct perf_header *self, | ||
| 28 | struct perf_header_attr *attr); | ||
| 29 | |||
| 30 | struct perf_header_attr * | ||
| 31 | perf_header_attr__new(struct perf_counter_attr *attr); | ||
| 32 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id); | ||
| 33 | |||
| 34 | |||
| 35 | struct perf_header *perf_header__new(void); | ||
| 36 | |||
| 37 | #endif /* _PERF_HEADER_H */ | ||
diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c new file mode 100644 index 000000000000..fbb00978b2e2 --- /dev/null +++ b/tools/perf/util/help.c | |||
| @@ -0,0 +1,356 @@ | |||
| 1 | #include "cache.h" | ||
| 2 | #include "../builtin.h" | ||
| 3 | #include "exec_cmd.h" | ||
| 4 | #include "levenshtein.h" | ||
| 5 | #include "help.h" | ||
| 6 | |||
| 7 | /* most GUI terminals set COLUMNS (although some don't export it) */ | ||
| 8 | static int term_columns(void) | ||
| 9 | { | ||
| 10 | char *col_string = getenv("COLUMNS"); | ||
| 11 | int n_cols; | ||
| 12 | |||
| 13 | if (col_string && (n_cols = atoi(col_string)) > 0) | ||
| 14 | return n_cols; | ||
| 15 | |||
| 16 | #ifdef TIOCGWINSZ | ||
| 17 | { | ||
| 18 | struct winsize ws; | ||
| 19 | if (!ioctl(1, TIOCGWINSZ, &ws)) { | ||
| 20 | if (ws.ws_col) | ||
| 21 | return ws.ws_col; | ||
| 22 | } | ||
| 23 | } | ||
| 24 | #endif | ||
| 25 | |||
| 26 | return 80; | ||
| 27 | } | ||
| 28 | |||
| 29 | void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) | ||
| 30 | { | ||
| 31 | struct cmdname *ent = malloc(sizeof(*ent) + len + 1); | ||
| 32 | |||
| 33 | ent->len = len; | ||
| 34 | memcpy(ent->name, name, len); | ||
| 35 | ent->name[len] = 0; | ||
| 36 | |||
| 37 | ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc); | ||
| 38 | cmds->names[cmds->cnt++] = ent; | ||
| 39 | } | ||
| 40 | |||
| 41 | static void clean_cmdnames(struct cmdnames *cmds) | ||
| 42 | { | ||
| 43 | unsigned int i; | ||
| 44 | |||
| 45 | for (i = 0; i < cmds->cnt; ++i) | ||
| 46 | free(cmds->names[i]); | ||
| 47 | free(cmds->names); | ||
| 48 | cmds->cnt = 0; | ||
| 49 | cmds->alloc = 0; | ||
| 50 | } | ||
| 51 | |||
| 52 | static int cmdname_compare(const void *a_, const void *b_) | ||
| 53 | { | ||
| 54 | struct cmdname *a = *(struct cmdname **)a_; | ||
| 55 | struct cmdname *b = *(struct cmdname **)b_; | ||
| 56 | return strcmp(a->name, b->name); | ||
| 57 | } | ||
| 58 | |||
| 59 | static void uniq(struct cmdnames *cmds) | ||
| 60 | { | ||
| 61 | unsigned int i, j; | ||
| 62 | |||
| 63 | if (!cmds->cnt) | ||
| 64 | return; | ||
| 65 | |||
| 66 | for (i = j = 1; i < cmds->cnt; i++) | ||
| 67 | if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name)) | ||
| 68 | cmds->names[j++] = cmds->names[i]; | ||
| 69 | |||
| 70 | cmds->cnt = j; | ||
| 71 | } | ||
| 72 | |||
| 73 | void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) | ||
| 74 | { | ||
| 75 | size_t ci, cj, ei; | ||
| 76 | int cmp; | ||
| 77 | |||
| 78 | ci = cj = ei = 0; | ||
| 79 | while (ci < cmds->cnt && ei < excludes->cnt) { | ||
| 80 | cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name); | ||
| 81 | if (cmp < 0) | ||
| 82 | cmds->names[cj++] = cmds->names[ci++]; | ||
| 83 | else if (cmp == 0) | ||
| 84 | ci++, ei++; | ||
| 85 | else if (cmp > 0) | ||
| 86 | ei++; | ||
| 87 | } | ||
| 88 | |||
| 89 | while (ci < cmds->cnt) | ||
| 90 | cmds->names[cj++] = cmds->names[ci++]; | ||
| 91 | |||
| 92 | cmds->cnt = cj; | ||
| 93 | } | ||
| 94 | |||
| 95 | static void pretty_print_string_list(struct cmdnames *cmds, int longest) | ||
| 96 | { | ||
| 97 | int cols = 1, rows; | ||
| 98 | int space = longest + 1; /* min 1 SP between words */ | ||
| 99 | int max_cols = term_columns() - 1; /* don't print *on* the edge */ | ||
| 100 | int i, j; | ||
| 101 | |||
| 102 | if (space < max_cols) | ||
| 103 | cols = max_cols / space; | ||
| 104 | rows = (cmds->cnt + cols - 1) / cols; | ||
| 105 | |||
| 106 | for (i = 0; i < rows; i++) { | ||
| 107 | printf(" "); | ||
| 108 | |||
| 109 | for (j = 0; j < cols; j++) { | ||
| 110 | unsigned int n = j * rows + i; | ||
| 111 | unsigned int size = space; | ||
| 112 | |||
| 113 | if (n >= cmds->cnt) | ||
| 114 | break; | ||
| 115 | if (j == cols-1 || n + rows >= cmds->cnt) | ||
| 116 | size = 1; | ||
| 117 | printf("%-*s", size, cmds->names[n]->name); | ||
| 118 | } | ||
| 119 | putchar('\n'); | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | static int is_executable(const char *name) | ||
| 124 | { | ||
| 125 | struct stat st; | ||
| 126 | |||
| 127 | if (stat(name, &st) || /* stat, not lstat */ | ||
| 128 | !S_ISREG(st.st_mode)) | ||
| 129 | return 0; | ||
| 130 | |||
| 131 | return st.st_mode & S_IXUSR; | ||
| 132 | } | ||
| 133 | |||
| 134 | static void list_commands_in_dir(struct cmdnames *cmds, | ||
| 135 | const char *path, | ||
| 136 | const char *prefix) | ||
| 137 | { | ||
| 138 | int prefix_len; | ||
| 139 | DIR *dir = opendir(path); | ||
| 140 | struct dirent *de; | ||
| 141 | struct strbuf buf = STRBUF_INIT; | ||
| 142 | int len; | ||
| 143 | |||
| 144 | if (!dir) | ||
| 145 | return; | ||
| 146 | if (!prefix) | ||
| 147 | prefix = "perf-"; | ||
| 148 | prefix_len = strlen(prefix); | ||
| 149 | |||
| 150 | strbuf_addf(&buf, "%s/", path); | ||
| 151 | len = buf.len; | ||
| 152 | |||
| 153 | while ((de = readdir(dir)) != NULL) { | ||
| 154 | int entlen; | ||
| 155 | |||
| 156 | if (prefixcmp(de->d_name, prefix)) | ||
| 157 | continue; | ||
| 158 | |||
| 159 | strbuf_setlen(&buf, len); | ||
| 160 | strbuf_addstr(&buf, de->d_name); | ||
| 161 | if (!is_executable(buf.buf)) | ||
| 162 | continue; | ||
| 163 | |||
| 164 | entlen = strlen(de->d_name) - prefix_len; | ||
| 165 | if (has_extension(de->d_name, ".exe")) | ||
| 166 | entlen -= 4; | ||
| 167 | |||
| 168 | add_cmdname(cmds, de->d_name + prefix_len, entlen); | ||
| 169 | } | ||
| 170 | closedir(dir); | ||
| 171 | strbuf_release(&buf); | ||
| 172 | } | ||
| 173 | |||
| 174 | void load_command_list(const char *prefix, | ||
| 175 | struct cmdnames *main_cmds, | ||
| 176 | struct cmdnames *other_cmds) | ||
| 177 | { | ||
| 178 | const char *env_path = getenv("PATH"); | ||
| 179 | const char *exec_path = perf_exec_path(); | ||
| 180 | |||
| 181 | if (exec_path) { | ||
| 182 | list_commands_in_dir(main_cmds, exec_path, prefix); | ||
| 183 | qsort(main_cmds->names, main_cmds->cnt, | ||
| 184 | sizeof(*main_cmds->names), cmdname_compare); | ||
| 185 | uniq(main_cmds); | ||
| 186 | } | ||
| 187 | |||
| 188 | if (env_path) { | ||
| 189 | char *paths, *path, *colon; | ||
| 190 | path = paths = strdup(env_path); | ||
| 191 | while (1) { | ||
| 192 | if ((colon = strchr(path, PATH_SEP))) | ||
| 193 | *colon = 0; | ||
| 194 | if (!exec_path || strcmp(path, exec_path)) | ||
| 195 | list_commands_in_dir(other_cmds, path, prefix); | ||
| 196 | |||
| 197 | if (!colon) | ||
| 198 | break; | ||
| 199 | path = colon + 1; | ||
| 200 | } | ||
| 201 | free(paths); | ||
| 202 | |||
| 203 | qsort(other_cmds->names, other_cmds->cnt, | ||
| 204 | sizeof(*other_cmds->names), cmdname_compare); | ||
| 205 | uniq(other_cmds); | ||
| 206 | } | ||
| 207 | exclude_cmds(other_cmds, main_cmds); | ||
| 208 | } | ||
| 209 | |||
| 210 | void list_commands(const char *title, struct cmdnames *main_cmds, | ||
| 211 | struct cmdnames *other_cmds) | ||
| 212 | { | ||
| 213 | unsigned int i, longest = 0; | ||
| 214 | |||
| 215 | for (i = 0; i < main_cmds->cnt; i++) | ||
| 216 | if (longest < main_cmds->names[i]->len) | ||
| 217 | longest = main_cmds->names[i]->len; | ||
| 218 | for (i = 0; i < other_cmds->cnt; i++) | ||
| 219 | if (longest < other_cmds->names[i]->len) | ||
| 220 | longest = other_cmds->names[i]->len; | ||
| 221 | |||
| 222 | if (main_cmds->cnt) { | ||
| 223 | const char *exec_path = perf_exec_path(); | ||
| 224 | printf("available %s in '%s'\n", title, exec_path); | ||
| 225 | printf("----------------"); | ||
| 226 | mput_char('-', strlen(title) + strlen(exec_path)); | ||
| 227 | putchar('\n'); | ||
| 228 | pretty_print_string_list(main_cmds, longest); | ||
| 229 | putchar('\n'); | ||
| 230 | } | ||
| 231 | |||
| 232 | if (other_cmds->cnt) { | ||
| 233 | printf("%s available from elsewhere on your $PATH\n", title); | ||
| 234 | printf("---------------------------------------"); | ||
| 235 | mput_char('-', strlen(title)); | ||
| 236 | putchar('\n'); | ||
| 237 | pretty_print_string_list(other_cmds, longest); | ||
| 238 | putchar('\n'); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | int is_in_cmdlist(struct cmdnames *c, const char *s) | ||
| 243 | { | ||
| 244 | unsigned int i; | ||
| 245 | |||
| 246 | for (i = 0; i < c->cnt; i++) | ||
| 247 | if (!strcmp(s, c->names[i]->name)) | ||
| 248 | return 1; | ||
| 249 | return 0; | ||
| 250 | } | ||
| 251 | |||
| 252 | static int autocorrect; | ||
| 253 | static struct cmdnames aliases; | ||
| 254 | |||
| 255 | static int perf_unknown_cmd_config(const char *var, const char *value, void *cb) | ||
| 256 | { | ||
| 257 | if (!strcmp(var, "help.autocorrect")) | ||
| 258 | autocorrect = perf_config_int(var,value); | ||
| 259 | /* Also use aliases for command lookup */ | ||
| 260 | if (!prefixcmp(var, "alias.")) | ||
| 261 | add_cmdname(&aliases, var + 6, strlen(var + 6)); | ||
| 262 | |||
| 263 | return perf_default_config(var, value, cb); | ||
| 264 | } | ||
| 265 | |||
| 266 | static int levenshtein_compare(const void *p1, const void *p2) | ||
| 267 | { | ||
| 268 | const struct cmdname *const *c1 = p1, *const *c2 = p2; | ||
| 269 | const char *s1 = (*c1)->name, *s2 = (*c2)->name; | ||
| 270 | int l1 = (*c1)->len; | ||
| 271 | int l2 = (*c2)->len; | ||
| 272 | return l1 != l2 ? l1 - l2 : strcmp(s1, s2); | ||
| 273 | } | ||
| 274 | |||
| 275 | static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old) | ||
| 276 | { | ||
| 277 | unsigned int i; | ||
| 278 | |||
| 279 | ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc); | ||
| 280 | |||
| 281 | for (i = 0; i < old->cnt; i++) | ||
| 282 | cmds->names[cmds->cnt++] = old->names[i]; | ||
| 283 | free(old->names); | ||
| 284 | old->cnt = 0; | ||
| 285 | old->names = NULL; | ||
| 286 | } | ||
| 287 | |||
| 288 | const char *help_unknown_cmd(const char *cmd) | ||
| 289 | { | ||
| 290 | unsigned int i, n = 0, best_similarity = 0; | ||
| 291 | struct cmdnames main_cmds, other_cmds; | ||
| 292 | |||
| 293 | memset(&main_cmds, 0, sizeof(main_cmds)); | ||
| 294 | memset(&other_cmds, 0, sizeof(main_cmds)); | ||
| 295 | memset(&aliases, 0, sizeof(aliases)); | ||
| 296 | |||
| 297 | perf_config(perf_unknown_cmd_config, NULL); | ||
| 298 | |||
| 299 | load_command_list("perf-", &main_cmds, &other_cmds); | ||
| 300 | |||
| 301 | add_cmd_list(&main_cmds, &aliases); | ||
| 302 | add_cmd_list(&main_cmds, &other_cmds); | ||
| 303 | qsort(main_cmds.names, main_cmds.cnt, | ||
| 304 | sizeof(main_cmds.names), cmdname_compare); | ||
| 305 | uniq(&main_cmds); | ||
| 306 | |||
| 307 | if (main_cmds.cnt) { | ||
| 308 | /* This reuses cmdname->len for similarity index */ | ||
| 309 | for (i = 0; i < main_cmds.cnt; ++i) | ||
| 310 | main_cmds.names[i]->len = | ||
| 311 | levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4); | ||
| 312 | |||
| 313 | qsort(main_cmds.names, main_cmds.cnt, | ||
| 314 | sizeof(*main_cmds.names), levenshtein_compare); | ||
| 315 | |||
| 316 | best_similarity = main_cmds.names[0]->len; | ||
| 317 | n = 1; | ||
| 318 | while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len) | ||
| 319 | ++n; | ||
| 320 | } | ||
| 321 | |||
| 322 | if (autocorrect && n == 1) { | ||
| 323 | const char *assumed = main_cmds.names[0]->name; | ||
| 324 | |||
| 325 | main_cmds.names[0] = NULL; | ||
| 326 | clean_cmdnames(&main_cmds); | ||
| 327 | fprintf(stderr, "WARNING: You called a Git program named '%s', " | ||
| 328 | "which does not exist.\n" | ||
| 329 | "Continuing under the assumption that you meant '%s'\n", | ||
| 330 | cmd, assumed); | ||
| 331 | if (autocorrect > 0) { | ||
| 332 | fprintf(stderr, "in %0.1f seconds automatically...\n", | ||
| 333 | (float)autocorrect/10.0); | ||
| 334 | poll(NULL, 0, autocorrect * 100); | ||
| 335 | } | ||
| 336 | return assumed; | ||
| 337 | } | ||
| 338 | |||
| 339 | fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd); | ||
| 340 | |||
| 341 | if (main_cmds.cnt && best_similarity < 6) { | ||
| 342 | fprintf(stderr, "\nDid you mean %s?\n", | ||
| 343 | n < 2 ? "this": "one of these"); | ||
| 344 | |||
| 345 | for (i = 0; i < n; i++) | ||
| 346 | fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); | ||
| 347 | } | ||
| 348 | |||
| 349 | exit(1); | ||
| 350 | } | ||
| 351 | |||
| 352 | int cmd_version(int argc __used, const char **argv __used, const char *prefix __used) | ||
| 353 | { | ||
| 354 | printf("perf version %s\n", perf_version_string); | ||
| 355 | return 0; | ||
| 356 | } | ||
diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h new file mode 100644 index 000000000000..7128783637b4 --- /dev/null +++ b/tools/perf/util/help.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #ifndef HELP_H | ||
| 2 | #define HELP_H | ||
| 3 | |||
| 4 | struct cmdnames { | ||
| 5 | size_t alloc; | ||
| 6 | size_t cnt; | ||
| 7 | struct cmdname { | ||
| 8 | size_t len; /* also used for similarity index in help.c */ | ||
| 9 | char name[FLEX_ARRAY]; | ||
| 10 | } **names; | ||
| 11 | }; | ||
| 12 | |||
| 13 | static inline void mput_char(char c, unsigned int num) | ||
| 14 | { | ||
| 15 | while(num--) | ||
| 16 | putchar(c); | ||
| 17 | } | ||
| 18 | |||
| 19 | void load_command_list(const char *prefix, | ||
| 20 | struct cmdnames *main_cmds, | ||
| 21 | struct cmdnames *other_cmds); | ||
| 22 | void add_cmdname(struct cmdnames *cmds, const char *name, size_t len); | ||
| 23 | /* Here we require that excludes is a sorted list. */ | ||
| 24 | void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); | ||
| 25 | int is_in_cmdlist(struct cmdnames *c, const char *s); | ||
| 26 | void list_commands(const char *title, struct cmdnames *main_cmds, | ||
| 27 | struct cmdnames *other_cmds); | ||
| 28 | |||
| 29 | #endif /* HELP_H */ | ||
diff --git a/tools/perf/util/include/asm/system.h b/tools/perf/util/include/asm/system.h new file mode 100644 index 000000000000..710cecca972d --- /dev/null +++ b/tools/perf/util/include/asm/system.h | |||
| @@ -0,0 +1 @@ | |||
| /* Empty */ | |||
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h new file mode 100644 index 000000000000..a6b87390cb52 --- /dev/null +++ b/tools/perf/util/include/linux/kernel.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #ifndef PERF_LINUX_KERNEL_H_ | ||
| 2 | #define PERF_LINUX_KERNEL_H_ | ||
| 3 | |||
| 4 | #ifndef offsetof | ||
| 5 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) | ||
| 6 | #endif | ||
| 7 | |||
| 8 | #ifndef container_of | ||
| 9 | /** | ||
| 10 | * container_of - cast a member of a structure out to the containing structure | ||
| 11 | * @ptr: the pointer to the member. | ||
| 12 | * @type: the type of the container struct this is embedded in. | ||
| 13 | * @member: the name of the member within the struct. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | #define container_of(ptr, type, member) ({ \ | ||
| 17 | const typeof(((type *)0)->member) * __mptr = (ptr); \ | ||
| 18 | (type *)((char *)__mptr - offsetof(type, member)); }) | ||
| 19 | #endif | ||
| 20 | |||
| 21 | #ifndef max | ||
| 22 | #define max(x, y) ({ \ | ||
| 23 | typeof(x) _max1 = (x); \ | ||
| 24 | typeof(y) _max2 = (y); \ | ||
| 25 | (void) (&_max1 == &_max2); \ | ||
| 26 | _max1 > _max2 ? _max1 : _max2; }) | ||
| 27 | #endif | ||
| 28 | |||
| 29 | #endif | ||
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h new file mode 100644 index 000000000000..dbe4b814382a --- /dev/null +++ b/tools/perf/util/include/linux/list.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #include "../../../../include/linux/list.h" | ||
| 2 | |||
| 3 | #ifndef PERF_LIST_H | ||
| 4 | #define PERF_LIST_H | ||
| 5 | /** | ||
| 6 | * list_del_range - deletes range of entries from list. | ||
| 7 | * @begin: first element in the range to delete from the list. | ||
| 8 | * @end: last element in the range to delete from the list. | ||
| 9 | * Note: list_empty on the range of entries does not return true after this, | ||
| 10 | * the entries is in an undefined state. | ||
| 11 | */ | ||
| 12 | static inline void list_del_range(struct list_head *begin, | ||
| 13 | struct list_head *end) | ||
| 14 | { | ||
| 15 | begin->prev->next = end->next; | ||
| 16 | end->next->prev = begin->prev; | ||
| 17 | } | ||
| 18 | #endif | ||
diff --git a/tools/perf/util/include/linux/module.h b/tools/perf/util/include/linux/module.h new file mode 100644 index 000000000000..b43e2dc21e04 --- /dev/null +++ b/tools/perf/util/include/linux/module.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef PERF_LINUX_MODULE_H | ||
| 2 | #define PERF_LINUX_MODULE_H | ||
| 3 | |||
| 4 | #define EXPORT_SYMBOL(name) | ||
| 5 | |||
| 6 | #endif | ||
diff --git a/tools/perf/util/include/linux/poison.h b/tools/perf/util/include/linux/poison.h new file mode 100644 index 000000000000..fef6dbc9ce13 --- /dev/null +++ b/tools/perf/util/include/linux/poison.h | |||
| @@ -0,0 +1 @@ | |||
| #include "../../../../include/linux/poison.h" | |||
diff --git a/tools/perf/util/include/linux/prefetch.h b/tools/perf/util/include/linux/prefetch.h new file mode 100644 index 000000000000..7841e485d8c3 --- /dev/null +++ b/tools/perf/util/include/linux/prefetch.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef PERF_LINUX_PREFETCH_H | ||
| 2 | #define PERF_LINUX_PREFETCH_H | ||
| 3 | |||
| 4 | static inline void prefetch(void *a __attribute__((unused))) { } | ||
| 5 | |||
| 6 | #endif | ||
diff --git a/tools/perf/util/include/linux/rbtree.h b/tools/perf/util/include/linux/rbtree.h new file mode 100644 index 000000000000..7a243a143037 --- /dev/null +++ b/tools/perf/util/include/linux/rbtree.h | |||
| @@ -0,0 +1 @@ | |||
| #include "../../../../include/linux/rbtree.h" | |||
diff --git a/tools/perf/util/levenshtein.c b/tools/perf/util/levenshtein.c new file mode 100644 index 000000000000..e521d1516df6 --- /dev/null +++ b/tools/perf/util/levenshtein.c | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | #include "cache.h" | ||
| 2 | #include "levenshtein.h" | ||
| 3 | |||
| 4 | /* | ||
| 5 | * This function implements the Damerau-Levenshtein algorithm to | ||
| 6 | * calculate a distance between strings. | ||
| 7 | * | ||
| 8 | * Basically, it says how many letters need to be swapped, substituted, | ||
| 9 | * deleted from, or added to string1, at least, to get string2. | ||
| 10 | * | ||
| 11 | * The idea is to build a distance matrix for the substrings of both | ||
| 12 | * strings. To avoid a large space complexity, only the last three rows | ||
| 13 | * are kept in memory (if swaps had the same or higher cost as one deletion | ||
| 14 | * plus one insertion, only two rows would be needed). | ||
| 15 | * | ||
| 16 | * At any stage, "i + 1" denotes the length of the current substring of | ||
| 17 | * string1 that the distance is calculated for. | ||
| 18 | * | ||
| 19 | * row2 holds the current row, row1 the previous row (i.e. for the substring | ||
| 20 | * of string1 of length "i"), and row0 the row before that. | ||
| 21 | * | ||
| 22 | * In other words, at the start of the big loop, row2[j + 1] contains the | ||
| 23 | * Damerau-Levenshtein distance between the substring of string1 of length | ||
| 24 | * "i" and the substring of string2 of length "j + 1". | ||
| 25 | * | ||
| 26 | * All the big loop does is determine the partial minimum-cost paths. | ||
| 27 | * | ||
| 28 | * It does so by calculating the costs of the path ending in characters | ||
| 29 | * i (in string1) and j (in string2), respectively, given that the last | ||
| 30 | * operation is a substition, a swap, a deletion, or an insertion. | ||
| 31 | * | ||
| 32 | * This implementation allows the costs to be weighted: | ||
| 33 | * | ||
| 34 | * - w (as in "sWap") | ||
| 35 | * - s (as in "Substitution") | ||
| 36 | * - a (for insertion, AKA "Add") | ||
| 37 | * - d (as in "Deletion") | ||
| 38 | * | ||
| 39 | * Note that this algorithm calculates a distance _iff_ d == a. | ||
| 40 | */ | ||
| 41 | int levenshtein(const char *string1, const char *string2, | ||
| 42 | int w, int s, int a, int d) | ||
| 43 | { | ||
| 44 | int len1 = strlen(string1), len2 = strlen(string2); | ||
| 45 | int *row0 = malloc(sizeof(int) * (len2 + 1)); | ||
| 46 | int *row1 = malloc(sizeof(int) * (len2 + 1)); | ||
| 47 | int *row2 = malloc(sizeof(int) * (len2 + 1)); | ||
| 48 | int i, j; | ||
| 49 | |||
| 50 | for (j = 0; j <= len2; j++) | ||
| 51 | row1[j] = j * a; | ||
| 52 | for (i = 0; i < len1; i++) { | ||
| 53 | int *dummy; | ||
| 54 | |||
| 55 | row2[0] = (i + 1) * d; | ||
| 56 | for (j = 0; j < len2; j++) { | ||
| 57 | /* substitution */ | ||
| 58 | row2[j + 1] = row1[j] + s * (string1[i] != string2[j]); | ||
| 59 | /* swap */ | ||
| 60 | if (i > 0 && j > 0 && string1[i - 1] == string2[j] && | ||
| 61 | string1[i] == string2[j - 1] && | ||
| 62 | row2[j + 1] > row0[j - 1] + w) | ||
| 63 | row2[j + 1] = row0[j - 1] + w; | ||
| 64 | /* deletion */ | ||
| 65 | if (row2[j + 1] > row1[j + 1] + d) | ||
| 66 | row2[j + 1] = row1[j + 1] + d; | ||
| 67 | /* insertion */ | ||
| 68 | if (row2[j + 1] > row2[j] + a) | ||
| 69 | row2[j + 1] = row2[j] + a; | ||
| 70 | } | ||
| 71 | |||
| 72 | dummy = row0; | ||
| 73 | row0 = row1; | ||
| 74 | row1 = row2; | ||
| 75 | row2 = dummy; | ||
| 76 | } | ||
| 77 | |||
| 78 | i = row1[len2]; | ||
| 79 | free(row0); | ||
| 80 | free(row1); | ||
| 81 | free(row2); | ||
| 82 | |||
| 83 | return i; | ||
| 84 | } | ||
diff --git a/tools/perf/util/levenshtein.h b/tools/perf/util/levenshtein.h new file mode 100644 index 000000000000..0173abeef52c --- /dev/null +++ b/tools/perf/util/levenshtein.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef LEVENSHTEIN_H | ||
| 2 | #define LEVENSHTEIN_H | ||
| 3 | |||
| 4 | int levenshtein(const char *string1, const char *string2, | ||
| 5 | int swap_penalty, int substition_penalty, | ||
| 6 | int insertion_penalty, int deletion_penalty); | ||
| 7 | |||
| 8 | #endif | ||
diff --git a/tools/perf/util/module.c b/tools/perf/util/module.c new file mode 100644 index 000000000000..ddabe925d65d --- /dev/null +++ b/tools/perf/util/module.c | |||
| @@ -0,0 +1,509 @@ | |||
| 1 | #include "util.h" | ||
| 2 | #include "../perf.h" | ||
| 3 | #include "string.h" | ||
| 4 | #include "module.h" | ||
| 5 | |||
| 6 | #include <libelf.h> | ||
| 7 | #include <gelf.h> | ||
| 8 | #include <elf.h> | ||
| 9 | #include <dirent.h> | ||
| 10 | #include <sys/utsname.h> | ||
| 11 | |||
| 12 | static unsigned int crc32(const char *p, unsigned int len) | ||
| 13 | { | ||
| 14 | int i; | ||
| 15 | unsigned int crc = 0; | ||
| 16 | |||
| 17 | while (len--) { | ||
| 18 | crc ^= *p++; | ||
| 19 | for (i = 0; i < 8; i++) | ||
| 20 | crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0); | ||
| 21 | } | ||
| 22 | return crc; | ||
| 23 | } | ||
| 24 | |||
| 25 | /* module section methods */ | ||
| 26 | |||
| 27 | struct sec_dso *sec_dso__new_dso(const char *name) | ||
| 28 | { | ||
| 29 | struct sec_dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
| 30 | |||
| 31 | if (self != NULL) { | ||
| 32 | strcpy(self->name, name); | ||
| 33 | self->secs = RB_ROOT; | ||
| 34 | self->find_section = sec_dso__find_section; | ||
| 35 | } | ||
| 36 | |||
| 37 | return self; | ||
| 38 | } | ||
| 39 | |||
| 40 | static void sec_dso__delete_section(struct section *self) | ||
| 41 | { | ||
| 42 | free(((void *)self)); | ||
| 43 | } | ||
| 44 | |||
| 45 | void sec_dso__delete_sections(struct sec_dso *self) | ||
| 46 | { | ||
| 47 | struct section *pos; | ||
| 48 | struct rb_node *next = rb_first(&self->secs); | ||
| 49 | |||
| 50 | while (next) { | ||
| 51 | pos = rb_entry(next, struct section, rb_node); | ||
| 52 | next = rb_next(&pos->rb_node); | ||
| 53 | rb_erase(&pos->rb_node, &self->secs); | ||
| 54 | sec_dso__delete_section(pos); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | void sec_dso__delete_self(struct sec_dso *self) | ||
| 59 | { | ||
| 60 | sec_dso__delete_sections(self); | ||
| 61 | free(self); | ||
| 62 | } | ||
| 63 | |||
| 64 | static void sec_dso__insert_section(struct sec_dso *self, struct section *sec) | ||
| 65 | { | ||
| 66 | struct rb_node **p = &self->secs.rb_node; | ||
| 67 | struct rb_node *parent = NULL; | ||
| 68 | const u64 hash = sec->hash; | ||
| 69 | struct section *s; | ||
| 70 | |||
| 71 | while (*p != NULL) { | ||
| 72 | parent = *p; | ||
| 73 | s = rb_entry(parent, struct section, rb_node); | ||
| 74 | if (hash < s->hash) | ||
| 75 | p = &(*p)->rb_left; | ||
| 76 | else | ||
| 77 | p = &(*p)->rb_right; | ||
| 78 | } | ||
| 79 | rb_link_node(&sec->rb_node, parent, p); | ||
| 80 | rb_insert_color(&sec->rb_node, &self->secs); | ||
| 81 | } | ||
| 82 | |||
| 83 | struct section *sec_dso__find_section(struct sec_dso *self, const char *name) | ||
| 84 | { | ||
| 85 | struct rb_node *n; | ||
| 86 | u64 hash; | ||
| 87 | int len; | ||
| 88 | |||
| 89 | if (self == NULL) | ||
| 90 | return NULL; | ||
| 91 | |||
| 92 | len = strlen(name); | ||
| 93 | hash = crc32(name, len); | ||
| 94 | |||
| 95 | n = self->secs.rb_node; | ||
| 96 | |||
| 97 | while (n) { | ||
| 98 | struct section *s = rb_entry(n, struct section, rb_node); | ||
| 99 | |||
| 100 | if (hash < s->hash) | ||
| 101 | n = n->rb_left; | ||
| 102 | else if (hash > s->hash) | ||
| 103 | n = n->rb_right; | ||
| 104 | else { | ||
| 105 | if (!strcmp(name, s->name)) | ||
| 106 | return s; | ||
| 107 | else | ||
| 108 | n = rb_next(&s->rb_node); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | return NULL; | ||
| 113 | } | ||
| 114 | |||
| 115 | static size_t sec_dso__fprintf_section(struct section *self, FILE *fp) | ||
| 116 | { | ||
| 117 | return fprintf(fp, "name:%s vma:%llx path:%s\n", | ||
| 118 | self->name, self->vma, self->path); | ||
| 119 | } | ||
| 120 | |||
| 121 | size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp) | ||
| 122 | { | ||
| 123 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | ||
| 124 | |||
| 125 | struct rb_node *nd; | ||
| 126 | for (nd = rb_first(&self->secs); nd; nd = rb_next(nd)) { | ||
| 127 | struct section *pos = rb_entry(nd, struct section, rb_node); | ||
| 128 | ret += sec_dso__fprintf_section(pos, fp); | ||
| 129 | } | ||
| 130 | |||
| 131 | return ret; | ||
| 132 | } | ||
| 133 | |||
| 134 | static struct section *section__new(const char *name, const char *path) | ||
| 135 | { | ||
| 136 | struct section *self = calloc(1, sizeof(*self)); | ||
| 137 | |||
| 138 | if (!self) | ||
| 139 | goto out_failure; | ||
| 140 | |||
| 141 | self->name = calloc(1, strlen(name) + 1); | ||
| 142 | if (!self->name) | ||
| 143 | goto out_failure; | ||
| 144 | |||
| 145 | self->path = calloc(1, strlen(path) + 1); | ||
| 146 | if (!self->path) | ||
| 147 | goto out_failure; | ||
| 148 | |||
| 149 | strcpy(self->name, name); | ||
| 150 | strcpy(self->path, path); | ||
| 151 | self->hash = crc32(self->name, strlen(name)); | ||
| 152 | |||
| 153 | return self; | ||
| 154 | |||
| 155 | out_failure: | ||
| 156 | if (self) { | ||
| 157 | if (self->name) | ||
| 158 | free(self->name); | ||
| 159 | if (self->path) | ||
| 160 | free(self->path); | ||
| 161 | free(self); | ||
| 162 | } | ||
| 163 | |||
| 164 | return NULL; | ||
| 165 | } | ||
| 166 | |||
| 167 | /* module methods */ | ||
| 168 | |||
| 169 | struct mod_dso *mod_dso__new_dso(const char *name) | ||
| 170 | { | ||
| 171 | struct mod_dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
| 172 | |||
| 173 | if (self != NULL) { | ||
| 174 | strcpy(self->name, name); | ||
| 175 | self->mods = RB_ROOT; | ||
| 176 | self->find_module = mod_dso__find_module; | ||
| 177 | } | ||
| 178 | |||
| 179 | return self; | ||
| 180 | } | ||
| 181 | |||
| 182 | static void mod_dso__delete_module(struct module *self) | ||
| 183 | { | ||
| 184 | free(((void *)self)); | ||
| 185 | } | ||
| 186 | |||
| 187 | void mod_dso__delete_modules(struct mod_dso *self) | ||
| 188 | { | ||
| 189 | struct module *pos; | ||
| 190 | struct rb_node *next = rb_first(&self->mods); | ||
| 191 | |||
| 192 | while (next) { | ||
| 193 | pos = rb_entry(next, struct module, rb_node); | ||
| 194 | next = rb_next(&pos->rb_node); | ||
| 195 | rb_erase(&pos->rb_node, &self->mods); | ||
| 196 | mod_dso__delete_module(pos); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | void mod_dso__delete_self(struct mod_dso *self) | ||
| 201 | { | ||
| 202 | mod_dso__delete_modules(self); | ||
| 203 | free(self); | ||
| 204 | } | ||
| 205 | |||
| 206 | static void mod_dso__insert_module(struct mod_dso *self, struct module *mod) | ||
| 207 | { | ||
| 208 | struct rb_node **p = &self->mods.rb_node; | ||
| 209 | struct rb_node *parent = NULL; | ||
| 210 | const u64 hash = mod->hash; | ||
| 211 | struct module *m; | ||
| 212 | |||
| 213 | while (*p != NULL) { | ||
| 214 | parent = *p; | ||
| 215 | m = rb_entry(parent, struct module, rb_node); | ||
| 216 | if (hash < m->hash) | ||
| 217 | p = &(*p)->rb_left; | ||
| 218 | else | ||
| 219 | p = &(*p)->rb_right; | ||
| 220 | } | ||
| 221 | rb_link_node(&mod->rb_node, parent, p); | ||
| 222 | rb_insert_color(&mod->rb_node, &self->mods); | ||
| 223 | } | ||
| 224 | |||
| 225 | struct module *mod_dso__find_module(struct mod_dso *self, const char *name) | ||
| 226 | { | ||
| 227 | struct rb_node *n; | ||
| 228 | u64 hash; | ||
| 229 | int len; | ||
| 230 | |||
| 231 | if (self == NULL) | ||
| 232 | return NULL; | ||
| 233 | |||
| 234 | len = strlen(name); | ||
| 235 | hash = crc32(name, len); | ||
| 236 | |||
| 237 | n = self->mods.rb_node; | ||
| 238 | |||
| 239 | while (n) { | ||
| 240 | struct module *m = rb_entry(n, struct module, rb_node); | ||
| 241 | |||
| 242 | if (hash < m->hash) | ||
| 243 | n = n->rb_left; | ||
| 244 | else if (hash > m->hash) | ||
| 245 | n = n->rb_right; | ||
| 246 | else { | ||
| 247 | if (!strcmp(name, m->name)) | ||
| 248 | return m; | ||
| 249 | else | ||
| 250 | n = rb_next(&m->rb_node); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | return NULL; | ||
| 255 | } | ||
| 256 | |||
| 257 | static size_t mod_dso__fprintf_module(struct module *self, FILE *fp) | ||
| 258 | { | ||
| 259 | return fprintf(fp, "name:%s path:%s\n", self->name, self->path); | ||
| 260 | } | ||
| 261 | |||
| 262 | size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp) | ||
| 263 | { | ||
| 264 | struct rb_node *nd; | ||
| 265 | size_t ret; | ||
| 266 | |||
| 267 | ret = fprintf(fp, "dso: %s\n", self->name); | ||
| 268 | |||
| 269 | for (nd = rb_first(&self->mods); nd; nd = rb_next(nd)) { | ||
| 270 | struct module *pos = rb_entry(nd, struct module, rb_node); | ||
| 271 | |||
| 272 | ret += mod_dso__fprintf_module(pos, fp); | ||
| 273 | } | ||
| 274 | |||
| 275 | return ret; | ||
| 276 | } | ||
| 277 | |||
| 278 | static struct module *module__new(const char *name, const char *path) | ||
| 279 | { | ||
| 280 | struct module *self = calloc(1, sizeof(*self)); | ||
| 281 | |||
| 282 | if (!self) | ||
| 283 | goto out_failure; | ||
| 284 | |||
| 285 | self->name = calloc(1, strlen(name) + 1); | ||
| 286 | if (!self->name) | ||
| 287 | goto out_failure; | ||
| 288 | |||
| 289 | self->path = calloc(1, strlen(path) + 1); | ||
| 290 | if (!self->path) | ||
| 291 | goto out_failure; | ||
| 292 | |||
| 293 | strcpy(self->name, name); | ||
| 294 | strcpy(self->path, path); | ||
| 295 | self->hash = crc32(self->name, strlen(name)); | ||
| 296 | |||
| 297 | return self; | ||
| 298 | |||
| 299 | out_failure: | ||
| 300 | if (self) { | ||
| 301 | if (self->name) | ||
| 302 | free(self->name); | ||
| 303 | if (self->path) | ||
| 304 | free(self->path); | ||
| 305 | free(self); | ||
| 306 | } | ||
| 307 | |||
| 308 | return NULL; | ||
| 309 | } | ||
| 310 | |||
| 311 | static int mod_dso__load_sections(struct module *mod) | ||
| 312 | { | ||
| 313 | int count = 0, path_len; | ||
| 314 | struct dirent *entry; | ||
| 315 | char *line = NULL; | ||
| 316 | char *dir_path; | ||
| 317 | DIR *dir; | ||
| 318 | size_t n; | ||
| 319 | |||
| 320 | path_len = strlen("/sys/module/"); | ||
| 321 | path_len += strlen(mod->name); | ||
| 322 | path_len += strlen("/sections/"); | ||
| 323 | |||
| 324 | dir_path = calloc(1, path_len + 1); | ||
| 325 | if (dir_path == NULL) | ||
| 326 | goto out_failure; | ||
| 327 | |||
| 328 | strcat(dir_path, "/sys/module/"); | ||
| 329 | strcat(dir_path, mod->name); | ||
| 330 | strcat(dir_path, "/sections/"); | ||
| 331 | |||
| 332 | dir = opendir(dir_path); | ||
| 333 | if (dir == NULL) | ||
| 334 | goto out_free; | ||
| 335 | |||
| 336 | while ((entry = readdir(dir))) { | ||
| 337 | struct section *section; | ||
| 338 | char *path, *vma; | ||
| 339 | int line_len; | ||
| 340 | FILE *file; | ||
| 341 | |||
| 342 | if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) | ||
| 343 | continue; | ||
| 344 | |||
| 345 | path = calloc(1, path_len + strlen(entry->d_name) + 1); | ||
| 346 | if (path == NULL) | ||
| 347 | break; | ||
| 348 | strcat(path, dir_path); | ||
| 349 | strcat(path, entry->d_name); | ||
| 350 | |||
| 351 | file = fopen(path, "r"); | ||
| 352 | if (file == NULL) { | ||
| 353 | free(path); | ||
| 354 | break; | ||
| 355 | } | ||
| 356 | |||
| 357 | line_len = getline(&line, &n, file); | ||
| 358 | if (line_len < 0) { | ||
| 359 | free(path); | ||
| 360 | fclose(file); | ||
| 361 | break; | ||
| 362 | } | ||
| 363 | |||
| 364 | if (!line) { | ||
| 365 | free(path); | ||
| 366 | fclose(file); | ||
| 367 | break; | ||
| 368 | } | ||
| 369 | |||
| 370 | line[--line_len] = '\0'; /* \n */ | ||
| 371 | |||
| 372 | vma = strstr(line, "0x"); | ||
| 373 | if (!vma) { | ||
| 374 | free(path); | ||
| 375 | fclose(file); | ||
| 376 | break; | ||
| 377 | } | ||
| 378 | vma += 2; | ||
| 379 | |||
| 380 | section = section__new(entry->d_name, path); | ||
| 381 | if (!section) { | ||
| 382 | fprintf(stderr, "load_sections: allocation error\n"); | ||
| 383 | free(path); | ||
| 384 | fclose(file); | ||
| 385 | break; | ||
| 386 | } | ||
| 387 | |||
| 388 | hex2u64(vma, §ion->vma); | ||
| 389 | sec_dso__insert_section(mod->sections, section); | ||
| 390 | |||
| 391 | free(path); | ||
| 392 | fclose(file); | ||
| 393 | count++; | ||
| 394 | } | ||
| 395 | |||
| 396 | closedir(dir); | ||
| 397 | free(line); | ||
| 398 | free(dir_path); | ||
| 399 | |||
| 400 | return count; | ||
| 401 | |||
| 402 | out_free: | ||
| 403 | free(dir_path); | ||
| 404 | |||
| 405 | out_failure: | ||
| 406 | return count; | ||
| 407 | } | ||
| 408 | |||
| 409 | static int mod_dso__load_module_paths(struct mod_dso *self) | ||
| 410 | { | ||
| 411 | struct utsname uts; | ||
| 412 | int count = 0, len; | ||
| 413 | char *line = NULL; | ||
| 414 | FILE *file; | ||
| 415 | char *path; | ||
| 416 | size_t n; | ||
| 417 | |||
| 418 | if (uname(&uts) < 0) | ||
| 419 | goto out_failure; | ||
| 420 | |||
| 421 | len = strlen("/lib/modules/"); | ||
| 422 | len += strlen(uts.release); | ||
| 423 | len += strlen("/modules.dep"); | ||
| 424 | |||
| 425 | path = calloc(1, len); | ||
| 426 | if (path == NULL) | ||
| 427 | goto out_failure; | ||
| 428 | |||
| 429 | strcat(path, "/lib/modules/"); | ||
| 430 | strcat(path, uts.release); | ||
| 431 | strcat(path, "/modules.dep"); | ||
| 432 | |||
| 433 | file = fopen(path, "r"); | ||
| 434 | free(path); | ||
| 435 | if (file == NULL) | ||
| 436 | goto out_failure; | ||
| 437 | |||
| 438 | while (!feof(file)) { | ||
| 439 | char *path, *name, *tmp; | ||
| 440 | struct module *module; | ||
| 441 | int line_len, len; | ||
| 442 | |||
| 443 | line_len = getline(&line, &n, file); | ||
| 444 | if (line_len < 0) | ||
| 445 | break; | ||
| 446 | |||
| 447 | if (!line) | ||
| 448 | goto out_failure; | ||
| 449 | |||
| 450 | line[--line_len] = '\0'; /* \n */ | ||
| 451 | |||
| 452 | path = strtok(line, ":"); | ||
| 453 | if (!path) | ||
| 454 | goto out_failure; | ||
| 455 | |||
| 456 | name = strdup(path); | ||
| 457 | name = strtok(name, "/"); | ||
| 458 | |||
| 459 | tmp = name; | ||
| 460 | |||
| 461 | while (tmp) { | ||
| 462 | tmp = strtok(NULL, "/"); | ||
| 463 | if (tmp) | ||
| 464 | name = tmp; | ||
| 465 | } | ||
| 466 | name = strsep(&name, "."); | ||
| 467 | |||
| 468 | /* Quirk: replace '-' with '_' in sound modules */ | ||
| 469 | for (len = strlen(name); len; len--) { | ||
| 470 | if (*(name+len) == '-') | ||
| 471 | *(name+len) = '_'; | ||
| 472 | } | ||
| 473 | |||
| 474 | module = module__new(name, path); | ||
| 475 | if (!module) { | ||
| 476 | fprintf(stderr, "load_module_paths: allocation error\n"); | ||
| 477 | goto out_failure; | ||
| 478 | } | ||
| 479 | mod_dso__insert_module(self, module); | ||
| 480 | |||
| 481 | module->sections = sec_dso__new_dso("sections"); | ||
| 482 | if (!module->sections) { | ||
| 483 | fprintf(stderr, "load_module_paths: allocation error\n"); | ||
| 484 | goto out_failure; | ||
| 485 | } | ||
| 486 | |||
| 487 | module->active = mod_dso__load_sections(module); | ||
| 488 | |||
| 489 | if (module->active > 0) | ||
| 490 | count++; | ||
| 491 | } | ||
| 492 | |||
| 493 | free(line); | ||
| 494 | fclose(file); | ||
| 495 | |||
| 496 | return count; | ||
| 497 | |||
| 498 | out_failure: | ||
| 499 | return -1; | ||
| 500 | } | ||
| 501 | |||
| 502 | int mod_dso__load_modules(struct mod_dso *dso) | ||
| 503 | { | ||
| 504 | int err; | ||
| 505 | |||
| 506 | err = mod_dso__load_module_paths(dso); | ||
| 507 | |||
| 508 | return err; | ||
| 509 | } | ||
diff --git a/tools/perf/util/module.h b/tools/perf/util/module.h new file mode 100644 index 000000000000..8a592ef641ca --- /dev/null +++ b/tools/perf/util/module.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | #ifndef _PERF_MODULE_ | ||
| 2 | #define _PERF_MODULE_ 1 | ||
| 3 | |||
| 4 | #include <linux/types.h> | ||
| 5 | #include "../types.h" | ||
| 6 | #include <linux/list.h> | ||
| 7 | #include <linux/rbtree.h> | ||
| 8 | |||
| 9 | struct section { | ||
| 10 | struct rb_node rb_node; | ||
| 11 | u64 hash; | ||
| 12 | u64 vma; | ||
| 13 | char *name; | ||
| 14 | char *path; | ||
| 15 | }; | ||
| 16 | |||
| 17 | struct sec_dso { | ||
| 18 | struct list_head node; | ||
| 19 | struct rb_root secs; | ||
| 20 | struct section *(*find_section)(struct sec_dso *, const char *name); | ||
| 21 | char name[0]; | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct module { | ||
| 25 | struct rb_node rb_node; | ||
| 26 | u64 hash; | ||
| 27 | char *name; | ||
| 28 | char *path; | ||
| 29 | struct sec_dso *sections; | ||
| 30 | int active; | ||
| 31 | }; | ||
| 32 | |||
| 33 | struct mod_dso { | ||
| 34 | struct list_head node; | ||
| 35 | struct rb_root mods; | ||
| 36 | struct module *(*find_module)(struct mod_dso *, const char *name); | ||
| 37 | char name[0]; | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct sec_dso *sec_dso__new_dso(const char *name); | ||
| 41 | void sec_dso__delete_sections(struct sec_dso *self); | ||
| 42 | void sec_dso__delete_self(struct sec_dso *self); | ||
| 43 | size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp); | ||
| 44 | struct section *sec_dso__find_section(struct sec_dso *self, const char *name); | ||
| 45 | |||
| 46 | struct mod_dso *mod_dso__new_dso(const char *name); | ||
| 47 | void mod_dso__delete_modules(struct mod_dso *self); | ||
| 48 | void mod_dso__delete_self(struct mod_dso *self); | ||
| 49 | size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp); | ||
| 50 | struct module *mod_dso__find_module(struct mod_dso *self, const char *name); | ||
| 51 | int mod_dso__load_modules(struct mod_dso *dso); | ||
| 52 | |||
| 53 | #endif /* _PERF_MODULE_ */ | ||
diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c new file mode 100644 index 000000000000..1915de20dcac --- /dev/null +++ b/tools/perf/util/pager.c | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | #include "cache.h" | ||
| 2 | #include "run-command.h" | ||
| 3 | #include "sigchain.h" | ||
| 4 | |||
| 5 | /* | ||
| 6 | * This is split up from the rest of git so that we can do | ||
| 7 | * something different on Windows. | ||
| 8 | */ | ||
| 9 | |||
| 10 | static int spawned_pager; | ||
| 11 | |||
| 12 | static void pager_preexec(void) | ||
| 13 | { | ||
| 14 | /* | ||
| 15 | * Work around bug in "less" by not starting it until we | ||
| 16 | * have real input | ||
| 17 | */ | ||
| 18 | fd_set in; | ||
| 19 | |||
| 20 | FD_ZERO(&in); | ||
| 21 | FD_SET(0, &in); | ||
| 22 | select(1, &in, NULL, &in, NULL); | ||
| 23 | |||
| 24 | setenv("LESS", "FRSX", 0); | ||
| 25 | } | ||
| 26 | |||
| 27 | static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; | ||
| 28 | static struct child_process pager_process; | ||
| 29 | |||
| 30 | static void wait_for_pager(void) | ||
| 31 | { | ||
| 32 | fflush(stdout); | ||
| 33 | fflush(stderr); | ||
| 34 | /* signal EOF to pager */ | ||
| 35 | close(1); | ||
| 36 | close(2); | ||
| 37 | finish_command(&pager_process); | ||
| 38 | } | ||
| 39 | |||
| 40 | static void wait_for_pager_signal(int signo) | ||
| 41 | { | ||
| 42 | wait_for_pager(); | ||
| 43 | sigchain_pop(signo); | ||
| 44 | raise(signo); | ||
| 45 | } | ||
| 46 | |||
| 47 | void setup_pager(void) | ||
| 48 | { | ||
| 49 | const char *pager = getenv("PERF_PAGER"); | ||
| 50 | |||
| 51 | if (!isatty(1)) | ||
| 52 | return; | ||
| 53 | if (!pager) { | ||
| 54 | if (!pager_program) | ||
| 55 | perf_config(perf_default_config, NULL); | ||
| 56 | pager = pager_program; | ||
| 57 | } | ||
| 58 | if (!pager) | ||
| 59 | pager = getenv("PAGER"); | ||
| 60 | if (!pager) | ||
| 61 | pager = "less"; | ||
| 62 | else if (!*pager || !strcmp(pager, "cat")) | ||
| 63 | return; | ||
| 64 | |||
| 65 | spawned_pager = 1; /* means we are emitting to terminal */ | ||
| 66 | |||
| 67 | /* spawn the pager */ | ||
| 68 | pager_argv[2] = pager; | ||
| 69 | pager_process.argv = pager_argv; | ||
| 70 | pager_process.in = -1; | ||
| 71 | pager_process.preexec_cb = pager_preexec; | ||
| 72 | |||
| 73 | if (start_command(&pager_process)) | ||
| 74 | return; | ||
| 75 | |||
| 76 | /* original process continues, but writes to the pipe */ | ||
| 77 | dup2(pager_process.in, 1); | ||
| 78 | if (isatty(2)) | ||
| 79 | dup2(pager_process.in, 2); | ||
| 80 | close(pager_process.in); | ||
| 81 | |||
| 82 | /* this makes sure that the parent terminates after the pager */ | ||
| 83 | sigchain_push_common(wait_for_pager_signal); | ||
| 84 | atexit(wait_for_pager); | ||
| 85 | } | ||
| 86 | |||
| 87 | int pager_in_use(void) | ||
| 88 | { | ||
| 89 | const char *env; | ||
| 90 | |||
| 91 | if (spawned_pager) | ||
| 92 | return 1; | ||
| 93 | |||
| 94 | env = getenv("PERF_PAGER_IN_USE"); | ||
| 95 | return env ? perf_config_bool("PERF_PAGER_IN_USE", env) : 0; | ||
| 96 | } | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c new file mode 100644 index 000000000000..044178408783 --- /dev/null +++ b/tools/perf/util/parse-events.c | |||
| @@ -0,0 +1,682 @@ | |||
| 1 | |||
| 2 | #include "../perf.h" | ||
| 3 | #include "util.h" | ||
| 4 | #include "parse-options.h" | ||
| 5 | #include "parse-events.h" | ||
| 6 | #include "exec_cmd.h" | ||
| 7 | #include "string.h" | ||
| 8 | #include "cache.h" | ||
| 9 | |||
| 10 | extern char *strcasestr(const char *haystack, const char *needle); | ||
| 11 | |||
| 12 | int nr_counters; | ||
| 13 | |||
| 14 | struct perf_counter_attr attrs[MAX_COUNTERS]; | ||
| 15 | |||
| 16 | struct event_symbol { | ||
| 17 | u8 type; | ||
| 18 | u64 config; | ||
| 19 | char *symbol; | ||
| 20 | char *alias; | ||
| 21 | }; | ||
| 22 | |||
| 23 | char debugfs_path[MAXPATHLEN]; | ||
| 24 | |||
| 25 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x | ||
| 26 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x | ||
| 27 | |||
| 28 | static struct event_symbol event_symbols[] = { | ||
| 29 | { CHW(CPU_CYCLES), "cpu-cycles", "cycles" }, | ||
| 30 | { CHW(INSTRUCTIONS), "instructions", "" }, | ||
| 31 | { CHW(CACHE_REFERENCES), "cache-references", "" }, | ||
| 32 | { CHW(CACHE_MISSES), "cache-misses", "" }, | ||
| 33 | { CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" }, | ||
| 34 | { CHW(BRANCH_MISSES), "branch-misses", "" }, | ||
| 35 | { CHW(BUS_CYCLES), "bus-cycles", "" }, | ||
| 36 | |||
| 37 | { CSW(CPU_CLOCK), "cpu-clock", "" }, | ||
| 38 | { CSW(TASK_CLOCK), "task-clock", "" }, | ||
| 39 | { CSW(PAGE_FAULTS), "page-faults", "faults" }, | ||
| 40 | { CSW(PAGE_FAULTS_MIN), "minor-faults", "" }, | ||
| 41 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, | ||
| 42 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, | ||
| 43 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, | ||
| 44 | }; | ||
| 45 | |||
| 46 | #define __PERF_COUNTER_FIELD(config, name) \ | ||
| 47 | ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT) | ||
| 48 | |||
| 49 | #define PERF_COUNTER_RAW(config) __PERF_COUNTER_FIELD(config, RAW) | ||
| 50 | #define PERF_COUNTER_CONFIG(config) __PERF_COUNTER_FIELD(config, CONFIG) | ||
| 51 | #define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE) | ||
| 52 | #define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT) | ||
| 53 | |||
| 54 | static char *hw_event_names[] = { | ||
| 55 | "cycles", | ||
| 56 | "instructions", | ||
| 57 | "cache-references", | ||
| 58 | "cache-misses", | ||
| 59 | "branches", | ||
| 60 | "branch-misses", | ||
| 61 | "bus-cycles", | ||
| 62 | }; | ||
| 63 | |||
| 64 | static char *sw_event_names[] = { | ||
| 65 | "cpu-clock-msecs", | ||
| 66 | "task-clock-msecs", | ||
| 67 | "page-faults", | ||
| 68 | "context-switches", | ||
| 69 | "CPU-migrations", | ||
| 70 | "minor-faults", | ||
| 71 | "major-faults", | ||
| 72 | }; | ||
| 73 | |||
| 74 | #define MAX_ALIASES 8 | ||
| 75 | |||
| 76 | static char *hw_cache[][MAX_ALIASES] = { | ||
| 77 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, | ||
| 78 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, | ||
| 79 | { "LLC", "L2" }, | ||
| 80 | { "dTLB", "d-tlb", "Data-TLB", }, | ||
| 81 | { "iTLB", "i-tlb", "Instruction-TLB", }, | ||
| 82 | { "branch", "branches", "bpu", "btb", "bpc", }, | ||
| 83 | }; | ||
| 84 | |||
| 85 | static char *hw_cache_op[][MAX_ALIASES] = { | ||
| 86 | { "load", "loads", "read", }, | ||
| 87 | { "store", "stores", "write", }, | ||
| 88 | { "prefetch", "prefetches", "speculative-read", "speculative-load", }, | ||
| 89 | }; | ||
| 90 | |||
| 91 | static char *hw_cache_result[][MAX_ALIASES] = { | ||
| 92 | { "refs", "Reference", "ops", "access", }, | ||
| 93 | { "misses", "miss", }, | ||
| 94 | }; | ||
| 95 | |||
| 96 | #define C(x) PERF_COUNT_HW_CACHE_##x | ||
| 97 | #define CACHE_READ (1 << C(OP_READ)) | ||
| 98 | #define CACHE_WRITE (1 << C(OP_WRITE)) | ||
| 99 | #define CACHE_PREFETCH (1 << C(OP_PREFETCH)) | ||
| 100 | #define COP(x) (1 << x) | ||
| 101 | |||
| 102 | /* | ||
| 103 | * cache operartion stat | ||
| 104 | * L1I : Read and prefetch only | ||
| 105 | * ITLB and BPU : Read-only | ||
| 106 | */ | ||
| 107 | static unsigned long hw_cache_stat[C(MAX)] = { | ||
| 108 | [C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
| 109 | [C(L1I)] = (CACHE_READ | CACHE_PREFETCH), | ||
| 110 | [C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
| 111 | [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
| 112 | [C(ITLB)] = (CACHE_READ), | ||
| 113 | [C(BPU)] = (CACHE_READ), | ||
| 114 | }; | ||
| 115 | |||
| 116 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st) \ | ||
| 117 | while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ | ||
| 118 | if (snprintf(file, MAXPATHLEN, "%s/%s", debugfs_path, \ | ||
| 119 | sys_dirent.d_name) && \ | ||
| 120 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
| 121 | (strcmp(sys_dirent.d_name, ".")) && \ | ||
| 122 | (strcmp(sys_dirent.d_name, ".."))) | ||
| 123 | |||
| 124 | static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) | ||
| 125 | { | ||
| 126 | char evt_path[MAXPATHLEN]; | ||
| 127 | int fd; | ||
| 128 | |||
| 129 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | ||
| 130 | sys_dir->d_name, evt_dir->d_name); | ||
| 131 | fd = open(evt_path, O_RDONLY); | ||
| 132 | if (fd < 0) | ||
| 133 | return -EINVAL; | ||
| 134 | close(fd); | ||
| 135 | |||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | |||
| 139 | #define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \ | ||
| 140 | while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ | ||
| 141 | if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path, \ | ||
| 142 | sys_dirent.d_name, evt_dirent.d_name) && \ | ||
| 143 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
| 144 | (strcmp(evt_dirent.d_name, ".")) && \ | ||
| 145 | (strcmp(evt_dirent.d_name, "..")) && \ | ||
| 146 | (!tp_event_has_id(&sys_dirent, &evt_dirent))) | ||
| 147 | |||
| 148 | #define MAX_EVENT_LENGTH 30 | ||
| 149 | |||
| 150 | int valid_debugfs_mount(const char *debugfs) | ||
| 151 | { | ||
| 152 | struct statfs st_fs; | ||
| 153 | |||
| 154 | if (statfs(debugfs, &st_fs) < 0) | ||
| 155 | return -ENOENT; | ||
| 156 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
| 157 | return -ENOENT; | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static char *tracepoint_id_to_name(u64 config) | ||
| 162 | { | ||
| 163 | static char tracepoint_name[2 * MAX_EVENT_LENGTH]; | ||
| 164 | DIR *sys_dir, *evt_dir; | ||
| 165 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
| 166 | struct stat st; | ||
| 167 | char id_buf[4]; | ||
| 168 | int fd; | ||
| 169 | u64 id; | ||
| 170 | char evt_path[MAXPATHLEN]; | ||
| 171 | |||
| 172 | if (valid_debugfs_mount(debugfs_path)) | ||
| 173 | return "unkown"; | ||
| 174 | |||
| 175 | sys_dir = opendir(debugfs_path); | ||
| 176 | if (!sys_dir) | ||
| 177 | goto cleanup; | ||
| 178 | |||
| 179 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
| 180 | evt_dir = opendir(evt_path); | ||
| 181 | if (!evt_dir) | ||
| 182 | goto cleanup; | ||
| 183 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
| 184 | evt_path, st) { | ||
| 185 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", | ||
| 186 | debugfs_path, sys_dirent.d_name, | ||
| 187 | evt_dirent.d_name); | ||
| 188 | fd = open(evt_path, O_RDONLY); | ||
| 189 | if (fd < 0) | ||
| 190 | continue; | ||
| 191 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
| 192 | close(fd); | ||
| 193 | continue; | ||
| 194 | } | ||
| 195 | close(fd); | ||
| 196 | id = atoll(id_buf); | ||
| 197 | if (id == config) { | ||
| 198 | closedir(evt_dir); | ||
| 199 | closedir(sys_dir); | ||
| 200 | snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH, | ||
| 201 | "%s:%s", sys_dirent.d_name, | ||
| 202 | evt_dirent.d_name); | ||
| 203 | return tracepoint_name; | ||
| 204 | } | ||
| 205 | } | ||
| 206 | closedir(evt_dir); | ||
| 207 | } | ||
| 208 | |||
| 209 | cleanup: | ||
| 210 | closedir(sys_dir); | ||
| 211 | return "unkown"; | ||
| 212 | } | ||
| 213 | |||
| 214 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) | ||
| 215 | { | ||
| 216 | if (hw_cache_stat[cache_type] & COP(cache_op)) | ||
| 217 | return 1; /* valid */ | ||
| 218 | else | ||
| 219 | return 0; /* invalid */ | ||
| 220 | } | ||
| 221 | |||
| 222 | static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) | ||
| 223 | { | ||
| 224 | static char name[50]; | ||
| 225 | |||
| 226 | if (cache_result) { | ||
| 227 | sprintf(name, "%s-%s-%s", hw_cache[cache_type][0], | ||
| 228 | hw_cache_op[cache_op][0], | ||
| 229 | hw_cache_result[cache_result][0]); | ||
| 230 | } else { | ||
| 231 | sprintf(name, "%s-%s", hw_cache[cache_type][0], | ||
| 232 | hw_cache_op[cache_op][1]); | ||
| 233 | } | ||
| 234 | |||
| 235 | return name; | ||
| 236 | } | ||
| 237 | |||
| 238 | char *event_name(int counter) | ||
| 239 | { | ||
| 240 | u64 config = attrs[counter].config; | ||
| 241 | int type = attrs[counter].type; | ||
| 242 | |||
| 243 | return __event_name(type, config); | ||
| 244 | } | ||
| 245 | |||
| 246 | char *__event_name(int type, u64 config) | ||
| 247 | { | ||
| 248 | static char buf[32]; | ||
| 249 | |||
| 250 | if (type == PERF_TYPE_RAW) { | ||
| 251 | sprintf(buf, "raw 0x%llx", config); | ||
| 252 | return buf; | ||
| 253 | } | ||
| 254 | |||
| 255 | switch (type) { | ||
| 256 | case PERF_TYPE_HARDWARE: | ||
| 257 | if (config < PERF_COUNT_HW_MAX) | ||
| 258 | return hw_event_names[config]; | ||
| 259 | return "unknown-hardware"; | ||
| 260 | |||
| 261 | case PERF_TYPE_HW_CACHE: { | ||
| 262 | u8 cache_type, cache_op, cache_result; | ||
| 263 | |||
| 264 | cache_type = (config >> 0) & 0xff; | ||
| 265 | if (cache_type > PERF_COUNT_HW_CACHE_MAX) | ||
| 266 | return "unknown-ext-hardware-cache-type"; | ||
| 267 | |||
| 268 | cache_op = (config >> 8) & 0xff; | ||
| 269 | if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX) | ||
| 270 | return "unknown-ext-hardware-cache-op"; | ||
| 271 | |||
| 272 | cache_result = (config >> 16) & 0xff; | ||
| 273 | if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX) | ||
| 274 | return "unknown-ext-hardware-cache-result"; | ||
| 275 | |||
| 276 | if (!is_cache_op_valid(cache_type, cache_op)) | ||
| 277 | return "invalid-cache"; | ||
| 278 | |||
| 279 | return event_cache_name(cache_type, cache_op, cache_result); | ||
| 280 | } | ||
| 281 | |||
| 282 | case PERF_TYPE_SOFTWARE: | ||
| 283 | if (config < PERF_COUNT_SW_MAX) | ||
| 284 | return sw_event_names[config]; | ||
| 285 | return "unknown-software"; | ||
| 286 | |||
| 287 | case PERF_TYPE_TRACEPOINT: | ||
| 288 | return tracepoint_id_to_name(config); | ||
| 289 | |||
| 290 | default: | ||
| 291 | break; | ||
| 292 | } | ||
| 293 | |||
| 294 | return "unknown"; | ||
| 295 | } | ||
| 296 | |||
| 297 | static int parse_aliases(const char **str, char *names[][MAX_ALIASES], int size) | ||
| 298 | { | ||
| 299 | int i, j; | ||
| 300 | int n, longest = -1; | ||
| 301 | |||
| 302 | for (i = 0; i < size; i++) { | ||
| 303 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { | ||
| 304 | n = strlen(names[i][j]); | ||
| 305 | if (n > longest && !strncasecmp(*str, names[i][j], n)) | ||
| 306 | longest = n; | ||
| 307 | } | ||
| 308 | if (longest > 0) { | ||
| 309 | *str += longest; | ||
| 310 | return i; | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | return -1; | ||
| 315 | } | ||
| 316 | |||
| 317 | static int | ||
| 318 | parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) | ||
| 319 | { | ||
| 320 | const char *s = *str; | ||
| 321 | int cache_type = -1, cache_op = -1, cache_result = -1; | ||
| 322 | |||
| 323 | cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX); | ||
| 324 | /* | ||
| 325 | * No fallback - if we cannot get a clear cache type | ||
| 326 | * then bail out: | ||
| 327 | */ | ||
| 328 | if (cache_type == -1) | ||
| 329 | return 0; | ||
| 330 | |||
| 331 | while ((cache_op == -1 || cache_result == -1) && *s == '-') { | ||
| 332 | ++s; | ||
| 333 | |||
| 334 | if (cache_op == -1) { | ||
| 335 | cache_op = parse_aliases(&s, hw_cache_op, | ||
| 336 | PERF_COUNT_HW_CACHE_OP_MAX); | ||
| 337 | if (cache_op >= 0) { | ||
| 338 | if (!is_cache_op_valid(cache_type, cache_op)) | ||
| 339 | return 0; | ||
| 340 | continue; | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | if (cache_result == -1) { | ||
| 345 | cache_result = parse_aliases(&s, hw_cache_result, | ||
| 346 | PERF_COUNT_HW_CACHE_RESULT_MAX); | ||
| 347 | if (cache_result >= 0) | ||
| 348 | continue; | ||
| 349 | } | ||
| 350 | |||
| 351 | /* | ||
| 352 | * Can't parse this as a cache op or result, so back up | ||
| 353 | * to the '-'. | ||
| 354 | */ | ||
| 355 | --s; | ||
| 356 | break; | ||
| 357 | } | ||
| 358 | |||
| 359 | /* | ||
| 360 | * Fall back to reads: | ||
| 361 | */ | ||
| 362 | if (cache_op == -1) | ||
| 363 | cache_op = PERF_COUNT_HW_CACHE_OP_READ; | ||
| 364 | |||
| 365 | /* | ||
| 366 | * Fall back to accesses: | ||
| 367 | */ | ||
| 368 | if (cache_result == -1) | ||
| 369 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; | ||
| 370 | |||
| 371 | attr->config = cache_type | (cache_op << 8) | (cache_result << 16); | ||
| 372 | attr->type = PERF_TYPE_HW_CACHE; | ||
| 373 | |||
| 374 | *str = s; | ||
| 375 | return 1; | ||
| 376 | } | ||
| 377 | |||
| 378 | static int parse_tracepoint_event(const char **strp, | ||
| 379 | struct perf_counter_attr *attr) | ||
| 380 | { | ||
| 381 | const char *evt_name; | ||
| 382 | char *flags; | ||
| 383 | char sys_name[MAX_EVENT_LENGTH]; | ||
| 384 | char id_buf[4]; | ||
| 385 | int fd; | ||
| 386 | unsigned int sys_length, evt_length; | ||
| 387 | u64 id; | ||
| 388 | char evt_path[MAXPATHLEN]; | ||
| 389 | |||
| 390 | if (valid_debugfs_mount(debugfs_path)) | ||
| 391 | return 0; | ||
| 392 | |||
| 393 | evt_name = strchr(*strp, ':'); | ||
| 394 | if (!evt_name) | ||
| 395 | return 0; | ||
| 396 | |||
| 397 | sys_length = evt_name - *strp; | ||
| 398 | if (sys_length >= MAX_EVENT_LENGTH) | ||
| 399 | return 0; | ||
| 400 | |||
| 401 | strncpy(sys_name, *strp, sys_length); | ||
| 402 | sys_name[sys_length] = '\0'; | ||
| 403 | evt_name = evt_name + 1; | ||
| 404 | |||
| 405 | flags = strchr(evt_name, ':'); | ||
| 406 | if (flags) { | ||
| 407 | *flags = '\0'; | ||
| 408 | flags++; | ||
| 409 | if (!strncmp(flags, "record", strlen(flags))) | ||
| 410 | attr->sample_type |= PERF_SAMPLE_RAW; | ||
| 411 | } | ||
| 412 | |||
| 413 | evt_length = strlen(evt_name); | ||
| 414 | if (evt_length >= MAX_EVENT_LENGTH) | ||
| 415 | return 0; | ||
| 416 | |||
| 417 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | ||
| 418 | sys_name, evt_name); | ||
| 419 | fd = open(evt_path, O_RDONLY); | ||
| 420 | if (fd < 0) | ||
| 421 | return 0; | ||
| 422 | |||
| 423 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
| 424 | close(fd); | ||
| 425 | return 0; | ||
| 426 | } | ||
| 427 | close(fd); | ||
| 428 | id = atoll(id_buf); | ||
| 429 | attr->config = id; | ||
| 430 | attr->type = PERF_TYPE_TRACEPOINT; | ||
| 431 | *strp = evt_name + evt_length; | ||
| 432 | return 1; | ||
| 433 | } | ||
| 434 | |||
| 435 | static int check_events(const char *str, unsigned int i) | ||
| 436 | { | ||
| 437 | int n; | ||
| 438 | |||
| 439 | n = strlen(event_symbols[i].symbol); | ||
| 440 | if (!strncmp(str, event_symbols[i].symbol, n)) | ||
| 441 | return n; | ||
| 442 | |||
| 443 | n = strlen(event_symbols[i].alias); | ||
| 444 | if (n) | ||
| 445 | if (!strncmp(str, event_symbols[i].alias, n)) | ||
| 446 | return n; | ||
| 447 | return 0; | ||
| 448 | } | ||
| 449 | |||
| 450 | static int | ||
| 451 | parse_symbolic_event(const char **strp, struct perf_counter_attr *attr) | ||
| 452 | { | ||
| 453 | const char *str = *strp; | ||
| 454 | unsigned int i; | ||
| 455 | int n; | ||
| 456 | |||
| 457 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | ||
| 458 | n = check_events(str, i); | ||
| 459 | if (n > 0) { | ||
| 460 | attr->type = event_symbols[i].type; | ||
| 461 | attr->config = event_symbols[i].config; | ||
| 462 | *strp = str + n; | ||
| 463 | return 1; | ||
| 464 | } | ||
| 465 | } | ||
| 466 | return 0; | ||
| 467 | } | ||
| 468 | |||
| 469 | static int parse_raw_event(const char **strp, struct perf_counter_attr *attr) | ||
| 470 | { | ||
| 471 | const char *str = *strp; | ||
| 472 | u64 config; | ||
| 473 | int n; | ||
| 474 | |||
| 475 | if (*str != 'r') | ||
| 476 | return 0; | ||
| 477 | n = hex2u64(str + 1, &config); | ||
| 478 | if (n > 0) { | ||
| 479 | *strp = str + n + 1; | ||
| 480 | attr->type = PERF_TYPE_RAW; | ||
| 481 | attr->config = config; | ||
| 482 | return 1; | ||
| 483 | } | ||
| 484 | return 0; | ||
| 485 | } | ||
| 486 | |||
| 487 | static int | ||
| 488 | parse_numeric_event(const char **strp, struct perf_counter_attr *attr) | ||
| 489 | { | ||
| 490 | const char *str = *strp; | ||
| 491 | char *endp; | ||
| 492 | unsigned long type; | ||
| 493 | u64 config; | ||
| 494 | |||
| 495 | type = strtoul(str, &endp, 0); | ||
| 496 | if (endp > str && type < PERF_TYPE_MAX && *endp == ':') { | ||
| 497 | str = endp + 1; | ||
| 498 | config = strtoul(str, &endp, 0); | ||
| 499 | if (endp > str) { | ||
| 500 | attr->type = type; | ||
| 501 | attr->config = config; | ||
| 502 | *strp = endp; | ||
| 503 | return 1; | ||
| 504 | } | ||
| 505 | } | ||
| 506 | return 0; | ||
| 507 | } | ||
| 508 | |||
| 509 | static int | ||
| 510 | parse_event_modifier(const char **strp, struct perf_counter_attr *attr) | ||
| 511 | { | ||
| 512 | const char *str = *strp; | ||
| 513 | int eu = 1, ek = 1, eh = 1; | ||
| 514 | |||
| 515 | if (*str++ != ':') | ||
| 516 | return 0; | ||
| 517 | while (*str) { | ||
| 518 | if (*str == 'u') | ||
| 519 | eu = 0; | ||
| 520 | else if (*str == 'k') | ||
| 521 | ek = 0; | ||
| 522 | else if (*str == 'h') | ||
| 523 | eh = 0; | ||
| 524 | else | ||
| 525 | break; | ||
| 526 | ++str; | ||
| 527 | } | ||
| 528 | if (str >= *strp + 2) { | ||
| 529 | *strp = str; | ||
| 530 | attr->exclude_user = eu; | ||
| 531 | attr->exclude_kernel = ek; | ||
| 532 | attr->exclude_hv = eh; | ||
| 533 | return 1; | ||
| 534 | } | ||
| 535 | return 0; | ||
| 536 | } | ||
| 537 | |||
| 538 | /* | ||
| 539 | * Each event can have multiple symbolic names. | ||
| 540 | * Symbolic names are (almost) exactly matched. | ||
| 541 | */ | ||
| 542 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) | ||
| 543 | { | ||
| 544 | if (!(parse_tracepoint_event(str, attr) || | ||
| 545 | parse_raw_event(str, attr) || | ||
| 546 | parse_numeric_event(str, attr) || | ||
| 547 | parse_symbolic_event(str, attr) || | ||
| 548 | parse_generic_hw_event(str, attr))) | ||
| 549 | return 0; | ||
| 550 | |||
| 551 | parse_event_modifier(str, attr); | ||
| 552 | |||
| 553 | return 1; | ||
| 554 | } | ||
| 555 | |||
| 556 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | ||
| 557 | { | ||
| 558 | struct perf_counter_attr attr; | ||
| 559 | |||
| 560 | for (;;) { | ||
| 561 | if (nr_counters == MAX_COUNTERS) | ||
| 562 | return -1; | ||
| 563 | |||
| 564 | memset(&attr, 0, sizeof(attr)); | ||
| 565 | if (!parse_event_symbols(&str, &attr)) | ||
| 566 | return -1; | ||
| 567 | |||
| 568 | if (!(*str == 0 || *str == ',' || isspace(*str))) | ||
| 569 | return -1; | ||
| 570 | |||
| 571 | attrs[nr_counters] = attr; | ||
| 572 | nr_counters++; | ||
| 573 | |||
| 574 | if (*str == 0) | ||
| 575 | break; | ||
| 576 | if (*str == ',') | ||
| 577 | ++str; | ||
| 578 | while (isspace(*str)) | ||
| 579 | ++str; | ||
| 580 | } | ||
| 581 | |||
| 582 | return 0; | ||
| 583 | } | ||
| 584 | |||
| 585 | static const char * const event_type_descriptors[] = { | ||
| 586 | "", | ||
| 587 | "Hardware event", | ||
| 588 | "Software event", | ||
| 589 | "Tracepoint event", | ||
| 590 | "Hardware cache event", | ||
| 591 | }; | ||
| 592 | |||
| 593 | /* | ||
| 594 | * Print the events from <debugfs_mount_point>/tracing/events | ||
| 595 | */ | ||
| 596 | |||
| 597 | static void print_tracepoint_events(void) | ||
| 598 | { | ||
| 599 | DIR *sys_dir, *evt_dir; | ||
| 600 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
| 601 | struct stat st; | ||
| 602 | char evt_path[MAXPATHLEN]; | ||
| 603 | |||
| 604 | if (valid_debugfs_mount(debugfs_path)) | ||
| 605 | return; | ||
| 606 | |||
| 607 | sys_dir = opendir(debugfs_path); | ||
| 608 | if (!sys_dir) | ||
| 609 | goto cleanup; | ||
| 610 | |||
| 611 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
| 612 | evt_dir = opendir(evt_path); | ||
| 613 | if (!evt_dir) | ||
| 614 | goto cleanup; | ||
| 615 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
| 616 | evt_path, st) { | ||
| 617 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | ||
| 618 | sys_dirent.d_name, evt_dirent.d_name); | ||
| 619 | fprintf(stderr, " %-40s [%s]\n", evt_path, | ||
| 620 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | ||
| 621 | } | ||
| 622 | closedir(evt_dir); | ||
| 623 | } | ||
| 624 | |||
| 625 | cleanup: | ||
| 626 | closedir(sys_dir); | ||
| 627 | } | ||
| 628 | |||
| 629 | /* | ||
| 630 | * Print the help text for the event symbols: | ||
| 631 | */ | ||
| 632 | void print_events(void) | ||
| 633 | { | ||
| 634 | struct event_symbol *syms = event_symbols; | ||
| 635 | unsigned int i, type, op, prev_type = -1; | ||
| 636 | char name[40]; | ||
| 637 | |||
| 638 | fprintf(stderr, "\n"); | ||
| 639 | fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); | ||
| 640 | |||
| 641 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | ||
| 642 | type = syms->type + 1; | ||
| 643 | if (type >= ARRAY_SIZE(event_type_descriptors)) | ||
| 644 | type = 0; | ||
| 645 | |||
| 646 | if (type != prev_type) | ||
| 647 | fprintf(stderr, "\n"); | ||
| 648 | |||
| 649 | if (strlen(syms->alias)) | ||
| 650 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | ||
| 651 | else | ||
| 652 | strcpy(name, syms->symbol); | ||
| 653 | fprintf(stderr, " %-40s [%s]\n", name, | ||
| 654 | event_type_descriptors[type]); | ||
| 655 | |||
| 656 | prev_type = type; | ||
| 657 | } | ||
| 658 | |||
| 659 | fprintf(stderr, "\n"); | ||
| 660 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
| 661 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
| 662 | /* skip invalid cache type */ | ||
| 663 | if (!is_cache_op_valid(type, op)) | ||
| 664 | continue; | ||
| 665 | |||
| 666 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
| 667 | fprintf(stderr, " %-40s [%s]\n", | ||
| 668 | event_cache_name(type, op, i), | ||
| 669 | event_type_descriptors[4]); | ||
| 670 | } | ||
| 671 | } | ||
| 672 | } | ||
| 673 | |||
| 674 | fprintf(stderr, "\n"); | ||
| 675 | fprintf(stderr, " %-40s [raw hardware event descriptor]\n", | ||
| 676 | "rNNN"); | ||
| 677 | fprintf(stderr, "\n"); | ||
| 678 | |||
| 679 | print_tracepoint_events(); | ||
| 680 | |||
| 681 | exit(129); | ||
| 682 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h new file mode 100644 index 000000000000..192a962e3a0f --- /dev/null +++ b/tools/perf/util/parse-events.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | |||
| 2 | /* | ||
| 3 | * Parse symbolic events/counts passed in as options: | ||
| 4 | */ | ||
| 5 | |||
| 6 | struct option; | ||
| 7 | |||
| 8 | extern int nr_counters; | ||
| 9 | |||
| 10 | extern struct perf_counter_attr attrs[MAX_COUNTERS]; | ||
| 11 | |||
| 12 | extern char *event_name(int ctr); | ||
| 13 | extern char *__event_name(int type, u64 config); | ||
| 14 | |||
| 15 | extern int parse_events(const struct option *opt, const char *str, int unset); | ||
| 16 | |||
| 17 | #define EVENTS_HELP_MAX (128*1024) | ||
| 18 | |||
| 19 | extern void print_events(void); | ||
| 20 | |||
| 21 | extern char debugfs_path[]; | ||
| 22 | extern int valid_debugfs_mount(const char *debugfs); | ||
| 23 | |||
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c new file mode 100644 index 000000000000..1bf67190c820 --- /dev/null +++ b/tools/perf/util/parse-options.c | |||
| @@ -0,0 +1,509 @@ | |||
| 1 | #include "util.h" | ||
| 2 | #include "parse-options.h" | ||
| 3 | #include "cache.h" | ||
| 4 | |||
| 5 | #define OPT_SHORT 1 | ||
| 6 | #define OPT_UNSET 2 | ||
| 7 | |||
| 8 | static int opterror(const struct option *opt, const char *reason, int flags) | ||
| 9 | { | ||
| 10 | if (flags & OPT_SHORT) | ||
| 11 | return error("switch `%c' %s", opt->short_name, reason); | ||
| 12 | if (flags & OPT_UNSET) | ||
| 13 | return error("option `no-%s' %s", opt->long_name, reason); | ||
| 14 | return error("option `%s' %s", opt->long_name, reason); | ||
| 15 | } | ||
| 16 | |||
| 17 | static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, | ||
| 18 | int flags, const char **arg) | ||
| 19 | { | ||
| 20 | if (p->opt) { | ||
| 21 | *arg = p->opt; | ||
| 22 | p->opt = NULL; | ||
| 23 | } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 || | ||
| 24 | **(p->argv + 1) == '-')) { | ||
| 25 | *arg = (const char *)opt->defval; | ||
| 26 | } else if (p->argc > 1) { | ||
| 27 | p->argc--; | ||
| 28 | *arg = *++p->argv; | ||
| 29 | } else | ||
| 30 | return opterror(opt, "requires a value", flags); | ||
| 31 | return 0; | ||
| 32 | } | ||
| 33 | |||
| 34 | static int get_value(struct parse_opt_ctx_t *p, | ||
| 35 | const struct option *opt, int flags) | ||
| 36 | { | ||
| 37 | const char *s, *arg = NULL; | ||
| 38 | const int unset = flags & OPT_UNSET; | ||
| 39 | |||
| 40 | if (unset && p->opt) | ||
| 41 | return opterror(opt, "takes no value", flags); | ||
| 42 | if (unset && (opt->flags & PARSE_OPT_NONEG)) | ||
| 43 | return opterror(opt, "isn't available", flags); | ||
| 44 | |||
| 45 | if (!(flags & OPT_SHORT) && p->opt) { | ||
| 46 | switch (opt->type) { | ||
| 47 | case OPTION_CALLBACK: | ||
| 48 | if (!(opt->flags & PARSE_OPT_NOARG)) | ||
| 49 | break; | ||
| 50 | /* FALLTHROUGH */ | ||
| 51 | case OPTION_BOOLEAN: | ||
| 52 | case OPTION_BIT: | ||
| 53 | case OPTION_SET_INT: | ||
| 54 | case OPTION_SET_PTR: | ||
| 55 | return opterror(opt, "takes no value", flags); | ||
| 56 | default: | ||
| 57 | break; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | switch (opt->type) { | ||
| 62 | case OPTION_BIT: | ||
| 63 | if (unset) | ||
| 64 | *(int *)opt->value &= ~opt->defval; | ||
| 65 | else | ||
| 66 | *(int *)opt->value |= opt->defval; | ||
| 67 | return 0; | ||
| 68 | |||
| 69 | case OPTION_BOOLEAN: | ||
| 70 | *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; | ||
| 71 | return 0; | ||
| 72 | |||
| 73 | case OPTION_SET_INT: | ||
| 74 | *(int *)opt->value = unset ? 0 : opt->defval; | ||
| 75 | return 0; | ||
| 76 | |||
| 77 | case OPTION_SET_PTR: | ||
| 78 | *(void **)opt->value = unset ? NULL : (void *)opt->defval; | ||
| 79 | return 0; | ||
| 80 | |||
| 81 | case OPTION_STRING: | ||
| 82 | if (unset) | ||
| 83 | *(const char **)opt->value = NULL; | ||
| 84 | else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) | ||
| 85 | *(const char **)opt->value = (const char *)opt->defval; | ||
| 86 | else | ||
| 87 | return get_arg(p, opt, flags, (const char **)opt->value); | ||
| 88 | return 0; | ||
| 89 | |||
| 90 | case OPTION_CALLBACK: | ||
| 91 | if (unset) | ||
| 92 | return (*opt->callback)(opt, NULL, 1) ? (-1) : 0; | ||
| 93 | if (opt->flags & PARSE_OPT_NOARG) | ||
| 94 | return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; | ||
| 95 | if (opt->flags & PARSE_OPT_OPTARG && !p->opt) | ||
| 96 | return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; | ||
| 97 | if (get_arg(p, opt, flags, &arg)) | ||
| 98 | return -1; | ||
| 99 | return (*opt->callback)(opt, arg, 0) ? (-1) : 0; | ||
| 100 | |||
| 101 | case OPTION_INTEGER: | ||
| 102 | if (unset) { | ||
| 103 | *(int *)opt->value = 0; | ||
| 104 | return 0; | ||
| 105 | } | ||
| 106 | if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { | ||
| 107 | *(int *)opt->value = opt->defval; | ||
| 108 | return 0; | ||
| 109 | } | ||
| 110 | if (get_arg(p, opt, flags, &arg)) | ||
| 111 | return -1; | ||
| 112 | *(int *)opt->value = strtol(arg, (char **)&s, 10); | ||
| 113 | if (*s) | ||
| 114 | return opterror(opt, "expects a numerical value", flags); | ||
| 115 | return 0; | ||
| 116 | |||
| 117 | case OPTION_LONG: | ||
| 118 | if (unset) { | ||
| 119 | *(long *)opt->value = 0; | ||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { | ||
| 123 | *(long *)opt->value = opt->defval; | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | if (get_arg(p, opt, flags, &arg)) | ||
| 127 | return -1; | ||
| 128 | *(long *)opt->value = strtol(arg, (char **)&s, 10); | ||
| 129 | if (*s) | ||
| 130 | return opterror(opt, "expects a numerical value", flags); | ||
| 131 | return 0; | ||
| 132 | |||
| 133 | default: | ||
| 134 | die("should not happen, someone must be hit on the forehead"); | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options) | ||
| 139 | { | ||
| 140 | for (; options->type != OPTION_END; options++) { | ||
| 141 | if (options->short_name == *p->opt) { | ||
| 142 | p->opt = p->opt[1] ? p->opt + 1 : NULL; | ||
| 143 | return get_value(p, options, OPT_SHORT); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | return -2; | ||
| 147 | } | ||
| 148 | |||
| 149 | static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, | ||
| 150 | const struct option *options) | ||
| 151 | { | ||
| 152 | const char *arg_end = strchr(arg, '='); | ||
| 153 | const struct option *abbrev_option = NULL, *ambiguous_option = NULL; | ||
| 154 | int abbrev_flags = 0, ambiguous_flags = 0; | ||
| 155 | |||
| 156 | if (!arg_end) | ||
| 157 | arg_end = arg + strlen(arg); | ||
| 158 | |||
| 159 | for (; options->type != OPTION_END; options++) { | ||
| 160 | const char *rest; | ||
| 161 | int flags = 0; | ||
| 162 | |||
| 163 | if (!options->long_name) | ||
| 164 | continue; | ||
| 165 | |||
| 166 | rest = skip_prefix(arg, options->long_name); | ||
| 167 | if (options->type == OPTION_ARGUMENT) { | ||
| 168 | if (!rest) | ||
| 169 | continue; | ||
| 170 | if (*rest == '=') | ||
| 171 | return opterror(options, "takes no value", flags); | ||
| 172 | if (*rest) | ||
| 173 | continue; | ||
| 174 | p->out[p->cpidx++] = arg - 2; | ||
| 175 | return 0; | ||
| 176 | } | ||
| 177 | if (!rest) { | ||
| 178 | /* abbreviated? */ | ||
| 179 | if (!strncmp(options->long_name, arg, arg_end - arg)) { | ||
| 180 | is_abbreviated: | ||
| 181 | if (abbrev_option) { | ||
| 182 | /* | ||
| 183 | * If this is abbreviated, it is | ||
| 184 | * ambiguous. So when there is no | ||
| 185 | * exact match later, we need to | ||
| 186 | * error out. | ||
| 187 | */ | ||
| 188 | ambiguous_option = abbrev_option; | ||
| 189 | ambiguous_flags = abbrev_flags; | ||
| 190 | } | ||
| 191 | if (!(flags & OPT_UNSET) && *arg_end) | ||
| 192 | p->opt = arg_end + 1; | ||
| 193 | abbrev_option = options; | ||
| 194 | abbrev_flags = flags; | ||
| 195 | continue; | ||
| 196 | } | ||
| 197 | /* negated and abbreviated very much? */ | ||
| 198 | if (!prefixcmp("no-", arg)) { | ||
| 199 | flags |= OPT_UNSET; | ||
| 200 | goto is_abbreviated; | ||
| 201 | } | ||
| 202 | /* negated? */ | ||
| 203 | if (strncmp(arg, "no-", 3)) | ||
| 204 | continue; | ||
| 205 | flags |= OPT_UNSET; | ||
| 206 | rest = skip_prefix(arg + 3, options->long_name); | ||
| 207 | /* abbreviated and negated? */ | ||
| 208 | if (!rest && !prefixcmp(options->long_name, arg + 3)) | ||
| 209 | goto is_abbreviated; | ||
| 210 | if (!rest) | ||
| 211 | continue; | ||
| 212 | } | ||
| 213 | if (*rest) { | ||
| 214 | if (*rest != '=') | ||
| 215 | continue; | ||
| 216 | p->opt = rest + 1; | ||
| 217 | } | ||
| 218 | return get_value(p, options, flags); | ||
| 219 | } | ||
| 220 | |||
| 221 | if (ambiguous_option) | ||
| 222 | return error("Ambiguous option: %s " | ||
| 223 | "(could be --%s%s or --%s%s)", | ||
| 224 | arg, | ||
| 225 | (ambiguous_flags & OPT_UNSET) ? "no-" : "", | ||
| 226 | ambiguous_option->long_name, | ||
| 227 | (abbrev_flags & OPT_UNSET) ? "no-" : "", | ||
| 228 | abbrev_option->long_name); | ||
| 229 | if (abbrev_option) | ||
| 230 | return get_value(p, abbrev_option, abbrev_flags); | ||
| 231 | return -2; | ||
| 232 | } | ||
| 233 | |||
| 234 | static void check_typos(const char *arg, const struct option *options) | ||
| 235 | { | ||
| 236 | if (strlen(arg) < 3) | ||
| 237 | return; | ||
| 238 | |||
| 239 | if (!prefixcmp(arg, "no-")) { | ||
| 240 | error ("did you mean `--%s` (with two dashes ?)", arg); | ||
| 241 | exit(129); | ||
| 242 | } | ||
| 243 | |||
| 244 | for (; options->type != OPTION_END; options++) { | ||
| 245 | if (!options->long_name) | ||
| 246 | continue; | ||
| 247 | if (!prefixcmp(options->long_name, arg)) { | ||
| 248 | error ("did you mean `--%s` (with two dashes ?)", arg); | ||
| 249 | exit(129); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | void parse_options_start(struct parse_opt_ctx_t *ctx, | ||
| 255 | int argc, const char **argv, int flags) | ||
| 256 | { | ||
| 257 | memset(ctx, 0, sizeof(*ctx)); | ||
| 258 | ctx->argc = argc - 1; | ||
| 259 | ctx->argv = argv + 1; | ||
| 260 | ctx->out = argv; | ||
| 261 | ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); | ||
| 262 | ctx->flags = flags; | ||
| 263 | if ((flags & PARSE_OPT_KEEP_UNKNOWN) && | ||
| 264 | (flags & PARSE_OPT_STOP_AT_NON_OPTION)) | ||
| 265 | die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); | ||
| 266 | } | ||
| 267 | |||
| 268 | static int usage_with_options_internal(const char * const *, | ||
| 269 | const struct option *, int); | ||
| 270 | |||
| 271 | int parse_options_step(struct parse_opt_ctx_t *ctx, | ||
| 272 | const struct option *options, | ||
| 273 | const char * const usagestr[]) | ||
| 274 | { | ||
| 275 | int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); | ||
| 276 | |||
| 277 | /* we must reset ->opt, unknown short option leave it dangling */ | ||
| 278 | ctx->opt = NULL; | ||
| 279 | |||
| 280 | for (; ctx->argc; ctx->argc--, ctx->argv++) { | ||
| 281 | const char *arg = ctx->argv[0]; | ||
| 282 | |||
| 283 | if (*arg != '-' || !arg[1]) { | ||
| 284 | if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) | ||
| 285 | break; | ||
| 286 | ctx->out[ctx->cpidx++] = ctx->argv[0]; | ||
| 287 | continue; | ||
| 288 | } | ||
| 289 | |||
| 290 | if (arg[1] != '-') { | ||
| 291 | ctx->opt = arg + 1; | ||
| 292 | if (internal_help && *ctx->opt == 'h') | ||
| 293 | return parse_options_usage(usagestr, options); | ||
| 294 | switch (parse_short_opt(ctx, options)) { | ||
| 295 | case -1: | ||
| 296 | return parse_options_usage(usagestr, options); | ||
| 297 | case -2: | ||
| 298 | goto unknown; | ||
| 299 | } | ||
| 300 | if (ctx->opt) | ||
| 301 | check_typos(arg + 1, options); | ||
| 302 | while (ctx->opt) { | ||
| 303 | if (internal_help && *ctx->opt == 'h') | ||
| 304 | return parse_options_usage(usagestr, options); | ||
| 305 | switch (parse_short_opt(ctx, options)) { | ||
| 306 | case -1: | ||
| 307 | return parse_options_usage(usagestr, options); | ||
| 308 | case -2: | ||
| 309 | /* fake a short option thing to hide the fact that we may have | ||
| 310 | * started to parse aggregated stuff | ||
| 311 | * | ||
| 312 | * This is leaky, too bad. | ||
| 313 | */ | ||
| 314 | ctx->argv[0] = strdup(ctx->opt - 1); | ||
| 315 | *(char *)ctx->argv[0] = '-'; | ||
| 316 | goto unknown; | ||
| 317 | } | ||
| 318 | } | ||
| 319 | continue; | ||
| 320 | } | ||
| 321 | |||
| 322 | if (!arg[2]) { /* "--" */ | ||
| 323 | if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { | ||
| 324 | ctx->argc--; | ||
| 325 | ctx->argv++; | ||
| 326 | } | ||
| 327 | break; | ||
| 328 | } | ||
| 329 | |||
| 330 | if (internal_help && !strcmp(arg + 2, "help-all")) | ||
| 331 | return usage_with_options_internal(usagestr, options, 1); | ||
| 332 | if (internal_help && !strcmp(arg + 2, "help")) | ||
| 333 | return parse_options_usage(usagestr, options); | ||
| 334 | switch (parse_long_opt(ctx, arg + 2, options)) { | ||
| 335 | case -1: | ||
| 336 | return parse_options_usage(usagestr, options); | ||
| 337 | case -2: | ||
| 338 | goto unknown; | ||
| 339 | } | ||
| 340 | continue; | ||
| 341 | unknown: | ||
| 342 | if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN)) | ||
| 343 | return PARSE_OPT_UNKNOWN; | ||
| 344 | ctx->out[ctx->cpidx++] = ctx->argv[0]; | ||
| 345 | ctx->opt = NULL; | ||
| 346 | } | ||
| 347 | return PARSE_OPT_DONE; | ||
| 348 | } | ||
| 349 | |||
| 350 | int parse_options_end(struct parse_opt_ctx_t *ctx) | ||
| 351 | { | ||
| 352 | memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out)); | ||
| 353 | ctx->out[ctx->cpidx + ctx->argc] = NULL; | ||
| 354 | return ctx->cpidx + ctx->argc; | ||
| 355 | } | ||
| 356 | |||
| 357 | int parse_options(int argc, const char **argv, const struct option *options, | ||
| 358 | const char * const usagestr[], int flags) | ||
| 359 | { | ||
| 360 | struct parse_opt_ctx_t ctx; | ||
| 361 | |||
| 362 | parse_options_start(&ctx, argc, argv, flags); | ||
| 363 | switch (parse_options_step(&ctx, options, usagestr)) { | ||
| 364 | case PARSE_OPT_HELP: | ||
| 365 | exit(129); | ||
| 366 | case PARSE_OPT_DONE: | ||
| 367 | break; | ||
| 368 | default: /* PARSE_OPT_UNKNOWN */ | ||
| 369 | if (ctx.argv[0][1] == '-') { | ||
| 370 | error("unknown option `%s'", ctx.argv[0] + 2); | ||
| 371 | } else { | ||
| 372 | error("unknown switch `%c'", *ctx.opt); | ||
| 373 | } | ||
| 374 | usage_with_options(usagestr, options); | ||
| 375 | } | ||
| 376 | |||
| 377 | return parse_options_end(&ctx); | ||
| 378 | } | ||
| 379 | |||
| 380 | #define USAGE_OPTS_WIDTH 24 | ||
| 381 | #define USAGE_GAP 2 | ||
| 382 | |||
| 383 | int usage_with_options_internal(const char * const *usagestr, | ||
| 384 | const struct option *opts, int full) | ||
| 385 | { | ||
| 386 | if (!usagestr) | ||
| 387 | return PARSE_OPT_HELP; | ||
| 388 | |||
| 389 | fprintf(stderr, "\n usage: %s\n", *usagestr++); | ||
| 390 | while (*usagestr && **usagestr) | ||
| 391 | fprintf(stderr, " or: %s\n", *usagestr++); | ||
| 392 | while (*usagestr) { | ||
| 393 | fprintf(stderr, "%s%s\n", | ||
| 394 | **usagestr ? " " : "", | ||
| 395 | *usagestr); | ||
| 396 | usagestr++; | ||
| 397 | } | ||
| 398 | |||
| 399 | if (opts->type != OPTION_GROUP) | ||
| 400 | fputc('\n', stderr); | ||
| 401 | |||
| 402 | for (; opts->type != OPTION_END; opts++) { | ||
| 403 | size_t pos; | ||
| 404 | int pad; | ||
| 405 | |||
| 406 | if (opts->type == OPTION_GROUP) { | ||
| 407 | fputc('\n', stderr); | ||
| 408 | if (*opts->help) | ||
| 409 | fprintf(stderr, "%s\n", opts->help); | ||
| 410 | continue; | ||
| 411 | } | ||
| 412 | if (!full && (opts->flags & PARSE_OPT_HIDDEN)) | ||
| 413 | continue; | ||
| 414 | |||
| 415 | pos = fprintf(stderr, " "); | ||
| 416 | if (opts->short_name) | ||
| 417 | pos += fprintf(stderr, "-%c", opts->short_name); | ||
| 418 | if (opts->long_name && opts->short_name) | ||
| 419 | pos += fprintf(stderr, ", "); | ||
| 420 | if (opts->long_name) | ||
| 421 | pos += fprintf(stderr, "--%s", opts->long_name); | ||
| 422 | |||
| 423 | switch (opts->type) { | ||
| 424 | case OPTION_ARGUMENT: | ||
| 425 | break; | ||
| 426 | case OPTION_INTEGER: | ||
| 427 | if (opts->flags & PARSE_OPT_OPTARG) | ||
| 428 | if (opts->long_name) | ||
| 429 | pos += fprintf(stderr, "[=<n>]"); | ||
| 430 | else | ||
| 431 | pos += fprintf(stderr, "[<n>]"); | ||
| 432 | else | ||
| 433 | pos += fprintf(stderr, " <n>"); | ||
| 434 | break; | ||
| 435 | case OPTION_CALLBACK: | ||
| 436 | if (opts->flags & PARSE_OPT_NOARG) | ||
| 437 | break; | ||
| 438 | /* FALLTHROUGH */ | ||
| 439 | case OPTION_STRING: | ||
| 440 | if (opts->argh) { | ||
| 441 | if (opts->flags & PARSE_OPT_OPTARG) | ||
| 442 | if (opts->long_name) | ||
| 443 | pos += fprintf(stderr, "[=<%s>]", opts->argh); | ||
| 444 | else | ||
| 445 | pos += fprintf(stderr, "[<%s>]", opts->argh); | ||
| 446 | else | ||
| 447 | pos += fprintf(stderr, " <%s>", opts->argh); | ||
| 448 | } else { | ||
| 449 | if (opts->flags & PARSE_OPT_OPTARG) | ||
| 450 | if (opts->long_name) | ||
| 451 | pos += fprintf(stderr, "[=...]"); | ||
| 452 | else | ||
| 453 | pos += fprintf(stderr, "[...]"); | ||
| 454 | else | ||
| 455 | pos += fprintf(stderr, " ..."); | ||
| 456 | } | ||
| 457 | break; | ||
| 458 | default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */ | ||
| 459 | break; | ||
| 460 | } | ||
| 461 | |||
| 462 | if (pos <= USAGE_OPTS_WIDTH) | ||
| 463 | pad = USAGE_OPTS_WIDTH - pos; | ||
| 464 | else { | ||
| 465 | fputc('\n', stderr); | ||
| 466 | pad = USAGE_OPTS_WIDTH; | ||
| 467 | } | ||
| 468 | fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); | ||
| 469 | } | ||
| 470 | fputc('\n', stderr); | ||
| 471 | |||
| 472 | return PARSE_OPT_HELP; | ||
| 473 | } | ||
| 474 | |||
| 475 | void usage_with_options(const char * const *usagestr, | ||
| 476 | const struct option *opts) | ||
| 477 | { | ||
| 478 | usage_with_options_internal(usagestr, opts, 0); | ||
| 479 | exit(129); | ||
| 480 | } | ||
| 481 | |||
| 482 | int parse_options_usage(const char * const *usagestr, | ||
| 483 | const struct option *opts) | ||
| 484 | { | ||
| 485 | return usage_with_options_internal(usagestr, opts, 0); | ||
| 486 | } | ||
| 487 | |||
| 488 | |||
| 489 | int parse_opt_verbosity_cb(const struct option *opt, const char *arg __used, | ||
| 490 | int unset) | ||
| 491 | { | ||
| 492 | int *target = opt->value; | ||
| 493 | |||
| 494 | if (unset) | ||
| 495 | /* --no-quiet, --no-verbose */ | ||
| 496 | *target = 0; | ||
| 497 | else if (opt->short_name == 'v') { | ||
| 498 | if (*target >= 0) | ||
| 499 | (*target)++; | ||
| 500 | else | ||
| 501 | *target = 1; | ||
| 502 | } else { | ||
| 503 | if (*target <= 0) | ||
| 504 | (*target)--; | ||
| 505 | else | ||
| 506 | *target = -1; | ||
| 507 | } | ||
| 508 | return 0; | ||
| 509 | } | ||
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h new file mode 100644 index 000000000000..8aa3464c7090 --- /dev/null +++ b/tools/perf/util/parse-options.h | |||
| @@ -0,0 +1,175 @@ | |||
| 1 | #ifndef PARSE_OPTIONS_H | ||
| 2 | #define PARSE_OPTIONS_H | ||
| 3 | |||
| 4 | enum parse_opt_type { | ||
| 5 | /* special types */ | ||
| 6 | OPTION_END, | ||
| 7 | OPTION_ARGUMENT, | ||
| 8 | OPTION_GROUP, | ||
| 9 | /* options with no arguments */ | ||
| 10 | OPTION_BIT, | ||
| 11 | OPTION_BOOLEAN, /* _INCR would have been a better name */ | ||
| 12 | OPTION_SET_INT, | ||
| 13 | OPTION_SET_PTR, | ||
| 14 | /* options with arguments (usually) */ | ||
| 15 | OPTION_STRING, | ||
| 16 | OPTION_INTEGER, | ||
| 17 | OPTION_LONG, | ||
| 18 | OPTION_CALLBACK, | ||
| 19 | }; | ||
| 20 | |||
| 21 | enum parse_opt_flags { | ||
| 22 | PARSE_OPT_KEEP_DASHDASH = 1, | ||
| 23 | PARSE_OPT_STOP_AT_NON_OPTION = 2, | ||
| 24 | PARSE_OPT_KEEP_ARGV0 = 4, | ||
| 25 | PARSE_OPT_KEEP_UNKNOWN = 8, | ||
| 26 | PARSE_OPT_NO_INTERNAL_HELP = 16, | ||
| 27 | }; | ||
| 28 | |||
| 29 | enum parse_opt_option_flags { | ||
| 30 | PARSE_OPT_OPTARG = 1, | ||
| 31 | PARSE_OPT_NOARG = 2, | ||
| 32 | PARSE_OPT_NONEG = 4, | ||
| 33 | PARSE_OPT_HIDDEN = 8, | ||
| 34 | PARSE_OPT_LASTARG_DEFAULT = 16, | ||
| 35 | }; | ||
| 36 | |||
| 37 | struct option; | ||
| 38 | typedef int parse_opt_cb(const struct option *, const char *arg, int unset); | ||
| 39 | |||
| 40 | /* | ||
| 41 | * `type`:: | ||
| 42 | * holds the type of the option, you must have an OPTION_END last in your | ||
| 43 | * array. | ||
| 44 | * | ||
| 45 | * `short_name`:: | ||
| 46 | * the character to use as a short option name, '\0' if none. | ||
| 47 | * | ||
| 48 | * `long_name`:: | ||
| 49 | * the long option name, without the leading dashes, NULL if none. | ||
| 50 | * | ||
| 51 | * `value`:: | ||
| 52 | * stores pointers to the values to be filled. | ||
| 53 | * | ||
| 54 | * `argh`:: | ||
| 55 | * token to explain the kind of argument this option wants. Keep it | ||
| 56 | * homogenous across the repository. | ||
| 57 | * | ||
| 58 | * `help`:: | ||
| 59 | * the short help associated to what the option does. | ||
| 60 | * Must never be NULL (except for OPTION_END). | ||
| 61 | * OPTION_GROUP uses this pointer to store the group header. | ||
| 62 | * | ||
| 63 | * `flags`:: | ||
| 64 | * mask of parse_opt_option_flags. | ||
| 65 | * PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs) | ||
| 66 | * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs | ||
| 67 | * PARSE_OPT_NONEG: says that this option cannot be negated | ||
| 68 | * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in | ||
| 69 | * the long one. | ||
| 70 | * | ||
| 71 | * `callback`:: | ||
| 72 | * pointer to the callback to use for OPTION_CALLBACK. | ||
| 73 | * | ||
| 74 | * `defval`:: | ||
| 75 | * default value to fill (*->value) with for PARSE_OPT_OPTARG. | ||
| 76 | * OPTION_{BIT,SET_INT,SET_PTR} store the {mask,integer,pointer} to put in | ||
| 77 | * the value when met. | ||
| 78 | * CALLBACKS can use it like they want. | ||
| 79 | */ | ||
| 80 | struct option { | ||
| 81 | enum parse_opt_type type; | ||
| 82 | int short_name; | ||
| 83 | const char *long_name; | ||
| 84 | void *value; | ||
| 85 | const char *argh; | ||
| 86 | const char *help; | ||
| 87 | |||
| 88 | int flags; | ||
| 89 | parse_opt_cb *callback; | ||
| 90 | intptr_t defval; | ||
| 91 | }; | ||
| 92 | |||
| 93 | #define OPT_END() { .type = OPTION_END } | ||
| 94 | #define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } | ||
| 95 | #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } | ||
| 96 | #define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (b) } | ||
| 97 | #define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } | ||
| 98 | #define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) } | ||
| 99 | #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } | ||
| 100 | #define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } | ||
| 101 | #define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } | ||
| 102 | #define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h) } | ||
| 103 | #define OPT_DATE(s, l, v, h) \ | ||
| 104 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } | ||
| 105 | #define OPT_CALLBACK(s, l, v, a, h, f) \ | ||
| 106 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) } | ||
| 107 | #define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ | ||
| 108 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT } | ||
| 109 | |||
| 110 | /* parse_options() will filter out the processed options and leave the | ||
| 111 | * non-option argments in argv[]. | ||
| 112 | * Returns the number of arguments left in argv[]. | ||
| 113 | */ | ||
| 114 | extern int parse_options(int argc, const char **argv, | ||
| 115 | const struct option *options, | ||
| 116 | const char * const usagestr[], int flags); | ||
| 117 | |||
| 118 | extern NORETURN void usage_with_options(const char * const *usagestr, | ||
| 119 | const struct option *options); | ||
| 120 | |||
| 121 | /*----- incremantal advanced APIs -----*/ | ||
| 122 | |||
| 123 | enum { | ||
| 124 | PARSE_OPT_HELP = -1, | ||
| 125 | PARSE_OPT_DONE, | ||
| 126 | PARSE_OPT_UNKNOWN, | ||
| 127 | }; | ||
| 128 | |||
| 129 | /* | ||
| 130 | * It's okay for the caller to consume argv/argc in the usual way. | ||
| 131 | * Other fields of that structure are private to parse-options and should not | ||
| 132 | * be modified in any way. | ||
| 133 | */ | ||
| 134 | struct parse_opt_ctx_t { | ||
| 135 | const char **argv; | ||
| 136 | const char **out; | ||
| 137 | int argc, cpidx; | ||
| 138 | const char *opt; | ||
| 139 | int flags; | ||
| 140 | }; | ||
| 141 | |||
| 142 | extern int parse_options_usage(const char * const *usagestr, | ||
| 143 | const struct option *opts); | ||
| 144 | |||
| 145 | extern void parse_options_start(struct parse_opt_ctx_t *ctx, | ||
| 146 | int argc, const char **argv, int flags); | ||
| 147 | |||
| 148 | extern int parse_options_step(struct parse_opt_ctx_t *ctx, | ||
| 149 | const struct option *options, | ||
| 150 | const char * const usagestr[]); | ||
| 151 | |||
| 152 | extern int parse_options_end(struct parse_opt_ctx_t *ctx); | ||
| 153 | |||
| 154 | |||
| 155 | /*----- some often used options -----*/ | ||
| 156 | extern int parse_opt_abbrev_cb(const struct option *, const char *, int); | ||
| 157 | extern int parse_opt_approxidate_cb(const struct option *, const char *, int); | ||
| 158 | extern int parse_opt_verbosity_cb(const struct option *, const char *, int); | ||
| 159 | |||
| 160 | #define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") | ||
| 161 | #define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") | ||
| 162 | #define OPT__VERBOSITY(var) \ | ||
| 163 | { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \ | ||
| 164 | PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \ | ||
| 165 | { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \ | ||
| 166 | PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 } | ||
| 167 | #define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run") | ||
| 168 | #define OPT__ABBREV(var) \ | ||
| 169 | { OPTION_CALLBACK, 0, "abbrev", (var), "n", \ | ||
| 170 | "use <n> digits to display SHA-1s", \ | ||
| 171 | PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } | ||
| 172 | |||
| 173 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); | ||
| 174 | |||
| 175 | #endif | ||
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c new file mode 100644 index 000000000000..a501a40dd2cb --- /dev/null +++ b/tools/perf/util/path.c | |||
| @@ -0,0 +1,353 @@ | |||
| 1 | /* | ||
| 2 | * I'm tired of doing "vsnprintf()" etc just to open a | ||
| 3 | * file, so here's a "return static buffer with printf" | ||
| 4 | * interface for paths. | ||
| 5 | * | ||
| 6 | * It's obviously not thread-safe. Sue me. But it's quite | ||
| 7 | * useful for doing things like | ||
| 8 | * | ||
| 9 | * f = open(mkpath("%s/%s.perf", base, name), O_RDONLY); | ||
| 10 | * | ||
| 11 | * which is what it's designed for. | ||
| 12 | */ | ||
| 13 | #include "cache.h" | ||
| 14 | |||
| 15 | static char bad_path[] = "/bad-path/"; | ||
| 16 | /* | ||
| 17 | * Two hacks: | ||
| 18 | */ | ||
| 19 | |||
| 20 | static char *get_perf_dir(void) | ||
| 21 | { | ||
| 22 | return "."; | ||
| 23 | } | ||
| 24 | |||
| 25 | size_t strlcpy(char *dest, const char *src, size_t size) | ||
| 26 | { | ||
| 27 | size_t ret = strlen(src); | ||
| 28 | |||
| 29 | if (size) { | ||
| 30 | size_t len = (ret >= size) ? size - 1 : ret; | ||
| 31 | memcpy(dest, src, len); | ||
| 32 | dest[len] = '\0'; | ||
| 33 | } | ||
| 34 | return ret; | ||
| 35 | } | ||
| 36 | |||
| 37 | |||
| 38 | static char *get_pathname(void) | ||
| 39 | { | ||
| 40 | static char pathname_array[4][PATH_MAX]; | ||
| 41 | static int index; | ||
| 42 | return pathname_array[3 & ++index]; | ||
| 43 | } | ||
| 44 | |||
| 45 | static char *cleanup_path(char *path) | ||
| 46 | { | ||
| 47 | /* Clean it up */ | ||
| 48 | if (!memcmp(path, "./", 2)) { | ||
| 49 | path += 2; | ||
| 50 | while (*path == '/') | ||
| 51 | path++; | ||
| 52 | } | ||
| 53 | return path; | ||
| 54 | } | ||
| 55 | |||
| 56 | char *mksnpath(char *buf, size_t n, const char *fmt, ...) | ||
| 57 | { | ||
| 58 | va_list args; | ||
| 59 | unsigned len; | ||
| 60 | |||
| 61 | va_start(args, fmt); | ||
| 62 | len = vsnprintf(buf, n, fmt, args); | ||
| 63 | va_end(args); | ||
| 64 | if (len >= n) { | ||
| 65 | strlcpy(buf, bad_path, n); | ||
| 66 | return buf; | ||
| 67 | } | ||
| 68 | return cleanup_path(buf); | ||
| 69 | } | ||
| 70 | |||
| 71 | static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args) | ||
| 72 | { | ||
| 73 | const char *perf_dir = get_perf_dir(); | ||
| 74 | size_t len; | ||
| 75 | |||
| 76 | len = strlen(perf_dir); | ||
| 77 | if (n < len + 1) | ||
| 78 | goto bad; | ||
| 79 | memcpy(buf, perf_dir, len); | ||
| 80 | if (len && !is_dir_sep(perf_dir[len-1])) | ||
| 81 | buf[len++] = '/'; | ||
| 82 | len += vsnprintf(buf + len, n - len, fmt, args); | ||
| 83 | if (len >= n) | ||
| 84 | goto bad; | ||
| 85 | return cleanup_path(buf); | ||
| 86 | bad: | ||
| 87 | strlcpy(buf, bad_path, n); | ||
| 88 | return buf; | ||
| 89 | } | ||
| 90 | |||
| 91 | char *perf_snpath(char *buf, size_t n, const char *fmt, ...) | ||
| 92 | { | ||
| 93 | va_list args; | ||
| 94 | va_start(args, fmt); | ||
| 95 | (void)perf_vsnpath(buf, n, fmt, args); | ||
| 96 | va_end(args); | ||
| 97 | return buf; | ||
| 98 | } | ||
| 99 | |||
| 100 | char *perf_pathdup(const char *fmt, ...) | ||
| 101 | { | ||
| 102 | char path[PATH_MAX]; | ||
| 103 | va_list args; | ||
| 104 | va_start(args, fmt); | ||
| 105 | (void)perf_vsnpath(path, sizeof(path), fmt, args); | ||
| 106 | va_end(args); | ||
| 107 | return xstrdup(path); | ||
| 108 | } | ||
| 109 | |||
| 110 | char *mkpath(const char *fmt, ...) | ||
| 111 | { | ||
| 112 | va_list args; | ||
| 113 | unsigned len; | ||
| 114 | char *pathname = get_pathname(); | ||
| 115 | |||
| 116 | va_start(args, fmt); | ||
| 117 | len = vsnprintf(pathname, PATH_MAX, fmt, args); | ||
| 118 | va_end(args); | ||
| 119 | if (len >= PATH_MAX) | ||
| 120 | return bad_path; | ||
| 121 | return cleanup_path(pathname); | ||
| 122 | } | ||
| 123 | |||
| 124 | char *perf_path(const char *fmt, ...) | ||
| 125 | { | ||
| 126 | const char *perf_dir = get_perf_dir(); | ||
| 127 | char *pathname = get_pathname(); | ||
| 128 | va_list args; | ||
| 129 | unsigned len; | ||
| 130 | |||
| 131 | len = strlen(perf_dir); | ||
| 132 | if (len > PATH_MAX-100) | ||
| 133 | return bad_path; | ||
| 134 | memcpy(pathname, perf_dir, len); | ||
| 135 | if (len && perf_dir[len-1] != '/') | ||
| 136 | pathname[len++] = '/'; | ||
| 137 | va_start(args, fmt); | ||
| 138 | len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args); | ||
| 139 | va_end(args); | ||
| 140 | if (len >= PATH_MAX) | ||
| 141 | return bad_path; | ||
| 142 | return cleanup_path(pathname); | ||
| 143 | } | ||
| 144 | |||
| 145 | |||
| 146 | /* perf_mkstemp() - create tmp file honoring TMPDIR variable */ | ||
| 147 | int perf_mkstemp(char *path, size_t len, const char *template) | ||
| 148 | { | ||
| 149 | const char *tmp; | ||
| 150 | size_t n; | ||
| 151 | |||
| 152 | tmp = getenv("TMPDIR"); | ||
| 153 | if (!tmp) | ||
| 154 | tmp = "/tmp"; | ||
| 155 | n = snprintf(path, len, "%s/%s", tmp, template); | ||
| 156 | if (len <= n) { | ||
| 157 | errno = ENAMETOOLONG; | ||
| 158 | return -1; | ||
| 159 | } | ||
| 160 | return mkstemp(path); | ||
| 161 | } | ||
| 162 | |||
| 163 | |||
| 164 | const char *make_relative_path(const char *abs, const char *base) | ||
| 165 | { | ||
| 166 | static char buf[PATH_MAX + 1]; | ||
| 167 | int baselen; | ||
| 168 | if (!base) | ||
| 169 | return abs; | ||
| 170 | baselen = strlen(base); | ||
| 171 | if (prefixcmp(abs, base)) | ||
| 172 | return abs; | ||
| 173 | if (abs[baselen] == '/') | ||
| 174 | baselen++; | ||
| 175 | else if (base[baselen - 1] != '/') | ||
| 176 | return abs; | ||
| 177 | strcpy(buf, abs + baselen); | ||
| 178 | return buf; | ||
| 179 | } | ||
| 180 | |||
| 181 | /* | ||
| 182 | * It is okay if dst == src, but they should not overlap otherwise. | ||
| 183 | * | ||
| 184 | * Performs the following normalizations on src, storing the result in dst: | ||
| 185 | * - Ensures that components are separated by '/' (Windows only) | ||
| 186 | * - Squashes sequences of '/'. | ||
| 187 | * - Removes "." components. | ||
| 188 | * - Removes ".." components, and the components the precede them. | ||
| 189 | * Returns failure (non-zero) if a ".." component appears as first path | ||
| 190 | * component anytime during the normalization. Otherwise, returns success (0). | ||
| 191 | * | ||
| 192 | * Note that this function is purely textual. It does not follow symlinks, | ||
| 193 | * verify the existence of the path, or make any system calls. | ||
| 194 | */ | ||
| 195 | int normalize_path_copy(char *dst, const char *src) | ||
| 196 | { | ||
| 197 | char *dst0; | ||
| 198 | |||
| 199 | if (has_dos_drive_prefix(src)) { | ||
| 200 | *dst++ = *src++; | ||
| 201 | *dst++ = *src++; | ||
| 202 | } | ||
| 203 | dst0 = dst; | ||
| 204 | |||
| 205 | if (is_dir_sep(*src)) { | ||
| 206 | *dst++ = '/'; | ||
| 207 | while (is_dir_sep(*src)) | ||
| 208 | src++; | ||
| 209 | } | ||
| 210 | |||
| 211 | for (;;) { | ||
| 212 | char c = *src; | ||
| 213 | |||
| 214 | /* | ||
| 215 | * A path component that begins with . could be | ||
| 216 | * special: | ||
| 217 | * (1) "." and ends -- ignore and terminate. | ||
| 218 | * (2) "./" -- ignore them, eat slash and continue. | ||
| 219 | * (3) ".." and ends -- strip one and terminate. | ||
| 220 | * (4) "../" -- strip one, eat slash and continue. | ||
| 221 | */ | ||
| 222 | if (c == '.') { | ||
| 223 | if (!src[1]) { | ||
| 224 | /* (1) */ | ||
| 225 | src++; | ||
| 226 | } else if (is_dir_sep(src[1])) { | ||
| 227 | /* (2) */ | ||
| 228 | src += 2; | ||
| 229 | while (is_dir_sep(*src)) | ||
| 230 | src++; | ||
| 231 | continue; | ||
| 232 | } else if (src[1] == '.') { | ||
| 233 | if (!src[2]) { | ||
| 234 | /* (3) */ | ||
| 235 | src += 2; | ||
| 236 | goto up_one; | ||
| 237 | } else if (is_dir_sep(src[2])) { | ||
| 238 | /* (4) */ | ||
| 239 | src += 3; | ||
| 240 | while (is_dir_sep(*src)) | ||
| 241 | src++; | ||
| 242 | goto up_one; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | /* copy up to the next '/', and eat all '/' */ | ||
| 248 | while ((c = *src++) != '\0' && !is_dir_sep(c)) | ||
| 249 | *dst++ = c; | ||
| 250 | if (is_dir_sep(c)) { | ||
| 251 | *dst++ = '/'; | ||
| 252 | while (is_dir_sep(c)) | ||
| 253 | c = *src++; | ||
| 254 | src--; | ||
| 255 | } else if (!c) | ||
| 256 | break; | ||
| 257 | continue; | ||
| 258 | |||
| 259 | up_one: | ||
| 260 | /* | ||
| 261 | * dst0..dst is prefix portion, and dst[-1] is '/'; | ||
| 262 | * go up one level. | ||
| 263 | */ | ||
| 264 | dst--; /* go to trailing '/' */ | ||
| 265 | if (dst <= dst0) | ||
| 266 | return -1; | ||
| 267 | /* Windows: dst[-1] cannot be backslash anymore */ | ||
| 268 | while (dst0 < dst && dst[-1] != '/') | ||
| 269 | dst--; | ||
| 270 | } | ||
| 271 | *dst = '\0'; | ||
| 272 | return 0; | ||
| 273 | } | ||
| 274 | |||
| 275 | /* | ||
| 276 | * path = Canonical absolute path | ||
| 277 | * prefix_list = Colon-separated list of absolute paths | ||
| 278 | * | ||
| 279 | * Determines, for each path in prefix_list, whether the "prefix" really | ||
| 280 | * is an ancestor directory of path. Returns the length of the longest | ||
| 281 | * ancestor directory, excluding any trailing slashes, or -1 if no prefix | ||
| 282 | * is an ancestor. (Note that this means 0 is returned if prefix_list is | ||
| 283 | * "/".) "/foo" is not considered an ancestor of "/foobar". Directories | ||
| 284 | * are not considered to be their own ancestors. path must be in a | ||
| 285 | * canonical form: empty components, or "." or ".." components are not | ||
| 286 | * allowed. prefix_list may be null, which is like "". | ||
| 287 | */ | ||
| 288 | int longest_ancestor_length(const char *path, const char *prefix_list) | ||
| 289 | { | ||
| 290 | char buf[PATH_MAX+1]; | ||
| 291 | const char *ceil, *colon; | ||
| 292 | int len, max_len = -1; | ||
| 293 | |||
| 294 | if (prefix_list == NULL || !strcmp(path, "/")) | ||
| 295 | return -1; | ||
| 296 | |||
| 297 | for (colon = ceil = prefix_list; *colon; ceil = colon+1) { | ||
| 298 | for (colon = ceil; *colon && *colon != PATH_SEP; colon++); | ||
| 299 | len = colon - ceil; | ||
| 300 | if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil)) | ||
| 301 | continue; | ||
| 302 | strlcpy(buf, ceil, len+1); | ||
| 303 | if (normalize_path_copy(buf, buf) < 0) | ||
| 304 | continue; | ||
| 305 | len = strlen(buf); | ||
| 306 | if (len > 0 && buf[len-1] == '/') | ||
| 307 | buf[--len] = '\0'; | ||
| 308 | |||
| 309 | if (!strncmp(path, buf, len) && | ||
| 310 | path[len] == '/' && | ||
| 311 | len > max_len) { | ||
| 312 | max_len = len; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | return max_len; | ||
| 317 | } | ||
| 318 | |||
| 319 | /* strip arbitrary amount of directory separators at end of path */ | ||
| 320 | static inline int chomp_trailing_dir_sep(const char *path, int len) | ||
| 321 | { | ||
| 322 | while (len && is_dir_sep(path[len - 1])) | ||
| 323 | len--; | ||
| 324 | return len; | ||
| 325 | } | ||
| 326 | |||
| 327 | /* | ||
| 328 | * If path ends with suffix (complete path components), returns the | ||
| 329 | * part before suffix (sans trailing directory separators). | ||
| 330 | * Otherwise returns NULL. | ||
| 331 | */ | ||
| 332 | char *strip_path_suffix(const char *path, const char *suffix) | ||
| 333 | { | ||
| 334 | int path_len = strlen(path), suffix_len = strlen(suffix); | ||
| 335 | |||
| 336 | while (suffix_len) { | ||
| 337 | if (!path_len) | ||
| 338 | return NULL; | ||
| 339 | |||
| 340 | if (is_dir_sep(path[path_len - 1])) { | ||
| 341 | if (!is_dir_sep(suffix[suffix_len - 1])) | ||
| 342 | return NULL; | ||
| 343 | path_len = chomp_trailing_dir_sep(path, path_len); | ||
| 344 | suffix_len = chomp_trailing_dir_sep(suffix, suffix_len); | ||
| 345 | } | ||
| 346 | else if (path[--path_len] != suffix[--suffix_len]) | ||
| 347 | return NULL; | ||
| 348 | } | ||
| 349 | |||
| 350 | if (path_len && !is_dir_sep(path[path_len - 1])) | ||
| 351 | return NULL; | ||
| 352 | return xstrndup(path, chomp_trailing_dir_sep(path, path_len)); | ||
| 353 | } | ||
diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c new file mode 100644 index 000000000000..2726fe40eb5d --- /dev/null +++ b/tools/perf/util/quote.c | |||
| @@ -0,0 +1,485 @@ | |||
| 1 | #include "cache.h" | ||
| 2 | #include "quote.h" | ||
| 3 | |||
| 4 | int quote_path_fully = 1; | ||
| 5 | |||
| 6 | /* Help to copy the thing properly quoted for the shell safety. | ||
| 7 | * any single quote is replaced with '\'', any exclamation point | ||
| 8 | * is replaced with '\!', and the whole thing is enclosed in a | ||
| 9 | * | ||
| 10 | * E.g. | ||
| 11 | * original sq_quote result | ||
| 12 | * name ==> name ==> 'name' | ||
| 13 | * a b ==> a b ==> 'a b' | ||
| 14 | * a'b ==> a'\''b ==> 'a'\''b' | ||
| 15 | * a!b ==> a'\!'b ==> 'a'\!'b' | ||
| 16 | */ | ||
| 17 | static inline int need_bs_quote(char c) | ||
| 18 | { | ||
| 19 | return (c == '\'' || c == '!'); | ||
| 20 | } | ||
| 21 | |||
| 22 | void sq_quote_buf(struct strbuf *dst, const char *src) | ||
| 23 | { | ||
| 24 | char *to_free = NULL; | ||
| 25 | |||
| 26 | if (dst->buf == src) | ||
| 27 | to_free = strbuf_detach(dst, NULL); | ||
| 28 | |||
| 29 | strbuf_addch(dst, '\''); | ||
| 30 | while (*src) { | ||
| 31 | size_t len = strcspn(src, "'!"); | ||
| 32 | strbuf_add(dst, src, len); | ||
| 33 | src += len; | ||
| 34 | while (need_bs_quote(*src)) { | ||
| 35 | strbuf_addstr(dst, "'\\"); | ||
| 36 | strbuf_addch(dst, *src++); | ||
| 37 | strbuf_addch(dst, '\''); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | strbuf_addch(dst, '\''); | ||
| 41 | free(to_free); | ||
| 42 | } | ||
| 43 | |||
| 44 | void sq_quote_print(FILE *stream, const char *src) | ||
| 45 | { | ||
| 46 | char c; | ||
| 47 | |||
| 48 | fputc('\'', stream); | ||
| 49 | while ((c = *src++)) { | ||
| 50 | if (need_bs_quote(c)) { | ||
| 51 | fputs("'\\", stream); | ||
| 52 | fputc(c, stream); | ||
| 53 | fputc('\'', stream); | ||
| 54 | } else { | ||
| 55 | fputc(c, stream); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | fputc('\'', stream); | ||
| 59 | } | ||
| 60 | |||
| 61 | void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) | ||
| 62 | { | ||
| 63 | int i; | ||
| 64 | |||
| 65 | /* Copy into destination buffer. */ | ||
| 66 | strbuf_grow(dst, 255); | ||
| 67 | for (i = 0; argv[i]; ++i) { | ||
| 68 | strbuf_addch(dst, ' '); | ||
| 69 | sq_quote_buf(dst, argv[i]); | ||
| 70 | if (maxlen && dst->len > maxlen) | ||
| 71 | die("Too many or long arguments"); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | char *sq_dequote_step(char *arg, char **next) | ||
| 76 | { | ||
| 77 | char *dst = arg; | ||
| 78 | char *src = arg; | ||
| 79 | char c; | ||
| 80 | |||
| 81 | if (*src != '\'') | ||
| 82 | return NULL; | ||
| 83 | for (;;) { | ||
| 84 | c = *++src; | ||
| 85 | if (!c) | ||
| 86 | return NULL; | ||
| 87 | if (c != '\'') { | ||
| 88 | *dst++ = c; | ||
| 89 | continue; | ||
| 90 | } | ||
| 91 | /* We stepped out of sq */ | ||
| 92 | switch (*++src) { | ||
| 93 | case '\0': | ||
| 94 | *dst = 0; | ||
| 95 | if (next) | ||
| 96 | *next = NULL; | ||
| 97 | return arg; | ||
| 98 | case '\\': | ||
| 99 | c = *++src; | ||
| 100 | if (need_bs_quote(c) && *++src == '\'') { | ||
| 101 | *dst++ = c; | ||
| 102 | continue; | ||
| 103 | } | ||
| 104 | /* Fallthrough */ | ||
| 105 | default: | ||
| 106 | if (!next || !isspace(*src)) | ||
| 107 | return NULL; | ||
| 108 | do { | ||
| 109 | c = *++src; | ||
| 110 | } while (isspace(c)); | ||
| 111 | *dst = 0; | ||
| 112 | *next = src; | ||
| 113 | return arg; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | char *sq_dequote(char *arg) | ||
| 119 | { | ||
| 120 | return sq_dequote_step(arg, NULL); | ||
| 121 | } | ||
| 122 | |||
| 123 | int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc) | ||
| 124 | { | ||
| 125 | char *next = arg; | ||
| 126 | |||
| 127 | if (!*arg) | ||
| 128 | return 0; | ||
| 129 | do { | ||
| 130 | char *dequoted = sq_dequote_step(next, &next); | ||
| 131 | if (!dequoted) | ||
| 132 | return -1; | ||
| 133 | ALLOC_GROW(*argv, *nr + 1, *alloc); | ||
| 134 | (*argv)[(*nr)++] = dequoted; | ||
| 135 | } while (next); | ||
| 136 | |||
| 137 | return 0; | ||
| 138 | } | ||
| 139 | |||
| 140 | /* 1 means: quote as octal | ||
| 141 | * 0 means: quote as octal if (quote_path_fully) | ||
| 142 | * -1 means: never quote | ||
| 143 | * c: quote as "\\c" | ||
| 144 | */ | ||
| 145 | #define X8(x) x, x, x, x, x, x, x, x | ||
| 146 | #define X16(x) X8(x), X8(x) | ||
| 147 | static signed char const sq_lookup[256] = { | ||
| 148 | /* 0 1 2 3 4 5 6 7 */ | ||
| 149 | /* 0x00 */ 1, 1, 1, 1, 1, 1, 1, 'a', | ||
| 150 | /* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r', 1, 1, | ||
| 151 | /* 0x10 */ X16(1), | ||
| 152 | /* 0x20 */ -1, -1, '"', -1, -1, -1, -1, -1, | ||
| 153 | /* 0x28 */ X16(-1), X16(-1), X16(-1), | ||
| 154 | /* 0x58 */ -1, -1, -1, -1,'\\', -1, -1, -1, | ||
| 155 | /* 0x60 */ X16(-1), X8(-1), | ||
| 156 | /* 0x78 */ -1, -1, -1, -1, -1, -1, -1, 1, | ||
| 157 | /* 0x80 */ /* set to 0 */ | ||
| 158 | }; | ||
| 159 | |||
| 160 | static inline int sq_must_quote(char c) | ||
| 161 | { | ||
| 162 | return sq_lookup[(unsigned char)c] + quote_path_fully > 0; | ||
| 163 | } | ||
| 164 | |||
| 165 | /* | ||
| 166 | * Returns the longest prefix not needing a quote up to maxlen if | ||
| 167 | * positive. | ||
| 168 | * This stops at the first \0 because it's marked as a character | ||
| 169 | * needing an escape. | ||
| 170 | */ | ||
| 171 | static ssize_t next_quote_pos(const char *s, ssize_t maxlen) | ||
| 172 | { | ||
| 173 | ssize_t len; | ||
| 174 | |||
| 175 | if (maxlen < 0) { | ||
| 176 | for (len = 0; !sq_must_quote(s[len]); len++); | ||
| 177 | } else { | ||
| 178 | for (len = 0; len < maxlen && !sq_must_quote(s[len]); len++); | ||
| 179 | } | ||
| 180 | return len; | ||
| 181 | } | ||
| 182 | |||
| 183 | /* | ||
| 184 | * C-style name quoting. | ||
| 185 | * | ||
| 186 | * (1) if sb and fp are both NULL, inspect the input name and counts the | ||
| 187 | * number of bytes that are needed to hold c_style quoted version of name, | ||
| 188 | * counting the double quotes around it but not terminating NUL, and | ||
| 189 | * returns it. | ||
| 190 | * However, if name does not need c_style quoting, it returns 0. | ||
| 191 | * | ||
| 192 | * (2) if sb or fp are not NULL, it emits the c_style quoted version | ||
| 193 | * of name, enclosed with double quotes if asked and needed only. | ||
| 194 | * Return value is the same as in (1). | ||
| 195 | */ | ||
| 196 | static size_t quote_c_style_counted(const char *name, ssize_t maxlen, | ||
| 197 | struct strbuf *sb, FILE *fp, int no_dq) | ||
| 198 | { | ||
| 199 | #define EMIT(c) \ | ||
| 200 | do { \ | ||
| 201 | if (sb) strbuf_addch(sb, (c)); \ | ||
| 202 | if (fp) fputc((c), fp); \ | ||
| 203 | count++; \ | ||
| 204 | } while (0) | ||
| 205 | |||
| 206 | #define EMITBUF(s, l) \ | ||
| 207 | do { \ | ||
| 208 | int __ret; \ | ||
| 209 | if (sb) strbuf_add(sb, (s), (l)); \ | ||
| 210 | if (fp) __ret = fwrite((s), (l), 1, fp); \ | ||
| 211 | count += (l); \ | ||
| 212 | } while (0) | ||
| 213 | |||
| 214 | ssize_t len, count = 0; | ||
| 215 | const char *p = name; | ||
| 216 | |||
| 217 | for (;;) { | ||
| 218 | int ch; | ||
| 219 | |||
| 220 | len = next_quote_pos(p, maxlen); | ||
| 221 | if (len == maxlen || !p[len]) | ||
| 222 | break; | ||
| 223 | |||
| 224 | if (!no_dq && p == name) | ||
| 225 | EMIT('"'); | ||
| 226 | |||
| 227 | EMITBUF(p, len); | ||
| 228 | EMIT('\\'); | ||
| 229 | p += len; | ||
| 230 | ch = (unsigned char)*p++; | ||
| 231 | if (sq_lookup[ch] >= ' ') { | ||
| 232 | EMIT(sq_lookup[ch]); | ||
| 233 | } else { | ||
| 234 | EMIT(((ch >> 6) & 03) + '0'); | ||
| 235 | EMIT(((ch >> 3) & 07) + '0'); | ||
| 236 | EMIT(((ch >> 0) & 07) + '0'); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | EMITBUF(p, len); | ||
| 241 | if (p == name) /* no ending quote needed */ | ||
| 242 | return 0; | ||
| 243 | |||
| 244 | if (!no_dq) | ||
| 245 | EMIT('"'); | ||
| 246 | return count; | ||
| 247 | } | ||
| 248 | |||
| 249 | size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq) | ||
| 250 | { | ||
| 251 | return quote_c_style_counted(name, -1, sb, fp, nodq); | ||
| 252 | } | ||
| 253 | |||
| 254 | void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path, int nodq) | ||
| 255 | { | ||
| 256 | if (quote_c_style(prefix, NULL, NULL, 0) || | ||
| 257 | quote_c_style(path, NULL, NULL, 0)) { | ||
| 258 | if (!nodq) | ||
| 259 | strbuf_addch(sb, '"'); | ||
| 260 | quote_c_style(prefix, sb, NULL, 1); | ||
| 261 | quote_c_style(path, sb, NULL, 1); | ||
| 262 | if (!nodq) | ||
| 263 | strbuf_addch(sb, '"'); | ||
| 264 | } else { | ||
| 265 | strbuf_addstr(sb, prefix); | ||
| 266 | strbuf_addstr(sb, path); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | void write_name_quoted(const char *name, FILE *fp, int terminator) | ||
| 271 | { | ||
| 272 | if (terminator) { | ||
| 273 | quote_c_style(name, NULL, fp, 0); | ||
| 274 | } else { | ||
| 275 | fputs(name, fp); | ||
| 276 | } | ||
| 277 | fputc(terminator, fp); | ||
| 278 | } | ||
| 279 | |||
| 280 | void write_name_quotedpfx(const char *pfx, ssize_t pfxlen, | ||
| 281 | const char *name, FILE *fp, int terminator) | ||
| 282 | { | ||
| 283 | int needquote = 0; | ||
| 284 | |||
| 285 | if (terminator) { | ||
| 286 | needquote = next_quote_pos(pfx, pfxlen) < pfxlen | ||
| 287 | || name[next_quote_pos(name, -1)]; | ||
| 288 | } | ||
| 289 | if (needquote) { | ||
| 290 | fputc('"', fp); | ||
| 291 | quote_c_style_counted(pfx, pfxlen, NULL, fp, 1); | ||
| 292 | quote_c_style(name, NULL, fp, 1); | ||
| 293 | fputc('"', fp); | ||
| 294 | } else { | ||
| 295 | int ret; | ||
| 296 | |||
| 297 | ret = fwrite(pfx, pfxlen, 1, fp); | ||
| 298 | fputs(name, fp); | ||
| 299 | } | ||
| 300 | fputc(terminator, fp); | ||
| 301 | } | ||
| 302 | |||
| 303 | /* quote path as relative to the given prefix */ | ||
| 304 | char *quote_path_relative(const char *in, int len, | ||
| 305 | struct strbuf *out, const char *prefix) | ||
| 306 | { | ||
| 307 | int needquote; | ||
| 308 | |||
| 309 | if (len < 0) | ||
| 310 | len = strlen(in); | ||
| 311 | |||
| 312 | /* "../" prefix itself does not need quoting, but "in" might. */ | ||
| 313 | needquote = (next_quote_pos(in, len) < len); | ||
| 314 | strbuf_setlen(out, 0); | ||
| 315 | strbuf_grow(out, len); | ||
| 316 | |||
| 317 | if (needquote) | ||
| 318 | strbuf_addch(out, '"'); | ||
| 319 | if (prefix) { | ||
| 320 | int off = 0; | ||
| 321 | while (off < len && prefix[off] && prefix[off] == in[off]) | ||
| 322 | if (prefix[off] == '/') { | ||
| 323 | prefix += off + 1; | ||
| 324 | in += off + 1; | ||
| 325 | len -= off + 1; | ||
| 326 | off = 0; | ||
| 327 | } else | ||
| 328 | off++; | ||
| 329 | |||
| 330 | for (; *prefix; prefix++) | ||
| 331 | if (*prefix == '/') | ||
| 332 | strbuf_addstr(out, "../"); | ||
| 333 | } | ||
| 334 | |||
| 335 | quote_c_style_counted (in, len, out, NULL, 1); | ||
| 336 | |||
| 337 | if (needquote) | ||
| 338 | strbuf_addch(out, '"'); | ||
| 339 | if (!out->len) | ||
| 340 | strbuf_addstr(out, "./"); | ||
| 341 | |||
| 342 | return out->buf; | ||
| 343 | } | ||
| 344 | |||
| 345 | /* | ||
| 346 | * C-style name unquoting. | ||
| 347 | * | ||
| 348 | * Quoted should point at the opening double quote. | ||
| 349 | * + Returns 0 if it was able to unquote the string properly, and appends the | ||
| 350 | * result in the strbuf `sb'. | ||
| 351 | * + Returns -1 in case of error, and doesn't touch the strbuf. Though note | ||
| 352 | * that this function will allocate memory in the strbuf, so calling | ||
| 353 | * strbuf_release is mandatory whichever result unquote_c_style returns. | ||
| 354 | * | ||
| 355 | * Updates endp pointer to point at one past the ending double quote if given. | ||
| 356 | */ | ||
| 357 | int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp) | ||
| 358 | { | ||
| 359 | size_t oldlen = sb->len, len; | ||
| 360 | int ch, ac; | ||
| 361 | |||
| 362 | if (*quoted++ != '"') | ||
| 363 | return -1; | ||
| 364 | |||
| 365 | for (;;) { | ||
| 366 | len = strcspn(quoted, "\"\\"); | ||
| 367 | strbuf_add(sb, quoted, len); | ||
| 368 | quoted += len; | ||
| 369 | |||
| 370 | switch (*quoted++) { | ||
| 371 | case '"': | ||
| 372 | if (endp) | ||
| 373 | *endp = quoted; | ||
| 374 | return 0; | ||
| 375 | case '\\': | ||
| 376 | break; | ||
| 377 | default: | ||
| 378 | goto error; | ||
| 379 | } | ||
| 380 | |||
| 381 | switch ((ch = *quoted++)) { | ||
| 382 | case 'a': ch = '\a'; break; | ||
| 383 | case 'b': ch = '\b'; break; | ||
| 384 | case 'f': ch = '\f'; break; | ||
| 385 | case 'n': ch = '\n'; break; | ||
| 386 | case 'r': ch = '\r'; break; | ||
| 387 | case 't': ch = '\t'; break; | ||
| 388 | case 'v': ch = '\v'; break; | ||
| 389 | |||
| 390 | case '\\': case '"': | ||
| 391 | break; /* verbatim */ | ||
| 392 | |||
| 393 | /* octal values with first digit over 4 overflow */ | ||
| 394 | case '0': case '1': case '2': case '3': | ||
| 395 | ac = ((ch - '0') << 6); | ||
| 396 | if ((ch = *quoted++) < '0' || '7' < ch) | ||
| 397 | goto error; | ||
| 398 | ac |= ((ch - '0') << 3); | ||
| 399 | if ((ch = *quoted++) < '0' || '7' < ch) | ||
| 400 | goto error; | ||
| 401 | ac |= (ch - '0'); | ||
| 402 | ch = ac; | ||
| 403 | break; | ||
| 404 | default: | ||
| 405 | goto error; | ||
| 406 | } | ||
| 407 | strbuf_addch(sb, ch); | ||
| 408 | } | ||
| 409 | |||
| 410 | error: | ||
| 411 | strbuf_setlen(sb, oldlen); | ||
| 412 | return -1; | ||
| 413 | } | ||
| 414 | |||
| 415 | /* quoting as a string literal for other languages */ | ||
| 416 | |||
| 417 | void perl_quote_print(FILE *stream, const char *src) | ||
| 418 | { | ||
| 419 | const char sq = '\''; | ||
| 420 | const char bq = '\\'; | ||
| 421 | char c; | ||
| 422 | |||
| 423 | fputc(sq, stream); | ||
| 424 | while ((c = *src++)) { | ||
| 425 | if (c == sq || c == bq) | ||
| 426 | fputc(bq, stream); | ||
| 427 | fputc(c, stream); | ||
| 428 | } | ||
| 429 | fputc(sq, stream); | ||
| 430 | } | ||
| 431 | |||
| 432 | void python_quote_print(FILE *stream, const char *src) | ||
| 433 | { | ||
| 434 | const char sq = '\''; | ||
| 435 | const char bq = '\\'; | ||
| 436 | const char nl = '\n'; | ||
| 437 | char c; | ||
| 438 | |||
| 439 | fputc(sq, stream); | ||
| 440 | while ((c = *src++)) { | ||
| 441 | if (c == nl) { | ||
| 442 | fputc(bq, stream); | ||
| 443 | fputc('n', stream); | ||
| 444 | continue; | ||
| 445 | } | ||
| 446 | if (c == sq || c == bq) | ||
| 447 | fputc(bq, stream); | ||
| 448 | fputc(c, stream); | ||
| 449 | } | ||
| 450 | fputc(sq, stream); | ||
| 451 | } | ||
| 452 | |||
| 453 | void tcl_quote_print(FILE *stream, const char *src) | ||
| 454 | { | ||
| 455 | char c; | ||
| 456 | |||
| 457 | fputc('"', stream); | ||
| 458 | while ((c = *src++)) { | ||
| 459 | switch (c) { | ||
| 460 | case '[': case ']': | ||
| 461 | case '{': case '}': | ||
| 462 | case '$': case '\\': case '"': | ||
| 463 | fputc('\\', stream); | ||
| 464 | default: | ||
| 465 | fputc(c, stream); | ||
| 466 | break; | ||
| 467 | case '\f': | ||
| 468 | fputs("\\f", stream); | ||
| 469 | break; | ||
| 470 | case '\r': | ||
| 471 | fputs("\\r", stream); | ||
| 472 | break; | ||
| 473 | case '\n': | ||
| 474 | fputs("\\n", stream); | ||
| 475 | break; | ||
| 476 | case '\t': | ||
| 477 | fputs("\\t", stream); | ||
| 478 | break; | ||
| 479 | case '\v': | ||
| 480 | fputs("\\v", stream); | ||
| 481 | break; | ||
| 482 | } | ||
| 483 | } | ||
| 484 | fputc('"', stream); | ||
| 485 | } | ||
diff --git a/tools/perf/util/quote.h b/tools/perf/util/quote.h new file mode 100644 index 000000000000..a5454a1d1c13 --- /dev/null +++ b/tools/perf/util/quote.h | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | #ifndef QUOTE_H | ||
| 2 | #define QUOTE_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | |||
| 7 | /* Help to copy the thing properly quoted for the shell safety. | ||
| 8 | * any single quote is replaced with '\'', any exclamation point | ||
| 9 | * is replaced with '\!', and the whole thing is enclosed in a | ||
| 10 | * single quote pair. | ||
| 11 | * | ||
| 12 | * For example, if you are passing the result to system() as an | ||
| 13 | * argument: | ||
| 14 | * | ||
| 15 | * sprintf(cmd, "foobar %s %s", sq_quote(arg0), sq_quote(arg1)) | ||
| 16 | * | ||
| 17 | * would be appropriate. If the system() is going to call ssh to | ||
| 18 | * run the command on the other side: | ||
| 19 | * | ||
| 20 | * sprintf(cmd, "git-diff-tree %s %s", sq_quote(arg0), sq_quote(arg1)); | ||
| 21 | * sprintf(rcmd, "ssh %s %s", sq_util/quote.host), sq_quote(cmd)); | ||
| 22 | * | ||
| 23 | * Note that the above examples leak memory! Remember to free result from | ||
| 24 | * sq_quote() in a real application. | ||
| 25 | * | ||
| 26 | * sq_quote_buf() writes to an existing buffer of specified size; it | ||
| 27 | * will return the number of characters that would have been written | ||
| 28 | * excluding the final null regardless of the buffer size. | ||
| 29 | */ | ||
| 30 | |||
| 31 | extern void sq_quote_print(FILE *stream, const char *src); | ||
| 32 | |||
| 33 | extern void sq_quote_buf(struct strbuf *, const char *src); | ||
| 34 | extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen); | ||
| 35 | |||
| 36 | /* This unwraps what sq_quote() produces in place, but returns | ||
| 37 | * NULL if the input does not look like what sq_quote would have | ||
| 38 | * produced. | ||
| 39 | */ | ||
| 40 | extern char *sq_dequote(char *); | ||
| 41 | |||
| 42 | /* | ||
| 43 | * Same as the above, but can be used to unwrap many arguments in the | ||
| 44 | * same string separated by space. "next" is changed to point to the | ||
| 45 | * next argument that should be passed as first parameter. When there | ||
| 46 | * is no more argument to be dequoted, "next" is updated to point to NULL. | ||
| 47 | */ | ||
| 48 | extern char *sq_dequote_step(char *arg, char **next); | ||
| 49 | extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc); | ||
| 50 | |||
| 51 | extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp); | ||
| 52 | extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq); | ||
| 53 | extern void quote_two_c_style(struct strbuf *, const char *, const char *, int); | ||
| 54 | |||
| 55 | extern void write_name_quoted(const char *name, FILE *, int terminator); | ||
| 56 | extern void write_name_quotedpfx(const char *pfx, ssize_t pfxlen, | ||
| 57 | const char *name, FILE *, int terminator); | ||
| 58 | |||
| 59 | /* quote path as relative to the given prefix */ | ||
| 60 | char *quote_path_relative(const char *in, int len, | ||
| 61 | struct strbuf *out, const char *prefix); | ||
| 62 | |||
| 63 | /* quoting as a string literal for other languages */ | ||
| 64 | extern void perl_quote_print(FILE *stream, const char *src); | ||
| 65 | extern void python_quote_print(FILE *stream, const char *src); | ||
| 66 | extern void tcl_quote_print(FILE *stream, const char *src); | ||
| 67 | |||
| 68 | #endif | ||
diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c new file mode 100644 index 000000000000..a3935343091a --- /dev/null +++ b/tools/perf/util/run-command.c | |||
| @@ -0,0 +1,304 @@ | |||
| 1 | #include "cache.h" | ||
| 2 | #include "run-command.h" | ||
| 3 | #include "exec_cmd.h" | ||
| 4 | |||
| 5 | static inline void close_pair(int fd[2]) | ||
| 6 | { | ||
| 7 | close(fd[0]); | ||
| 8 | close(fd[1]); | ||
| 9 | } | ||
| 10 | |||
| 11 | static inline void dup_devnull(int to) | ||
| 12 | { | ||
| 13 | int fd = open("/dev/null", O_RDWR); | ||
| 14 | dup2(fd, to); | ||
| 15 | close(fd); | ||
| 16 | } | ||
| 17 | |||
| 18 | int start_command(struct child_process *cmd) | ||
| 19 | { | ||
| 20 | int need_in, need_out, need_err; | ||
| 21 | int fdin[2], fdout[2], fderr[2]; | ||
| 22 | |||
| 23 | /* | ||
| 24 | * In case of errors we must keep the promise to close FDs | ||
| 25 | * that have been passed in via ->in and ->out. | ||
| 26 | */ | ||
| 27 | |||
| 28 | need_in = !cmd->no_stdin && cmd->in < 0; | ||
| 29 | if (need_in) { | ||
| 30 | if (pipe(fdin) < 0) { | ||
| 31 | if (cmd->out > 0) | ||
| 32 | close(cmd->out); | ||
| 33 | return -ERR_RUN_COMMAND_PIPE; | ||
| 34 | } | ||
| 35 | cmd->in = fdin[1]; | ||
| 36 | } | ||
| 37 | |||
| 38 | need_out = !cmd->no_stdout | ||
| 39 | && !cmd->stdout_to_stderr | ||
| 40 | && cmd->out < 0; | ||
| 41 | if (need_out) { | ||
| 42 | if (pipe(fdout) < 0) { | ||
| 43 | if (need_in) | ||
| 44 | close_pair(fdin); | ||
| 45 | else if (cmd->in) | ||
| 46 | close(cmd->in); | ||
| 47 | return -ERR_RUN_COMMAND_PIPE; | ||
| 48 | } | ||
| 49 | cmd->out = fdout[0]; | ||
| 50 | } | ||
| 51 | |||
| 52 | need_err = !cmd->no_stderr && cmd->err < 0; | ||
| 53 | if (need_err) { | ||
| 54 | if (pipe(fderr) < 0) { | ||
| 55 | if (need_in) | ||
| 56 | close_pair(fdin); | ||
| 57 | else if (cmd->in) | ||
| 58 | close(cmd->in); | ||
| 59 | if (need_out) | ||
| 60 | close_pair(fdout); | ||
| 61 | else if (cmd->out) | ||
| 62 | close(cmd->out); | ||
| 63 | return -ERR_RUN_COMMAND_PIPE; | ||
| 64 | } | ||
| 65 | cmd->err = fderr[0]; | ||
| 66 | } | ||
| 67 | |||
| 68 | fflush(NULL); | ||
| 69 | cmd->pid = fork(); | ||
| 70 | if (!cmd->pid) { | ||
| 71 | if (cmd->no_stdin) | ||
| 72 | dup_devnull(0); | ||
| 73 | else if (need_in) { | ||
| 74 | dup2(fdin[0], 0); | ||
| 75 | close_pair(fdin); | ||
| 76 | } else if (cmd->in) { | ||
| 77 | dup2(cmd->in, 0); | ||
| 78 | close(cmd->in); | ||
| 79 | } | ||
| 80 | |||
| 81 | if (cmd->no_stderr) | ||
| 82 | dup_devnull(2); | ||
| 83 | else if (need_err) { | ||
| 84 | dup2(fderr[1], 2); | ||
| 85 | close_pair(fderr); | ||
| 86 | } | ||
| 87 | |||
| 88 | if (cmd->no_stdout) | ||
| 89 | dup_devnull(1); | ||
| 90 | else if (cmd->stdout_to_stderr) | ||
| 91 | dup2(2, 1); | ||
| 92 | else if (need_out) { | ||
| 93 | dup2(fdout[1], 1); | ||
| 94 | close_pair(fdout); | ||
| 95 | } else if (cmd->out > 1) { | ||
| 96 | dup2(cmd->out, 1); | ||
| 97 | close(cmd->out); | ||
| 98 | } | ||
| 99 | |||
| 100 | if (cmd->dir && chdir(cmd->dir)) | ||
| 101 | die("exec %s: cd to %s failed (%s)", cmd->argv[0], | ||
| 102 | cmd->dir, strerror(errno)); | ||
| 103 | if (cmd->env) { | ||
| 104 | for (; *cmd->env; cmd->env++) { | ||
| 105 | if (strchr(*cmd->env, '=')) | ||
| 106 | putenv((char*)*cmd->env); | ||
| 107 | else | ||
| 108 | unsetenv(*cmd->env); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | if (cmd->preexec_cb) | ||
| 112 | cmd->preexec_cb(); | ||
| 113 | if (cmd->perf_cmd) { | ||
| 114 | execv_perf_cmd(cmd->argv); | ||
| 115 | } else { | ||
| 116 | execvp(cmd->argv[0], (char *const*) cmd->argv); | ||
| 117 | } | ||
| 118 | exit(127); | ||
| 119 | } | ||
| 120 | |||
| 121 | if (cmd->pid < 0) { | ||
| 122 | int err = errno; | ||
| 123 | if (need_in) | ||
| 124 | close_pair(fdin); | ||
| 125 | else if (cmd->in) | ||
| 126 | close(cmd->in); | ||
| 127 | if (need_out) | ||
| 128 | close_pair(fdout); | ||
| 129 | else if (cmd->out) | ||
| 130 | close(cmd->out); | ||
| 131 | if (need_err) | ||
| 132 | close_pair(fderr); | ||
| 133 | return err == ENOENT ? | ||
| 134 | -ERR_RUN_COMMAND_EXEC : | ||
| 135 | -ERR_RUN_COMMAND_FORK; | ||
| 136 | } | ||
| 137 | |||
| 138 | if (need_in) | ||
| 139 | close(fdin[0]); | ||
| 140 | else if (cmd->in) | ||
| 141 | close(cmd->in); | ||
| 142 | |||
| 143 | if (need_out) | ||
| 144 | close(fdout[1]); | ||
| 145 | else if (cmd->out) | ||
| 146 | close(cmd->out); | ||
| 147 | |||
| 148 | if (need_err) | ||
| 149 | close(fderr[1]); | ||
| 150 | |||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | |||
| 154 | static int wait_or_whine(pid_t pid) | ||
| 155 | { | ||
| 156 | for (;;) { | ||
| 157 | int status, code; | ||
| 158 | pid_t waiting = waitpid(pid, &status, 0); | ||
| 159 | |||
| 160 | if (waiting < 0) { | ||
| 161 | if (errno == EINTR) | ||
| 162 | continue; | ||
| 163 | error("waitpid failed (%s)", strerror(errno)); | ||
| 164 | return -ERR_RUN_COMMAND_WAITPID; | ||
| 165 | } | ||
| 166 | if (waiting != pid) | ||
| 167 | return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; | ||
| 168 | if (WIFSIGNALED(status)) | ||
| 169 | return -ERR_RUN_COMMAND_WAITPID_SIGNAL; | ||
| 170 | |||
| 171 | if (!WIFEXITED(status)) | ||
| 172 | return -ERR_RUN_COMMAND_WAITPID_NOEXIT; | ||
| 173 | code = WEXITSTATUS(status); | ||
| 174 | switch (code) { | ||
| 175 | case 127: | ||
| 176 | return -ERR_RUN_COMMAND_EXEC; | ||
| 177 | case 0: | ||
| 178 | return 0; | ||
| 179 | default: | ||
| 180 | return -code; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | int finish_command(struct child_process *cmd) | ||
| 186 | { | ||
| 187 | return wait_or_whine(cmd->pid); | ||
| 188 | } | ||
| 189 | |||
| 190 | int run_command(struct child_process *cmd) | ||
| 191 | { | ||
| 192 | int code = start_command(cmd); | ||
| 193 | if (code) | ||
| 194 | return code; | ||
| 195 | return finish_command(cmd); | ||
| 196 | } | ||
| 197 | |||
| 198 | static void prepare_run_command_v_opt(struct child_process *cmd, | ||
| 199 | const char **argv, | ||
| 200 | int opt) | ||
| 201 | { | ||
| 202 | memset(cmd, 0, sizeof(*cmd)); | ||
| 203 | cmd->argv = argv; | ||
| 204 | cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; | ||
| 205 | cmd->perf_cmd = opt & RUN_PERF_CMD ? 1 : 0; | ||
| 206 | cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; | ||
| 207 | } | ||
| 208 | |||
| 209 | int run_command_v_opt(const char **argv, int opt) | ||
| 210 | { | ||
| 211 | struct child_process cmd; | ||
| 212 | prepare_run_command_v_opt(&cmd, argv, opt); | ||
| 213 | return run_command(&cmd); | ||
| 214 | } | ||
| 215 | |||
| 216 | int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env) | ||
| 217 | { | ||
| 218 | struct child_process cmd; | ||
| 219 | prepare_run_command_v_opt(&cmd, argv, opt); | ||
| 220 | cmd.dir = dir; | ||
| 221 | cmd.env = env; | ||
| 222 | return run_command(&cmd); | ||
| 223 | } | ||
| 224 | |||
| 225 | int start_async(struct async *async) | ||
| 226 | { | ||
| 227 | int pipe_out[2]; | ||
| 228 | |||
| 229 | if (pipe(pipe_out) < 0) | ||
| 230 | return error("cannot create pipe: %s", strerror(errno)); | ||
| 231 | async->out = pipe_out[0]; | ||
| 232 | |||
| 233 | /* Flush stdio before fork() to avoid cloning buffers */ | ||
| 234 | fflush(NULL); | ||
| 235 | |||
| 236 | async->pid = fork(); | ||
| 237 | if (async->pid < 0) { | ||
| 238 | error("fork (async) failed: %s", strerror(errno)); | ||
| 239 | close_pair(pipe_out); | ||
| 240 | return -1; | ||
| 241 | } | ||
| 242 | if (!async->pid) { | ||
| 243 | close(pipe_out[0]); | ||
| 244 | exit(!!async->proc(pipe_out[1], async->data)); | ||
| 245 | } | ||
| 246 | close(pipe_out[1]); | ||
| 247 | |||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | |||
| 251 | int finish_async(struct async *async) | ||
| 252 | { | ||
| 253 | int ret = 0; | ||
| 254 | |||
| 255 | if (wait_or_whine(async->pid)) | ||
| 256 | ret = error("waitpid (async) failed"); | ||
| 257 | |||
| 258 | return ret; | ||
| 259 | } | ||
| 260 | |||
| 261 | int run_hook(const char *index_file, const char *name, ...) | ||
| 262 | { | ||
| 263 | struct child_process hook; | ||
| 264 | const char **argv = NULL, *env[2]; | ||
| 265 | char index[PATH_MAX]; | ||
| 266 | va_list args; | ||
| 267 | int ret; | ||
| 268 | size_t i = 0, alloc = 0; | ||
| 269 | |||
| 270 | if (access(perf_path("hooks/%s", name), X_OK) < 0) | ||
| 271 | return 0; | ||
| 272 | |||
| 273 | va_start(args, name); | ||
| 274 | ALLOC_GROW(argv, i + 1, alloc); | ||
| 275 | argv[i++] = perf_path("hooks/%s", name); | ||
| 276 | while (argv[i-1]) { | ||
| 277 | ALLOC_GROW(argv, i + 1, alloc); | ||
| 278 | argv[i++] = va_arg(args, const char *); | ||
| 279 | } | ||
| 280 | va_end(args); | ||
| 281 | |||
| 282 | memset(&hook, 0, sizeof(hook)); | ||
| 283 | hook.argv = argv; | ||
| 284 | hook.no_stdin = 1; | ||
| 285 | hook.stdout_to_stderr = 1; | ||
| 286 | if (index_file) { | ||
| 287 | snprintf(index, sizeof(index), "PERF_INDEX_FILE=%s", index_file); | ||
| 288 | env[0] = index; | ||
| 289 | env[1] = NULL; | ||
| 290 | hook.env = env; | ||
| 291 | } | ||
| 292 | |||
| 293 | ret = start_command(&hook); | ||
| 294 | free(argv); | ||
| 295 | if (ret) { | ||
| 296 | warning("Could not spawn %s", argv[0]); | ||
| 297 | return ret; | ||
| 298 | } | ||
| 299 | ret = finish_command(&hook); | ||
| 300 | if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL) | ||
| 301 | warning("%s exited due to uncaught signal", argv[0]); | ||
| 302 | |||
| 303 | return ret; | ||
| 304 | } | ||
diff --git a/tools/perf/util/run-command.h b/tools/perf/util/run-command.h new file mode 100644 index 000000000000..cc1837deba88 --- /dev/null +++ b/tools/perf/util/run-command.h | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | #ifndef RUN_COMMAND_H | ||
| 2 | #define RUN_COMMAND_H | ||
| 3 | |||
| 4 | enum { | ||
| 5 | ERR_RUN_COMMAND_FORK = 10000, | ||
| 6 | ERR_RUN_COMMAND_EXEC, | ||
| 7 | ERR_RUN_COMMAND_PIPE, | ||
| 8 | ERR_RUN_COMMAND_WAITPID, | ||
| 9 | ERR_RUN_COMMAND_WAITPID_WRONG_PID, | ||
| 10 | ERR_RUN_COMMAND_WAITPID_SIGNAL, | ||
| 11 | ERR_RUN_COMMAND_WAITPID_NOEXIT, | ||
| 12 | }; | ||
| 13 | #define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK) | ||
| 14 | |||
| 15 | struct child_process { | ||
| 16 | const char **argv; | ||
| 17 | pid_t pid; | ||
| 18 | /* | ||
| 19 | * Using .in, .out, .err: | ||
| 20 | * - Specify 0 for no redirections (child inherits stdin, stdout, | ||
| 21 | * stderr from parent). | ||
| 22 | * - Specify -1 to have a pipe allocated as follows: | ||
| 23 | * .in: returns the writable pipe end; parent writes to it, | ||
| 24 | * the readable pipe end becomes child's stdin | ||
| 25 | * .out, .err: returns the readable pipe end; parent reads from | ||
| 26 | * it, the writable pipe end becomes child's stdout/stderr | ||
| 27 | * The caller of start_command() must close the returned FDs | ||
| 28 | * after it has completed reading from/writing to it! | ||
| 29 | * - Specify > 0 to set a channel to a particular FD as follows: | ||
| 30 | * .in: a readable FD, becomes child's stdin | ||
| 31 | * .out: a writable FD, becomes child's stdout/stderr | ||
| 32 | * .err > 0 not supported | ||
| 33 | * The specified FD is closed by start_command(), even in case | ||
| 34 | * of errors! | ||
| 35 | */ | ||
| 36 | int in; | ||
| 37 | int out; | ||
| 38 | int err; | ||
| 39 | const char *dir; | ||
| 40 | const char *const *env; | ||
| 41 | unsigned no_stdin:1; | ||
| 42 | unsigned no_stdout:1; | ||
| 43 | unsigned no_stderr:1; | ||
| 44 | unsigned perf_cmd:1; /* if this is to be perf sub-command */ | ||
| 45 | unsigned stdout_to_stderr:1; | ||
| 46 | void (*preexec_cb)(void); | ||
| 47 | }; | ||
| 48 | |||
| 49 | int start_command(struct child_process *); | ||
| 50 | int finish_command(struct child_process *); | ||
| 51 | int run_command(struct child_process *); | ||
| 52 | |||
| 53 | extern int run_hook(const char *index_file, const char *name, ...); | ||
| 54 | |||
| 55 | #define RUN_COMMAND_NO_STDIN 1 | ||
| 56 | #define RUN_PERF_CMD 2 /*If this is to be perf sub-command */ | ||
| 57 | #define RUN_COMMAND_STDOUT_TO_STDERR 4 | ||
| 58 | int run_command_v_opt(const char **argv, int opt); | ||
| 59 | |||
| 60 | /* | ||
| 61 | * env (the environment) is to be formatted like environ: "VAR=VALUE". | ||
| 62 | * To unset an environment variable use just "VAR". | ||
| 63 | */ | ||
| 64 | int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env); | ||
| 65 | |||
| 66 | /* | ||
| 67 | * The purpose of the following functions is to feed a pipe by running | ||
| 68 | * a function asynchronously and providing output that the caller reads. | ||
| 69 | * | ||
| 70 | * It is expected that no synchronization and mutual exclusion between | ||
| 71 | * the caller and the feed function is necessary so that the function | ||
| 72 | * can run in a thread without interfering with the caller. | ||
| 73 | */ | ||
| 74 | struct async { | ||
| 75 | /* | ||
| 76 | * proc writes to fd and closes it; | ||
| 77 | * returns 0 on success, non-zero on failure | ||
| 78 | */ | ||
| 79 | int (*proc)(int fd, void *data); | ||
| 80 | void *data; | ||
| 81 | int out; /* caller reads from here and closes it */ | ||
| 82 | pid_t pid; | ||
| 83 | }; | ||
| 84 | |||
| 85 | int start_async(struct async *async); | ||
| 86 | int finish_async(struct async *async); | ||
| 87 | |||
| 88 | #endif | ||
diff --git a/tools/perf/util/sigchain.c b/tools/perf/util/sigchain.c new file mode 100644 index 000000000000..1118b99e57d3 --- /dev/null +++ b/tools/perf/util/sigchain.c | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | #include "sigchain.h" | ||
| 2 | #include "cache.h" | ||
| 3 | |||
| 4 | #define SIGCHAIN_MAX_SIGNALS 32 | ||
| 5 | |||
| 6 | struct sigchain_signal { | ||
| 7 | sigchain_fun *old; | ||
| 8 | int n; | ||
| 9 | int alloc; | ||
| 10 | }; | ||
| 11 | static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS]; | ||
| 12 | |||
| 13 | static void check_signum(int sig) | ||
| 14 | { | ||
| 15 | if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS) | ||
| 16 | die("BUG: signal out of range: %d", sig); | ||
| 17 | } | ||
| 18 | |||
| 19 | int sigchain_push(int sig, sigchain_fun f) | ||
| 20 | { | ||
| 21 | struct sigchain_signal *s = signals + sig; | ||
| 22 | check_signum(sig); | ||
| 23 | |||
| 24 | ALLOC_GROW(s->old, s->n + 1, s->alloc); | ||
| 25 | s->old[s->n] = signal(sig, f); | ||
| 26 | if (s->old[s->n] == SIG_ERR) | ||
| 27 | return -1; | ||
| 28 | s->n++; | ||
| 29 | return 0; | ||
| 30 | } | ||
| 31 | |||
| 32 | int sigchain_pop(int sig) | ||
| 33 | { | ||
| 34 | struct sigchain_signal *s = signals + sig; | ||
| 35 | check_signum(sig); | ||
| 36 | if (s->n < 1) | ||
| 37 | return 0; | ||
| 38 | |||
| 39 | if (signal(sig, s->old[s->n - 1]) == SIG_ERR) | ||
| 40 | return -1; | ||
| 41 | s->n--; | ||
| 42 | return 0; | ||
| 43 | } | ||
| 44 | |||
| 45 | void sigchain_push_common(sigchain_fun f) | ||
| 46 | { | ||
| 47 | sigchain_push(SIGINT, f); | ||
| 48 | sigchain_push(SIGHUP, f); | ||
| 49 | sigchain_push(SIGTERM, f); | ||
| 50 | sigchain_push(SIGQUIT, f); | ||
| 51 | sigchain_push(SIGPIPE, f); | ||
| 52 | } | ||
diff --git a/tools/perf/util/sigchain.h b/tools/perf/util/sigchain.h new file mode 100644 index 000000000000..618083bce0c6 --- /dev/null +++ b/tools/perf/util/sigchain.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #ifndef SIGCHAIN_H | ||
| 2 | #define SIGCHAIN_H | ||
| 3 | |||
| 4 | typedef void (*sigchain_fun)(int); | ||
| 5 | |||
| 6 | int sigchain_push(int sig, sigchain_fun f); | ||
| 7 | int sigchain_pop(int sig); | ||
| 8 | |||
| 9 | void sigchain_push_common(sigchain_fun f); | ||
| 10 | |||
| 11 | #endif /* SIGCHAIN_H */ | ||
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c new file mode 100644 index 000000000000..5249d5a1b0c2 --- /dev/null +++ b/tools/perf/util/strbuf.c | |||
| @@ -0,0 +1,360 @@ | |||
| 1 | #include "cache.h" | ||
| 2 | |||
| 3 | int prefixcmp(const char *str, const char *prefix) | ||
| 4 | { | ||
| 5 | for (; ; str++, prefix++) | ||
| 6 | if (!*prefix) | ||
| 7 | return 0; | ||
| 8 | else if (*str != *prefix) | ||
| 9 | return (unsigned char)*prefix - (unsigned char)*str; | ||
| 10 | } | ||
| 11 | |||
| 12 | /* | ||
| 13 | * Used as the default ->buf value, so that people can always assume | ||
| 14 | * buf is non NULL and ->buf is NUL terminated even for a freshly | ||
| 15 | * initialized strbuf. | ||
| 16 | */ | ||
| 17 | char strbuf_slopbuf[1]; | ||
| 18 | |||
| 19 | void strbuf_init(struct strbuf *sb, ssize_t hint) | ||
| 20 | { | ||
| 21 | sb->alloc = sb->len = 0; | ||
| 22 | sb->buf = strbuf_slopbuf; | ||
| 23 | if (hint) | ||
| 24 | strbuf_grow(sb, hint); | ||
| 25 | } | ||
| 26 | |||
| 27 | void strbuf_release(struct strbuf *sb) | ||
| 28 | { | ||
| 29 | if (sb->alloc) { | ||
| 30 | free(sb->buf); | ||
| 31 | strbuf_init(sb, 0); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | char *strbuf_detach(struct strbuf *sb, size_t *sz) | ||
| 36 | { | ||
| 37 | char *res = sb->alloc ? sb->buf : NULL; | ||
| 38 | if (sz) | ||
| 39 | *sz = sb->len; | ||
| 40 | strbuf_init(sb, 0); | ||
| 41 | return res; | ||
| 42 | } | ||
| 43 | |||
| 44 | void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc) | ||
| 45 | { | ||
| 46 | strbuf_release(sb); | ||
| 47 | sb->buf = buf; | ||
| 48 | sb->len = len; | ||
| 49 | sb->alloc = alloc; | ||
| 50 | strbuf_grow(sb, 0); | ||
| 51 | sb->buf[sb->len] = '\0'; | ||
| 52 | } | ||
| 53 | |||
| 54 | void strbuf_grow(struct strbuf *sb, size_t extra) | ||
| 55 | { | ||
| 56 | if (sb->len + extra + 1 <= sb->len) | ||
| 57 | die("you want to use way too much memory"); | ||
| 58 | if (!sb->alloc) | ||
| 59 | sb->buf = NULL; | ||
| 60 | ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); | ||
| 61 | } | ||
| 62 | |||
| 63 | void strbuf_trim(struct strbuf *sb) | ||
| 64 | { | ||
| 65 | char *b = sb->buf; | ||
| 66 | while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1])) | ||
| 67 | sb->len--; | ||
| 68 | while (sb->len > 0 && isspace(*b)) { | ||
| 69 | b++; | ||
| 70 | sb->len--; | ||
| 71 | } | ||
| 72 | memmove(sb->buf, b, sb->len); | ||
| 73 | sb->buf[sb->len] = '\0'; | ||
| 74 | } | ||
| 75 | void strbuf_rtrim(struct strbuf *sb) | ||
| 76 | { | ||
| 77 | while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1])) | ||
| 78 | sb->len--; | ||
| 79 | sb->buf[sb->len] = '\0'; | ||
| 80 | } | ||
| 81 | |||
| 82 | void strbuf_ltrim(struct strbuf *sb) | ||
| 83 | { | ||
| 84 | char *b = sb->buf; | ||
| 85 | while (sb->len > 0 && isspace(*b)) { | ||
| 86 | b++; | ||
| 87 | sb->len--; | ||
| 88 | } | ||
| 89 | memmove(sb->buf, b, sb->len); | ||
| 90 | sb->buf[sb->len] = '\0'; | ||
| 91 | } | ||
| 92 | |||
| 93 | void strbuf_tolower(struct strbuf *sb) | ||
| 94 | { | ||
| 95 | unsigned int i; | ||
| 96 | |||
| 97 | for (i = 0; i < sb->len; i++) | ||
| 98 | sb->buf[i] = tolower(sb->buf[i]); | ||
| 99 | } | ||
| 100 | |||
| 101 | struct strbuf **strbuf_split(const struct strbuf *sb, int delim) | ||
| 102 | { | ||
| 103 | int alloc = 2, pos = 0; | ||
| 104 | char *n, *p; | ||
| 105 | struct strbuf **ret; | ||
| 106 | struct strbuf *t; | ||
| 107 | |||
| 108 | ret = calloc(alloc, sizeof(struct strbuf *)); | ||
| 109 | p = n = sb->buf; | ||
| 110 | while (n < sb->buf + sb->len) { | ||
| 111 | int len; | ||
| 112 | n = memchr(n, delim, sb->len - (n - sb->buf)); | ||
| 113 | if (pos + 1 >= alloc) { | ||
| 114 | alloc = alloc * 2; | ||
| 115 | ret = realloc(ret, sizeof(struct strbuf *) * alloc); | ||
| 116 | } | ||
| 117 | if (!n) | ||
| 118 | n = sb->buf + sb->len - 1; | ||
| 119 | len = n - p + 1; | ||
| 120 | t = malloc(sizeof(struct strbuf)); | ||
| 121 | strbuf_init(t, len); | ||
| 122 | strbuf_add(t, p, len); | ||
| 123 | ret[pos] = t; | ||
| 124 | ret[++pos] = NULL; | ||
| 125 | p = ++n; | ||
| 126 | } | ||
| 127 | return ret; | ||
| 128 | } | ||
| 129 | |||
| 130 | void strbuf_list_free(struct strbuf **sbs) | ||
| 131 | { | ||
| 132 | struct strbuf **s = sbs; | ||
| 133 | |||
| 134 | while (*s) { | ||
| 135 | strbuf_release(*s); | ||
| 136 | free(*s++); | ||
| 137 | } | ||
| 138 | free(sbs); | ||
| 139 | } | ||
| 140 | |||
| 141 | int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) | ||
| 142 | { | ||
| 143 | int len = a->len < b->len ? a->len: b->len; | ||
| 144 | int cmp = memcmp(a->buf, b->buf, len); | ||
| 145 | if (cmp) | ||
| 146 | return cmp; | ||
| 147 | return a->len < b->len ? -1: a->len != b->len; | ||
| 148 | } | ||
| 149 | |||
| 150 | void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, | ||
| 151 | const void *data, size_t dlen) | ||
| 152 | { | ||
| 153 | if (pos + len < pos) | ||
| 154 | die("you want to use way too much memory"); | ||
| 155 | if (pos > sb->len) | ||
| 156 | die("`pos' is too far after the end of the buffer"); | ||
| 157 | if (pos + len > sb->len) | ||
| 158 | die("`pos + len' is too far after the end of the buffer"); | ||
| 159 | |||
| 160 | if (dlen >= len) | ||
| 161 | strbuf_grow(sb, dlen - len); | ||
| 162 | memmove(sb->buf + pos + dlen, | ||
| 163 | sb->buf + pos + len, | ||
| 164 | sb->len - pos - len); | ||
| 165 | memcpy(sb->buf + pos, data, dlen); | ||
| 166 | strbuf_setlen(sb, sb->len + dlen - len); | ||
| 167 | } | ||
| 168 | |||
| 169 | void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) | ||
| 170 | { | ||
| 171 | strbuf_splice(sb, pos, 0, data, len); | ||
| 172 | } | ||
| 173 | |||
| 174 | void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) | ||
| 175 | { | ||
| 176 | strbuf_splice(sb, pos, len, NULL, 0); | ||
| 177 | } | ||
| 178 | |||
| 179 | void strbuf_add(struct strbuf *sb, const void *data, size_t len) | ||
| 180 | { | ||
| 181 | strbuf_grow(sb, len); | ||
| 182 | memcpy(sb->buf + sb->len, data, len); | ||
| 183 | strbuf_setlen(sb, sb->len + len); | ||
| 184 | } | ||
| 185 | |||
| 186 | void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len) | ||
| 187 | { | ||
| 188 | strbuf_grow(sb, len); | ||
| 189 | memcpy(sb->buf + sb->len, sb->buf + pos, len); | ||
| 190 | strbuf_setlen(sb, sb->len + len); | ||
| 191 | } | ||
| 192 | |||
| 193 | void strbuf_addf(struct strbuf *sb, const char *fmt, ...) | ||
| 194 | { | ||
| 195 | int len; | ||
| 196 | va_list ap; | ||
| 197 | |||
| 198 | if (!strbuf_avail(sb)) | ||
| 199 | strbuf_grow(sb, 64); | ||
| 200 | va_start(ap, fmt); | ||
| 201 | len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); | ||
| 202 | va_end(ap); | ||
| 203 | if (len < 0) | ||
| 204 | die("your vsnprintf is broken"); | ||
| 205 | if (len > strbuf_avail(sb)) { | ||
| 206 | strbuf_grow(sb, len); | ||
| 207 | va_start(ap, fmt); | ||
| 208 | len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); | ||
| 209 | va_end(ap); | ||
| 210 | if (len > strbuf_avail(sb)) { | ||
| 211 | die("this should not happen, your snprintf is broken"); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | strbuf_setlen(sb, sb->len + len); | ||
| 215 | } | ||
| 216 | |||
| 217 | void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, | ||
| 218 | void *context) | ||
| 219 | { | ||
| 220 | for (;;) { | ||
| 221 | const char *percent; | ||
| 222 | size_t consumed; | ||
| 223 | |||
| 224 | percent = strchrnul(format, '%'); | ||
| 225 | strbuf_add(sb, format, percent - format); | ||
| 226 | if (!*percent) | ||
| 227 | break; | ||
| 228 | format = percent + 1; | ||
| 229 | |||
| 230 | consumed = fn(sb, format, context); | ||
| 231 | if (consumed) | ||
| 232 | format += consumed; | ||
| 233 | else | ||
| 234 | strbuf_addch(sb, '%'); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, | ||
| 239 | void *context) | ||
| 240 | { | ||
| 241 | struct strbuf_expand_dict_entry *e = context; | ||
| 242 | size_t len; | ||
| 243 | |||
| 244 | for (; e->placeholder && (len = strlen(e->placeholder)); e++) { | ||
| 245 | if (!strncmp(placeholder, e->placeholder, len)) { | ||
| 246 | if (e->value) | ||
| 247 | strbuf_addstr(sb, e->value); | ||
| 248 | return len; | ||
| 249 | } | ||
| 250 | } | ||
| 251 | return 0; | ||
| 252 | } | ||
| 253 | |||
| 254 | size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) | ||
| 255 | { | ||
| 256 | size_t res; | ||
| 257 | size_t oldalloc = sb->alloc; | ||
| 258 | |||
| 259 | strbuf_grow(sb, size); | ||
| 260 | res = fread(sb->buf + sb->len, 1, size, f); | ||
| 261 | if (res > 0) | ||
| 262 | strbuf_setlen(sb, sb->len + res); | ||
| 263 | else if (oldalloc == 0) | ||
| 264 | strbuf_release(sb); | ||
| 265 | return res; | ||
| 266 | } | ||
| 267 | |||
| 268 | ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint) | ||
| 269 | { | ||
| 270 | size_t oldlen = sb->len; | ||
| 271 | size_t oldalloc = sb->alloc; | ||
| 272 | |||
| 273 | strbuf_grow(sb, hint ? hint : 8192); | ||
| 274 | for (;;) { | ||
| 275 | ssize_t cnt; | ||
| 276 | |||
| 277 | cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); | ||
| 278 | if (cnt < 0) { | ||
| 279 | if (oldalloc == 0) | ||
| 280 | strbuf_release(sb); | ||
| 281 | else | ||
| 282 | strbuf_setlen(sb, oldlen); | ||
| 283 | return -1; | ||
| 284 | } | ||
| 285 | if (!cnt) | ||
| 286 | break; | ||
| 287 | sb->len += cnt; | ||
| 288 | strbuf_grow(sb, 8192); | ||
| 289 | } | ||
| 290 | |||
| 291 | sb->buf[sb->len] = '\0'; | ||
| 292 | return sb->len - oldlen; | ||
| 293 | } | ||
| 294 | |||
| 295 | #define STRBUF_MAXLINK (2*PATH_MAX) | ||
| 296 | |||
| 297 | int strbuf_readlink(struct strbuf *sb, const char *path, ssize_t hint) | ||
| 298 | { | ||
| 299 | size_t oldalloc = sb->alloc; | ||
| 300 | |||
| 301 | if (hint < 32) | ||
| 302 | hint = 32; | ||
| 303 | |||
| 304 | while (hint < STRBUF_MAXLINK) { | ||
| 305 | ssize_t len; | ||
| 306 | |||
| 307 | strbuf_grow(sb, hint); | ||
| 308 | len = readlink(path, sb->buf, hint); | ||
| 309 | if (len < 0) { | ||
| 310 | if (errno != ERANGE) | ||
| 311 | break; | ||
| 312 | } else if (len < hint) { | ||
| 313 | strbuf_setlen(sb, len); | ||
| 314 | return 0; | ||
| 315 | } | ||
| 316 | |||
| 317 | /* .. the buffer was too small - try again */ | ||
| 318 | hint *= 2; | ||
| 319 | } | ||
| 320 | if (oldalloc == 0) | ||
| 321 | strbuf_release(sb); | ||
| 322 | return -1; | ||
| 323 | } | ||
| 324 | |||
| 325 | int strbuf_getline(struct strbuf *sb, FILE *fp, int term) | ||
| 326 | { | ||
| 327 | int ch; | ||
| 328 | |||
| 329 | strbuf_grow(sb, 0); | ||
| 330 | if (feof(fp)) | ||
| 331 | return EOF; | ||
| 332 | |||
| 333 | strbuf_reset(sb); | ||
| 334 | while ((ch = fgetc(fp)) != EOF) { | ||
| 335 | if (ch == term) | ||
| 336 | break; | ||
| 337 | strbuf_grow(sb, 1); | ||
| 338 | sb->buf[sb->len++] = ch; | ||
| 339 | } | ||
| 340 | if (ch == EOF && sb->len == 0) | ||
| 341 | return EOF; | ||
| 342 | |||
| 343 | sb->buf[sb->len] = '\0'; | ||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 347 | int strbuf_read_file(struct strbuf *sb, const char *path, ssize_t hint) | ||
| 348 | { | ||
| 349 | int fd, len; | ||
| 350 | |||
| 351 | fd = open(path, O_RDONLY); | ||
| 352 | if (fd < 0) | ||
| 353 | return -1; | ||
| 354 | len = strbuf_read(sb, fd, hint); | ||
| 355 | close(fd); | ||
| 356 | if (len < 0) | ||
| 357 | return -1; | ||
| 358 | |||
| 359 | return len; | ||
| 360 | } | ||
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h new file mode 100644 index 000000000000..d2aa86c014c1 --- /dev/null +++ b/tools/perf/util/strbuf.h | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | #ifndef STRBUF_H | ||
| 2 | #define STRBUF_H | ||
| 3 | |||
| 4 | /* | ||
| 5 | * Strbuf's can be use in many ways: as a byte array, or to store arbitrary | ||
| 6 | * long, overflow safe strings. | ||
| 7 | * | ||
| 8 | * Strbufs has some invariants that are very important to keep in mind: | ||
| 9 | * | ||
| 10 | * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to | ||
| 11 | * build complex strings/buffers whose final size isn't easily known. | ||
| 12 | * | ||
| 13 | * It is NOT legal to copy the ->buf pointer away. | ||
| 14 | * `strbuf_detach' is the operation that detachs a buffer from its shell | ||
| 15 | * while keeping the shell valid wrt its invariants. | ||
| 16 | * | ||
| 17 | * 2. the ->buf member is a byte array that has at least ->len + 1 bytes | ||
| 18 | * allocated. The extra byte is used to store a '\0', allowing the ->buf | ||
| 19 | * member to be a valid C-string. Every strbuf function ensure this | ||
| 20 | * invariant is preserved. | ||
| 21 | * | ||
| 22 | * Note that it is OK to "play" with the buffer directly if you work it | ||
| 23 | * that way: | ||
| 24 | * | ||
| 25 | * strbuf_grow(sb, SOME_SIZE); | ||
| 26 | * ... Here, the memory array starting at sb->buf, and of length | ||
| 27 | * ... strbuf_avail(sb) is all yours, and you are sure that | ||
| 28 | * ... strbuf_avail(sb) is at least SOME_SIZE. | ||
| 29 | * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE); | ||
| 30 | * | ||
| 31 | * Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb). | ||
| 32 | * | ||
| 33 | * Doing so is safe, though if it has to be done in many places, adding the | ||
| 34 | * missing API to the strbuf module is the way to go. | ||
| 35 | * | ||
| 36 | * XXX: do _not_ assume that the area that is yours is of size ->alloc - 1 | ||
| 37 | * even if it's true in the current implementation. Alloc is somehow a | ||
| 38 | * "private" member that should not be messed with. | ||
| 39 | */ | ||
| 40 | |||
| 41 | #include <assert.h> | ||
| 42 | |||
| 43 | extern char strbuf_slopbuf[]; | ||
| 44 | struct strbuf { | ||
| 45 | size_t alloc; | ||
| 46 | size_t len; | ||
| 47 | char *buf; | ||
| 48 | }; | ||
| 49 | |||
| 50 | #define STRBUF_INIT { 0, 0, strbuf_slopbuf } | ||
| 51 | |||
| 52 | /*----- strbuf life cycle -----*/ | ||
| 53 | extern void strbuf_init(struct strbuf *buf, ssize_t hint); | ||
| 54 | extern void strbuf_release(struct strbuf *); | ||
| 55 | extern char *strbuf_detach(struct strbuf *, size_t *); | ||
| 56 | extern void strbuf_attach(struct strbuf *, void *, size_t, size_t); | ||
| 57 | static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) { | ||
| 58 | struct strbuf tmp = *a; | ||
| 59 | *a = *b; | ||
| 60 | *b = tmp; | ||
| 61 | } | ||
| 62 | |||
| 63 | /*----- strbuf size related -----*/ | ||
| 64 | static inline ssize_t strbuf_avail(const struct strbuf *sb) { | ||
| 65 | return sb->alloc ? sb->alloc - sb->len - 1 : 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | extern void strbuf_grow(struct strbuf *, size_t); | ||
| 69 | |||
| 70 | static inline void strbuf_setlen(struct strbuf *sb, size_t len) { | ||
| 71 | if (!sb->alloc) | ||
| 72 | strbuf_grow(sb, 0); | ||
| 73 | assert(len < sb->alloc); | ||
| 74 | sb->len = len; | ||
| 75 | sb->buf[len] = '\0'; | ||
| 76 | } | ||
| 77 | #define strbuf_reset(sb) strbuf_setlen(sb, 0) | ||
| 78 | |||
| 79 | /*----- content related -----*/ | ||
| 80 | extern void strbuf_trim(struct strbuf *); | ||
| 81 | extern void strbuf_rtrim(struct strbuf *); | ||
| 82 | extern void strbuf_ltrim(struct strbuf *); | ||
| 83 | extern int strbuf_cmp(const struct strbuf *, const struct strbuf *); | ||
| 84 | extern void strbuf_tolower(struct strbuf *); | ||
| 85 | |||
| 86 | extern struct strbuf **strbuf_split(const struct strbuf *, int delim); | ||
| 87 | extern void strbuf_list_free(struct strbuf **); | ||
| 88 | |||
| 89 | /*----- add data in your buffer -----*/ | ||
| 90 | static inline void strbuf_addch(struct strbuf *sb, int c) { | ||
| 91 | strbuf_grow(sb, 1); | ||
| 92 | sb->buf[sb->len++] = c; | ||
| 93 | sb->buf[sb->len] = '\0'; | ||
| 94 | } | ||
| 95 | |||
| 96 | extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t); | ||
| 97 | extern void strbuf_remove(struct strbuf *, size_t pos, size_t len); | ||
| 98 | |||
| 99 | /* splice pos..pos+len with given data */ | ||
| 100 | extern void strbuf_splice(struct strbuf *, size_t pos, size_t len, | ||
| 101 | const void *, size_t); | ||
| 102 | |||
| 103 | extern void strbuf_add(struct strbuf *, const void *, size_t); | ||
| 104 | static inline void strbuf_addstr(struct strbuf *sb, const char *s) { | ||
| 105 | strbuf_add(sb, s, strlen(s)); | ||
| 106 | } | ||
| 107 | static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) { | ||
| 108 | strbuf_add(sb, sb2->buf, sb2->len); | ||
| 109 | } | ||
| 110 | extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len); | ||
| 111 | |||
| 112 | typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context); | ||
| 113 | extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context); | ||
| 114 | struct strbuf_expand_dict_entry { | ||
| 115 | const char *placeholder; | ||
| 116 | const char *value; | ||
| 117 | }; | ||
| 118 | extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context); | ||
| 119 | |||
| 120 | __attribute__((format(printf,2,3))) | ||
| 121 | extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); | ||
| 122 | |||
| 123 | extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); | ||
| 124 | /* XXX: if read fails, any partial read is undone */ | ||
| 125 | extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint); | ||
| 126 | extern int strbuf_read_file(struct strbuf *sb, const char *path, ssize_t hint); | ||
| 127 | extern int strbuf_readlink(struct strbuf *sb, const char *path, ssize_t hint); | ||
| 128 | |||
| 129 | extern int strbuf_getline(struct strbuf *, FILE *, int); | ||
| 130 | |||
| 131 | extern void stripspace(struct strbuf *buf, int skip_comments); | ||
| 132 | extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env); | ||
| 133 | |||
| 134 | extern int strbuf_branchname(struct strbuf *sb, const char *name); | ||
| 135 | extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); | ||
| 136 | |||
| 137 | #endif /* STRBUF_H */ | ||
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c new file mode 100644 index 000000000000..c93eca9a7be3 --- /dev/null +++ b/tools/perf/util/string.c | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #include "string.h" | ||
| 2 | |||
| 3 | static int hex(char ch) | ||
| 4 | { | ||
| 5 | if ((ch >= '0') && (ch <= '9')) | ||
| 6 | return ch - '0'; | ||
| 7 | if ((ch >= 'a') && (ch <= 'f')) | ||
| 8 | return ch - 'a' + 10; | ||
| 9 | if ((ch >= 'A') && (ch <= 'F')) | ||
| 10 | return ch - 'A' + 10; | ||
| 11 | return -1; | ||
| 12 | } | ||
| 13 | |||
| 14 | /* | ||
| 15 | * While we find nice hex chars, build a long_val. | ||
| 16 | * Return number of chars processed. | ||
| 17 | */ | ||
| 18 | int hex2u64(const char *ptr, u64 *long_val) | ||
| 19 | { | ||
| 20 | const char *p = ptr; | ||
| 21 | *long_val = 0; | ||
| 22 | |||
| 23 | while (*p) { | ||
| 24 | const int hex_val = hex(*p); | ||
| 25 | |||
| 26 | if (hex_val < 0) | ||
| 27 | break; | ||
| 28 | |||
| 29 | *long_val = (*long_val << 4) | hex_val; | ||
| 30 | p++; | ||
| 31 | } | ||
| 32 | |||
| 33 | return p - ptr; | ||
| 34 | } | ||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h new file mode 100644 index 000000000000..bf39dfadfd24 --- /dev/null +++ b/tools/perf/util/string.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #ifndef _PERF_STRING_H_ | ||
| 2 | #define _PERF_STRING_H_ | ||
| 3 | |||
| 4 | #include "types.h" | ||
| 5 | |||
| 6 | int hex2u64(const char *ptr, u64 *val); | ||
| 7 | |||
| 8 | #define _STR(x) #x | ||
| 9 | #define STR(x) _STR(x) | ||
| 10 | |||
| 11 | #endif | ||
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c new file mode 100644 index 000000000000..7ad38171dc2b --- /dev/null +++ b/tools/perf/util/strlist.c | |||
| @@ -0,0 +1,200 @@ | |||
| 1 | /* | ||
| 2 | * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com> | ||
| 3 | * | ||
| 4 | * Licensed under the GPLv2. | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include "strlist.h" | ||
| 8 | #include <errno.h> | ||
| 9 | #include <stdio.h> | ||
| 10 | #include <stdlib.h> | ||
| 11 | #include <string.h> | ||
| 12 | |||
| 13 | static struct str_node *str_node__new(const char *s, bool dupstr) | ||
| 14 | { | ||
| 15 | struct str_node *self = malloc(sizeof(*self)); | ||
| 16 | |||
| 17 | if (self != NULL) { | ||
| 18 | if (dupstr) { | ||
| 19 | s = strdup(s); | ||
| 20 | if (s == NULL) | ||
| 21 | goto out_delete; | ||
| 22 | } | ||
| 23 | self->s = s; | ||
| 24 | } | ||
| 25 | |||
| 26 | return self; | ||
| 27 | |||
| 28 | out_delete: | ||
| 29 | free(self); | ||
| 30 | return NULL; | ||
| 31 | } | ||
| 32 | |||
| 33 | static void str_node__delete(struct str_node *self, bool dupstr) | ||
| 34 | { | ||
| 35 | if (dupstr) | ||
| 36 | free((void *)self->s); | ||
| 37 | free(self); | ||
| 38 | } | ||
| 39 | |||
| 40 | int strlist__add(struct strlist *self, const char *new_entry) | ||
| 41 | { | ||
| 42 | struct rb_node **p = &self->entries.rb_node; | ||
| 43 | struct rb_node *parent = NULL; | ||
| 44 | struct str_node *sn; | ||
| 45 | |||
| 46 | while (*p != NULL) { | ||
| 47 | int rc; | ||
| 48 | |||
| 49 | parent = *p; | ||
| 50 | sn = rb_entry(parent, struct str_node, rb_node); | ||
| 51 | rc = strcmp(sn->s, new_entry); | ||
| 52 | |||
| 53 | if (rc > 0) | ||
| 54 | p = &(*p)->rb_left; | ||
| 55 | else if (rc < 0) | ||
| 56 | p = &(*p)->rb_right; | ||
| 57 | else | ||
| 58 | return -EEXIST; | ||
| 59 | } | ||
| 60 | |||
| 61 | sn = str_node__new(new_entry, self->dupstr); | ||
| 62 | if (sn == NULL) | ||
| 63 | return -ENOMEM; | ||
| 64 | |||
| 65 | rb_link_node(&sn->rb_node, parent, p); | ||
| 66 | rb_insert_color(&sn->rb_node, &self->entries); | ||
| 67 | ++self->nr_entries; | ||
| 68 | |||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | int strlist__load(struct strlist *self, const char *filename) | ||
| 73 | { | ||
| 74 | char entry[1024]; | ||
| 75 | int err; | ||
| 76 | FILE *fp = fopen(filename, "r"); | ||
| 77 | |||
| 78 | if (fp == NULL) | ||
| 79 | return errno; | ||
| 80 | |||
| 81 | while (fgets(entry, sizeof(entry), fp) != NULL) { | ||
| 82 | const size_t len = strlen(entry); | ||
| 83 | |||
| 84 | if (len == 0) | ||
| 85 | continue; | ||
| 86 | entry[len - 1] = '\0'; | ||
| 87 | |||
| 88 | err = strlist__add(self, entry); | ||
| 89 | if (err != 0) | ||
| 90 | goto out; | ||
| 91 | } | ||
| 92 | |||
| 93 | err = 0; | ||
| 94 | out: | ||
| 95 | fclose(fp); | ||
| 96 | return err; | ||
| 97 | } | ||
| 98 | |||
| 99 | void strlist__remove(struct strlist *self, struct str_node *sn) | ||
| 100 | { | ||
| 101 | rb_erase(&sn->rb_node, &self->entries); | ||
| 102 | str_node__delete(sn, self->dupstr); | ||
| 103 | } | ||
| 104 | |||
| 105 | bool strlist__has_entry(struct strlist *self, const char *entry) | ||
| 106 | { | ||
| 107 | struct rb_node **p = &self->entries.rb_node; | ||
| 108 | struct rb_node *parent = NULL; | ||
| 109 | |||
| 110 | while (*p != NULL) { | ||
| 111 | struct str_node *sn; | ||
| 112 | int rc; | ||
| 113 | |||
| 114 | parent = *p; | ||
| 115 | sn = rb_entry(parent, struct str_node, rb_node); | ||
| 116 | rc = strcmp(sn->s, entry); | ||
| 117 | |||
| 118 | if (rc > 0) | ||
| 119 | p = &(*p)->rb_left; | ||
| 120 | else if (rc < 0) | ||
| 121 | p = &(*p)->rb_right; | ||
| 122 | else | ||
| 123 | return true; | ||
| 124 | } | ||
| 125 | |||
| 126 | return false; | ||
| 127 | } | ||
| 128 | |||
| 129 | static int strlist__parse_list_entry(struct strlist *self, const char *s) | ||
| 130 | { | ||
| 131 | if (strncmp(s, "file://", 7) == 0) | ||
| 132 | return strlist__load(self, s + 7); | ||
| 133 | |||
| 134 | return strlist__add(self, s); | ||
| 135 | } | ||
| 136 | |||
| 137 | int strlist__parse_list(struct strlist *self, const char *s) | ||
| 138 | { | ||
| 139 | char *sep; | ||
| 140 | int err; | ||
| 141 | |||
| 142 | while ((sep = strchr(s, ',')) != NULL) { | ||
| 143 | *sep = '\0'; | ||
| 144 | err = strlist__parse_list_entry(self, s); | ||
| 145 | *sep = ','; | ||
| 146 | if (err != 0) | ||
| 147 | return err; | ||
| 148 | s = sep + 1; | ||
| 149 | } | ||
| 150 | |||
| 151 | return *s ? strlist__parse_list_entry(self, s) : 0; | ||
| 152 | } | ||
| 153 | |||
| 154 | struct strlist *strlist__new(bool dupstr, const char *slist) | ||
| 155 | { | ||
| 156 | struct strlist *self = malloc(sizeof(*self)); | ||
| 157 | |||
| 158 | if (self != NULL) { | ||
| 159 | self->entries = RB_ROOT; | ||
| 160 | self->dupstr = dupstr; | ||
| 161 | self->nr_entries = 0; | ||
| 162 | if (slist && strlist__parse_list(self, slist) != 0) | ||
| 163 | goto out_error; | ||
| 164 | } | ||
| 165 | |||
| 166 | return self; | ||
| 167 | out_error: | ||
| 168 | free(self); | ||
| 169 | return NULL; | ||
| 170 | } | ||
| 171 | |||
| 172 | void strlist__delete(struct strlist *self) | ||
| 173 | { | ||
| 174 | if (self != NULL) { | ||
| 175 | struct str_node *pos; | ||
| 176 | struct rb_node *next = rb_first(&self->entries); | ||
| 177 | |||
| 178 | while (next) { | ||
| 179 | pos = rb_entry(next, struct str_node, rb_node); | ||
| 180 | next = rb_next(&pos->rb_node); | ||
| 181 | strlist__remove(self, pos); | ||
| 182 | } | ||
| 183 | self->entries = RB_ROOT; | ||
| 184 | free(self); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx) | ||
| 189 | { | ||
| 190 | struct rb_node *nd; | ||
| 191 | |||
| 192 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | ||
| 193 | struct str_node *pos = rb_entry(nd, struct str_node, rb_node); | ||
| 194 | |||
| 195 | if (!idx--) | ||
| 196 | return pos; | ||
| 197 | } | ||
| 198 | |||
| 199 | return NULL; | ||
| 200 | } | ||
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h new file mode 100644 index 000000000000..921818e44a54 --- /dev/null +++ b/tools/perf/util/strlist.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #ifndef STRLIST_H_ | ||
| 2 | #define STRLIST_H_ | ||
| 3 | |||
| 4 | #include <linux/rbtree.h> | ||
| 5 | #include <stdbool.h> | ||
| 6 | |||
| 7 | struct str_node { | ||
| 8 | struct rb_node rb_node; | ||
| 9 | const char *s; | ||
| 10 | }; | ||
| 11 | |||
| 12 | struct strlist { | ||
| 13 | struct rb_root entries; | ||
| 14 | unsigned int nr_entries; | ||
| 15 | bool dupstr; | ||
| 16 | }; | ||
| 17 | |||
| 18 | struct strlist *strlist__new(bool dupstr, const char *slist); | ||
| 19 | void strlist__delete(struct strlist *self); | ||
| 20 | |||
| 21 | void strlist__remove(struct strlist *self, struct str_node *sn); | ||
| 22 | int strlist__load(struct strlist *self, const char *filename); | ||
| 23 | int strlist__add(struct strlist *self, const char *str); | ||
| 24 | |||
| 25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); | ||
| 26 | bool strlist__has_entry(struct strlist *self, const char *entry); | ||
| 27 | |||
| 28 | static inline bool strlist__empty(const struct strlist *self) | ||
| 29 | { | ||
| 30 | return self->nr_entries == 0; | ||
| 31 | } | ||
| 32 | |||
| 33 | static inline unsigned int strlist__nr_entries(const struct strlist *self) | ||
| 34 | { | ||
| 35 | return self->nr_entries; | ||
| 36 | } | ||
| 37 | |||
| 38 | int strlist__parse_list(struct strlist *self, const char *s); | ||
| 39 | #endif /* STRLIST_H_ */ | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c new file mode 100644 index 000000000000..5c0f42e6b33b --- /dev/null +++ b/tools/perf/util/symbol.c | |||
| @@ -0,0 +1,930 @@ | |||
| 1 | #include "util.h" | ||
| 2 | #include "../perf.h" | ||
| 3 | #include "string.h" | ||
| 4 | #include "symbol.h" | ||
| 5 | |||
| 6 | #include <libelf.h> | ||
| 7 | #include <gelf.h> | ||
| 8 | #include <elf.h> | ||
| 9 | |||
| 10 | const char *sym_hist_filter; | ||
| 11 | |||
| 12 | enum dso_origin { | ||
| 13 | DSO__ORIG_KERNEL = 0, | ||
| 14 | DSO__ORIG_JAVA_JIT, | ||
| 15 | DSO__ORIG_FEDORA, | ||
| 16 | DSO__ORIG_UBUNTU, | ||
| 17 | DSO__ORIG_BUILDID, | ||
| 18 | DSO__ORIG_DSO, | ||
| 19 | DSO__ORIG_NOT_FOUND, | ||
| 20 | }; | ||
| 21 | |||
| 22 | static struct symbol *symbol__new(u64 start, u64 len, | ||
| 23 | const char *name, unsigned int priv_size, | ||
| 24 | u64 obj_start, int verbose) | ||
| 25 | { | ||
| 26 | size_t namelen = strlen(name) + 1; | ||
| 27 | struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); | ||
| 28 | |||
| 29 | if (!self) | ||
| 30 | return NULL; | ||
| 31 | |||
| 32 | if (verbose >= 2) | ||
| 33 | printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n", | ||
| 34 | (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start); | ||
| 35 | |||
| 36 | self->obj_start= obj_start; | ||
| 37 | self->hist = NULL; | ||
| 38 | self->hist_sum = 0; | ||
| 39 | |||
| 40 | if (sym_hist_filter && !strcmp(name, sym_hist_filter)) | ||
| 41 | self->hist = calloc(sizeof(u64), len); | ||
| 42 | |||
| 43 | if (priv_size) { | ||
| 44 | memset(self, 0, priv_size); | ||
| 45 | self = ((void *)self) + priv_size; | ||
| 46 | } | ||
| 47 | self->start = start; | ||
| 48 | self->end = len ? start + len - 1 : start; | ||
| 49 | memcpy(self->name, name, namelen); | ||
| 50 | |||
| 51 | return self; | ||
| 52 | } | ||
| 53 | |||
| 54 | static void symbol__delete(struct symbol *self, unsigned int priv_size) | ||
| 55 | { | ||
| 56 | free(((void *)self) - priv_size); | ||
| 57 | } | ||
| 58 | |||
| 59 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | ||
| 60 | { | ||
| 61 | if (!self->module) | ||
| 62 | return fprintf(fp, " %llx-%llx %s\n", | ||
| 63 | self->start, self->end, self->name); | ||
| 64 | else | ||
| 65 | return fprintf(fp, " %llx-%llx %s \t[%s]\n", | ||
| 66 | self->start, self->end, self->name, self->module->name); | ||
| 67 | } | ||
| 68 | |||
| 69 | struct dso *dso__new(const char *name, unsigned int sym_priv_size) | ||
| 70 | { | ||
| 71 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
| 72 | |||
| 73 | if (self != NULL) { | ||
| 74 | strcpy(self->name, name); | ||
| 75 | self->syms = RB_ROOT; | ||
| 76 | self->sym_priv_size = sym_priv_size; | ||
| 77 | self->find_symbol = dso__find_symbol; | ||
| 78 | self->slen_calculated = 0; | ||
| 79 | self->origin = DSO__ORIG_NOT_FOUND; | ||
| 80 | } | ||
| 81 | |||
| 82 | return self; | ||
| 83 | } | ||
| 84 | |||
| 85 | static void dso__delete_symbols(struct dso *self) | ||
| 86 | { | ||
| 87 | struct symbol *pos; | ||
| 88 | struct rb_node *next = rb_first(&self->syms); | ||
| 89 | |||
| 90 | while (next) { | ||
| 91 | pos = rb_entry(next, struct symbol, rb_node); | ||
| 92 | next = rb_next(&pos->rb_node); | ||
| 93 | rb_erase(&pos->rb_node, &self->syms); | ||
| 94 | symbol__delete(pos, self->sym_priv_size); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | void dso__delete(struct dso *self) | ||
| 99 | { | ||
| 100 | dso__delete_symbols(self); | ||
| 101 | free(self); | ||
| 102 | } | ||
| 103 | |||
| 104 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) | ||
| 105 | { | ||
| 106 | struct rb_node **p = &self->syms.rb_node; | ||
| 107 | struct rb_node *parent = NULL; | ||
| 108 | const u64 ip = sym->start; | ||
| 109 | struct symbol *s; | ||
| 110 | |||
| 111 | while (*p != NULL) { | ||
| 112 | parent = *p; | ||
| 113 | s = rb_entry(parent, struct symbol, rb_node); | ||
| 114 | if (ip < s->start) | ||
| 115 | p = &(*p)->rb_left; | ||
| 116 | else | ||
| 117 | p = &(*p)->rb_right; | ||
| 118 | } | ||
| 119 | rb_link_node(&sym->rb_node, parent, p); | ||
| 120 | rb_insert_color(&sym->rb_node, &self->syms); | ||
| 121 | } | ||
| 122 | |||
| 123 | struct symbol *dso__find_symbol(struct dso *self, u64 ip) | ||
| 124 | { | ||
| 125 | struct rb_node *n; | ||
| 126 | |||
| 127 | if (self == NULL) | ||
| 128 | return NULL; | ||
| 129 | |||
| 130 | n = self->syms.rb_node; | ||
| 131 | |||
| 132 | while (n) { | ||
| 133 | struct symbol *s = rb_entry(n, struct symbol, rb_node); | ||
| 134 | |||
| 135 | if (ip < s->start) | ||
| 136 | n = n->rb_left; | ||
| 137 | else if (ip > s->end) | ||
| 138 | n = n->rb_right; | ||
| 139 | else | ||
| 140 | return s; | ||
| 141 | } | ||
| 142 | |||
| 143 | return NULL; | ||
| 144 | } | ||
| 145 | |||
| 146 | size_t dso__fprintf(struct dso *self, FILE *fp) | ||
| 147 | { | ||
| 148 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | ||
| 149 | |||
| 150 | struct rb_node *nd; | ||
| 151 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | ||
| 152 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
| 153 | ret += symbol__fprintf(pos, fp); | ||
| 154 | } | ||
| 155 | |||
| 156 | return ret; | ||
| 157 | } | ||
| 158 | |||
| 159 | static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verbose) | ||
| 160 | { | ||
| 161 | struct rb_node *nd, *prevnd; | ||
| 162 | char *line = NULL; | ||
| 163 | size_t n; | ||
| 164 | FILE *file = fopen("/proc/kallsyms", "r"); | ||
| 165 | int count = 0; | ||
| 166 | |||
| 167 | if (file == NULL) | ||
| 168 | goto out_failure; | ||
| 169 | |||
| 170 | while (!feof(file)) { | ||
| 171 | u64 start; | ||
| 172 | struct symbol *sym; | ||
| 173 | int line_len, len; | ||
| 174 | char symbol_type; | ||
| 175 | |||
| 176 | line_len = getline(&line, &n, file); | ||
| 177 | if (line_len < 0) | ||
| 178 | break; | ||
| 179 | |||
| 180 | if (!line) | ||
| 181 | goto out_failure; | ||
| 182 | |||
| 183 | line[--line_len] = '\0'; /* \n */ | ||
| 184 | |||
| 185 | len = hex2u64(line, &start); | ||
| 186 | |||
| 187 | len++; | ||
| 188 | if (len + 2 >= line_len) | ||
| 189 | continue; | ||
| 190 | |||
| 191 | symbol_type = toupper(line[len]); | ||
| 192 | /* | ||
| 193 | * We're interested only in code ('T'ext) | ||
| 194 | */ | ||
| 195 | if (symbol_type != 'T' && symbol_type != 'W') | ||
| 196 | continue; | ||
| 197 | /* | ||
| 198 | * Well fix up the end later, when we have all sorted. | ||
| 199 | */ | ||
| 200 | sym = symbol__new(start, 0xdead, line + len + 2, | ||
| 201 | self->sym_priv_size, 0, verbose); | ||
| 202 | |||
| 203 | if (sym == NULL) | ||
| 204 | goto out_delete_line; | ||
| 205 | |||
| 206 | if (filter && filter(self, sym)) | ||
| 207 | symbol__delete(sym, self->sym_priv_size); | ||
| 208 | else { | ||
| 209 | dso__insert_symbol(self, sym); | ||
| 210 | count++; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | /* | ||
| 215 | * Now that we have all sorted out, just set the ->end of all | ||
| 216 | * symbols | ||
| 217 | */ | ||
| 218 | prevnd = rb_first(&self->syms); | ||
| 219 | |||
| 220 | if (prevnd == NULL) | ||
| 221 | goto out_delete_line; | ||
| 222 | |||
| 223 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | ||
| 224 | struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node), | ||
| 225 | *curr = rb_entry(nd, struct symbol, rb_node); | ||
| 226 | |||
| 227 | prev->end = curr->start - 1; | ||
| 228 | prevnd = nd; | ||
| 229 | } | ||
| 230 | |||
| 231 | free(line); | ||
| 232 | fclose(file); | ||
| 233 | |||
| 234 | return count; | ||
| 235 | |||
| 236 | out_delete_line: | ||
| 237 | free(line); | ||
| 238 | out_failure: | ||
| 239 | return -1; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int verbose) | ||
| 243 | { | ||
| 244 | char *line = NULL; | ||
| 245 | size_t n; | ||
| 246 | FILE *file; | ||
| 247 | int nr_syms = 0; | ||
| 248 | |||
| 249 | file = fopen(self->name, "r"); | ||
| 250 | if (file == NULL) | ||
| 251 | goto out_failure; | ||
| 252 | |||
| 253 | while (!feof(file)) { | ||
| 254 | u64 start, size; | ||
| 255 | struct symbol *sym; | ||
| 256 | int line_len, len; | ||
| 257 | |||
| 258 | line_len = getline(&line, &n, file); | ||
| 259 | if (line_len < 0) | ||
| 260 | break; | ||
| 261 | |||
| 262 | if (!line) | ||
| 263 | goto out_failure; | ||
| 264 | |||
| 265 | line[--line_len] = '\0'; /* \n */ | ||
| 266 | |||
| 267 | len = hex2u64(line, &start); | ||
| 268 | |||
| 269 | len++; | ||
| 270 | if (len + 2 >= line_len) | ||
| 271 | continue; | ||
| 272 | |||
| 273 | len += hex2u64(line + len, &size); | ||
| 274 | |||
| 275 | len++; | ||
| 276 | if (len + 2 >= line_len) | ||
| 277 | continue; | ||
| 278 | |||
| 279 | sym = symbol__new(start, size, line + len, | ||
| 280 | self->sym_priv_size, start, verbose); | ||
| 281 | |||
| 282 | if (sym == NULL) | ||
| 283 | goto out_delete_line; | ||
| 284 | |||
| 285 | if (filter && filter(self, sym)) | ||
| 286 | symbol__delete(sym, self->sym_priv_size); | ||
| 287 | else { | ||
| 288 | dso__insert_symbol(self, sym); | ||
| 289 | nr_syms++; | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 293 | free(line); | ||
| 294 | fclose(file); | ||
| 295 | |||
| 296 | return nr_syms; | ||
| 297 | |||
| 298 | out_delete_line: | ||
| 299 | free(line); | ||
| 300 | out_failure: | ||
| 301 | return -1; | ||
| 302 | } | ||
| 303 | |||
| 304 | /** | ||
| 305 | * elf_symtab__for_each_symbol - iterate thru all the symbols | ||
| 306 | * | ||
| 307 | * @self: struct elf_symtab instance to iterate | ||
| 308 | * @index: uint32_t index | ||
| 309 | * @sym: GElf_Sym iterator | ||
| 310 | */ | ||
| 311 | #define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \ | ||
| 312 | for (index = 0, gelf_getsym(syms, index, &sym);\ | ||
| 313 | index < nr_syms; \ | ||
| 314 | index++, gelf_getsym(syms, index, &sym)) | ||
| 315 | |||
| 316 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) | ||
| 317 | { | ||
| 318 | return GELF_ST_TYPE(sym->st_info); | ||
| 319 | } | ||
| 320 | |||
| 321 | static inline int elf_sym__is_function(const GElf_Sym *sym) | ||
| 322 | { | ||
| 323 | return elf_sym__type(sym) == STT_FUNC && | ||
| 324 | sym->st_name != 0 && | ||
| 325 | sym->st_shndx != SHN_UNDEF && | ||
| 326 | sym->st_size != 0; | ||
| 327 | } | ||
| 328 | |||
| 329 | static inline int elf_sym__is_label(const GElf_Sym *sym) | ||
| 330 | { | ||
| 331 | return elf_sym__type(sym) == STT_NOTYPE && | ||
| 332 | sym->st_name != 0 && | ||
| 333 | sym->st_shndx != SHN_UNDEF && | ||
| 334 | sym->st_shndx != SHN_ABS; | ||
| 335 | } | ||
| 336 | |||
| 337 | static inline const char *elf_sec__name(const GElf_Shdr *shdr, | ||
| 338 | const Elf_Data *secstrs) | ||
| 339 | { | ||
| 340 | return secstrs->d_buf + shdr->sh_name; | ||
| 341 | } | ||
| 342 | |||
| 343 | static inline int elf_sec__is_text(const GElf_Shdr *shdr, | ||
| 344 | const Elf_Data *secstrs) | ||
| 345 | { | ||
| 346 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | ||
| 347 | } | ||
| 348 | |||
| 349 | static inline const char *elf_sym__name(const GElf_Sym *sym, | ||
| 350 | const Elf_Data *symstrs) | ||
| 351 | { | ||
| 352 | return symstrs->d_buf + sym->st_name; | ||
| 353 | } | ||
| 354 | |||
| 355 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | ||
| 356 | GElf_Shdr *shp, const char *name, | ||
| 357 | size_t *index) | ||
| 358 | { | ||
| 359 | Elf_Scn *sec = NULL; | ||
| 360 | size_t cnt = 1; | ||
| 361 | |||
| 362 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
| 363 | char *str; | ||
| 364 | |||
| 365 | gelf_getshdr(sec, shp); | ||
| 366 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | ||
| 367 | if (!strcmp(name, str)) { | ||
| 368 | if (index) | ||
| 369 | *index = cnt; | ||
| 370 | break; | ||
| 371 | } | ||
| 372 | ++cnt; | ||
| 373 | } | ||
| 374 | |||
| 375 | return sec; | ||
| 376 | } | ||
| 377 | |||
| 378 | #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ | ||
| 379 | for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ | ||
| 380 | idx < nr_entries; \ | ||
| 381 | ++idx, pos = gelf_getrel(reldata, idx, &pos_mem)) | ||
| 382 | |||
| 383 | #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \ | ||
| 384 | for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \ | ||
| 385 | idx < nr_entries; \ | ||
| 386 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | ||
| 387 | |||
| 388 | /* | ||
| 389 | * We need to check if we have a .dynsym, so that we can handle the | ||
| 390 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | ||
| 391 | * .dynsym or .symtab). | ||
| 392 | * And always look at the original dso, not at debuginfo packages, that | ||
| 393 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | ||
| 394 | */ | ||
| 395 | static int dso__synthesize_plt_symbols(struct dso *self, int verbose) | ||
| 396 | { | ||
| 397 | uint32_t nr_rel_entries, idx; | ||
| 398 | GElf_Sym sym; | ||
| 399 | u64 plt_offset; | ||
| 400 | GElf_Shdr shdr_plt; | ||
| 401 | struct symbol *f; | ||
| 402 | GElf_Shdr shdr_rel_plt, shdr_dynsym; | ||
| 403 | Elf_Data *reldata, *syms, *symstrs; | ||
| 404 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; | ||
| 405 | size_t dynsym_idx; | ||
| 406 | GElf_Ehdr ehdr; | ||
| 407 | char sympltname[1024]; | ||
| 408 | Elf *elf; | ||
| 409 | int nr = 0, symidx, fd, err = 0; | ||
| 410 | |||
| 411 | fd = open(self->name, O_RDONLY); | ||
| 412 | if (fd < 0) | ||
| 413 | goto out; | ||
| 414 | |||
| 415 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
| 416 | if (elf == NULL) | ||
| 417 | goto out_close; | ||
| 418 | |||
| 419 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
| 420 | goto out_elf_end; | ||
| 421 | |||
| 422 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, | ||
| 423 | ".dynsym", &dynsym_idx); | ||
| 424 | if (scn_dynsym == NULL) | ||
| 425 | goto out_elf_end; | ||
| 426 | |||
| 427 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
| 428 | ".rela.plt", NULL); | ||
| 429 | if (scn_plt_rel == NULL) { | ||
| 430 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
| 431 | ".rel.plt", NULL); | ||
| 432 | if (scn_plt_rel == NULL) | ||
| 433 | goto out_elf_end; | ||
| 434 | } | ||
| 435 | |||
| 436 | err = -1; | ||
| 437 | |||
| 438 | if (shdr_rel_plt.sh_link != dynsym_idx) | ||
| 439 | goto out_elf_end; | ||
| 440 | |||
| 441 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) | ||
| 442 | goto out_elf_end; | ||
| 443 | |||
| 444 | /* | ||
| 445 | * Fetch the relocation section to find the indexes to the GOT | ||
| 446 | * and the symbols in the .dynsym they refer to. | ||
| 447 | */ | ||
| 448 | reldata = elf_getdata(scn_plt_rel, NULL); | ||
| 449 | if (reldata == NULL) | ||
| 450 | goto out_elf_end; | ||
| 451 | |||
| 452 | syms = elf_getdata(scn_dynsym, NULL); | ||
| 453 | if (syms == NULL) | ||
| 454 | goto out_elf_end; | ||
| 455 | |||
| 456 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); | ||
| 457 | if (scn_symstrs == NULL) | ||
| 458 | goto out_elf_end; | ||
| 459 | |||
| 460 | symstrs = elf_getdata(scn_symstrs, NULL); | ||
| 461 | if (symstrs == NULL) | ||
| 462 | goto out_elf_end; | ||
| 463 | |||
| 464 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | ||
| 465 | plt_offset = shdr_plt.sh_offset; | ||
| 466 | |||
| 467 | if (shdr_rel_plt.sh_type == SHT_RELA) { | ||
| 468 | GElf_Rela pos_mem, *pos; | ||
| 469 | |||
| 470 | elf_section__for_each_rela(reldata, pos, pos_mem, idx, | ||
| 471 | nr_rel_entries) { | ||
| 472 | symidx = GELF_R_SYM(pos->r_info); | ||
| 473 | plt_offset += shdr_plt.sh_entsize; | ||
| 474 | gelf_getsym(syms, symidx, &sym); | ||
| 475 | snprintf(sympltname, sizeof(sympltname), | ||
| 476 | "%s@plt", elf_sym__name(&sym, symstrs)); | ||
| 477 | |||
| 478 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | ||
| 479 | sympltname, self->sym_priv_size, 0, verbose); | ||
| 480 | if (!f) | ||
| 481 | goto out_elf_end; | ||
| 482 | |||
| 483 | dso__insert_symbol(self, f); | ||
| 484 | ++nr; | ||
| 485 | } | ||
| 486 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | ||
| 487 | GElf_Rel pos_mem, *pos; | ||
| 488 | elf_section__for_each_rel(reldata, pos, pos_mem, idx, | ||
| 489 | nr_rel_entries) { | ||
| 490 | symidx = GELF_R_SYM(pos->r_info); | ||
| 491 | plt_offset += shdr_plt.sh_entsize; | ||
| 492 | gelf_getsym(syms, symidx, &sym); | ||
| 493 | snprintf(sympltname, sizeof(sympltname), | ||
| 494 | "%s@plt", elf_sym__name(&sym, symstrs)); | ||
| 495 | |||
| 496 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | ||
| 497 | sympltname, self->sym_priv_size, 0, verbose); | ||
| 498 | if (!f) | ||
| 499 | goto out_elf_end; | ||
| 500 | |||
| 501 | dso__insert_symbol(self, f); | ||
| 502 | ++nr; | ||
| 503 | } | ||
| 504 | } | ||
| 505 | |||
| 506 | err = 0; | ||
| 507 | out_elf_end: | ||
| 508 | elf_end(elf); | ||
| 509 | out_close: | ||
| 510 | close(fd); | ||
| 511 | |||
| 512 | if (err == 0) | ||
| 513 | return nr; | ||
| 514 | out: | ||
| 515 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | ||
| 516 | __func__, self->name); | ||
| 517 | return 0; | ||
| 518 | } | ||
| 519 | |||
| 520 | static int dso__load_sym(struct dso *self, int fd, const char *name, | ||
| 521 | symbol_filter_t filter, int verbose, struct module *mod) | ||
| 522 | { | ||
| 523 | Elf_Data *symstrs, *secstrs; | ||
| 524 | uint32_t nr_syms; | ||
| 525 | int err = -1; | ||
| 526 | uint32_t index; | ||
| 527 | GElf_Ehdr ehdr; | ||
| 528 | GElf_Shdr shdr; | ||
| 529 | Elf_Data *syms; | ||
| 530 | GElf_Sym sym; | ||
| 531 | Elf_Scn *sec, *sec_strndx; | ||
| 532 | Elf *elf; | ||
| 533 | int nr = 0, kernel = !strcmp("[kernel]", self->name); | ||
| 534 | |||
| 535 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
| 536 | if (elf == NULL) { | ||
| 537 | if (verbose) | ||
| 538 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
| 539 | __func__, name); | ||
| 540 | goto out_close; | ||
| 541 | } | ||
| 542 | |||
| 543 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
| 544 | if (verbose) | ||
| 545 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
| 546 | goto out_elf_end; | ||
| 547 | } | ||
| 548 | |||
| 549 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | ||
| 550 | if (sec == NULL) { | ||
| 551 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | ||
| 552 | if (sec == NULL) | ||
| 553 | goto out_elf_end; | ||
| 554 | } | ||
| 555 | |||
| 556 | syms = elf_getdata(sec, NULL); | ||
| 557 | if (syms == NULL) | ||
| 558 | goto out_elf_end; | ||
| 559 | |||
| 560 | sec = elf_getscn(elf, shdr.sh_link); | ||
| 561 | if (sec == NULL) | ||
| 562 | goto out_elf_end; | ||
| 563 | |||
| 564 | symstrs = elf_getdata(sec, NULL); | ||
| 565 | if (symstrs == NULL) | ||
| 566 | goto out_elf_end; | ||
| 567 | |||
| 568 | sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); | ||
| 569 | if (sec_strndx == NULL) | ||
| 570 | goto out_elf_end; | ||
| 571 | |||
| 572 | secstrs = elf_getdata(sec_strndx, NULL); | ||
| 573 | if (secstrs == NULL) | ||
| 574 | goto out_elf_end; | ||
| 575 | |||
| 576 | nr_syms = shdr.sh_size / shdr.sh_entsize; | ||
| 577 | |||
| 578 | memset(&sym, 0, sizeof(sym)); | ||
| 579 | if (!kernel) { | ||
| 580 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | ||
| 581 | elf_section_by_name(elf, &ehdr, &shdr, | ||
| 582 | ".gnu.prelink_undo", | ||
| 583 | NULL) != NULL); | ||
| 584 | } else self->adjust_symbols = 0; | ||
| 585 | |||
| 586 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { | ||
| 587 | struct symbol *f; | ||
| 588 | const char *name; | ||
| 589 | char *demangled; | ||
| 590 | u64 obj_start; | ||
| 591 | struct section *section = NULL; | ||
| 592 | int is_label = elf_sym__is_label(&sym); | ||
| 593 | const char *section_name; | ||
| 594 | |||
| 595 | if (!is_label && !elf_sym__is_function(&sym)) | ||
| 596 | continue; | ||
| 597 | |||
| 598 | sec = elf_getscn(elf, sym.st_shndx); | ||
| 599 | if (!sec) | ||
| 600 | goto out_elf_end; | ||
| 601 | |||
| 602 | gelf_getshdr(sec, &shdr); | ||
| 603 | |||
| 604 | if (is_label && !elf_sec__is_text(&shdr, secstrs)) | ||
| 605 | continue; | ||
| 606 | |||
| 607 | section_name = elf_sec__name(&shdr, secstrs); | ||
| 608 | obj_start = sym.st_value; | ||
| 609 | |||
| 610 | if (self->adjust_symbols) { | ||
| 611 | if (verbose >= 2) | ||
| 612 | printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n", | ||
| 613 | (u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset); | ||
| 614 | |||
| 615 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
| 616 | } | ||
| 617 | |||
| 618 | if (mod) { | ||
| 619 | section = mod->sections->find_section(mod->sections, section_name); | ||
| 620 | if (section) | ||
| 621 | sym.st_value += section->vma; | ||
| 622 | else { | ||
| 623 | fprintf(stderr, "dso__load_sym() module %s lookup of %s failed\n", | ||
| 624 | mod->name, section_name); | ||
| 625 | goto out_elf_end; | ||
| 626 | } | ||
| 627 | } | ||
| 628 | /* | ||
| 629 | * We need to figure out if the object was created from C++ sources | ||
| 630 | * DWARF DW_compile_unit has this, but we don't always have access | ||
| 631 | * to it... | ||
| 632 | */ | ||
| 633 | name = elf_sym__name(&sym, symstrs); | ||
| 634 | demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI); | ||
| 635 | if (demangled != NULL) | ||
| 636 | name = demangled; | ||
| 637 | |||
| 638 | f = symbol__new(sym.st_value, sym.st_size, name, | ||
| 639 | self->sym_priv_size, obj_start, verbose); | ||
| 640 | free(demangled); | ||
| 641 | if (!f) | ||
| 642 | goto out_elf_end; | ||
| 643 | |||
| 644 | if (filter && filter(self, f)) | ||
| 645 | symbol__delete(f, self->sym_priv_size); | ||
| 646 | else { | ||
| 647 | f->module = mod; | ||
| 648 | dso__insert_symbol(self, f); | ||
| 649 | nr++; | ||
| 650 | } | ||
| 651 | } | ||
| 652 | |||
| 653 | err = nr; | ||
| 654 | out_elf_end: | ||
| 655 | elf_end(elf); | ||
| 656 | out_close: | ||
| 657 | return err; | ||
| 658 | } | ||
| 659 | |||
| 660 | #define BUILD_ID_SIZE 128 | ||
| 661 | |||
| 662 | static char *dso__read_build_id(struct dso *self, int verbose) | ||
| 663 | { | ||
| 664 | int i; | ||
| 665 | GElf_Ehdr ehdr; | ||
| 666 | GElf_Shdr shdr; | ||
| 667 | Elf_Data *build_id_data; | ||
| 668 | Elf_Scn *sec; | ||
| 669 | char *build_id = NULL, *bid; | ||
| 670 | unsigned char *raw; | ||
| 671 | Elf *elf; | ||
| 672 | int fd = open(self->name, O_RDONLY); | ||
| 673 | |||
| 674 | if (fd < 0) | ||
| 675 | goto out; | ||
| 676 | |||
| 677 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
| 678 | if (elf == NULL) { | ||
| 679 | if (verbose) | ||
| 680 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
| 681 | __func__, self->name); | ||
| 682 | goto out_close; | ||
| 683 | } | ||
| 684 | |||
| 685 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
| 686 | if (verbose) | ||
| 687 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
| 688 | goto out_elf_end; | ||
| 689 | } | ||
| 690 | |||
| 691 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); | ||
| 692 | if (sec == NULL) | ||
| 693 | goto out_elf_end; | ||
| 694 | |||
| 695 | build_id_data = elf_getdata(sec, NULL); | ||
| 696 | if (build_id_data == NULL) | ||
| 697 | goto out_elf_end; | ||
| 698 | build_id = malloc(BUILD_ID_SIZE); | ||
| 699 | if (build_id == NULL) | ||
| 700 | goto out_elf_end; | ||
| 701 | raw = build_id_data->d_buf + 16; | ||
| 702 | bid = build_id; | ||
| 703 | |||
| 704 | for (i = 0; i < 20; ++i) { | ||
| 705 | sprintf(bid, "%02x", *raw); | ||
| 706 | ++raw; | ||
| 707 | bid += 2; | ||
| 708 | } | ||
| 709 | if (verbose >= 2) | ||
| 710 | printf("%s(%s): %s\n", __func__, self->name, build_id); | ||
| 711 | out_elf_end: | ||
| 712 | elf_end(elf); | ||
| 713 | out_close: | ||
| 714 | close(fd); | ||
| 715 | out: | ||
| 716 | return build_id; | ||
| 717 | } | ||
| 718 | |||
| 719 | char dso__symtab_origin(const struct dso *self) | ||
| 720 | { | ||
| 721 | static const char origin[] = { | ||
| 722 | [DSO__ORIG_KERNEL] = 'k', | ||
| 723 | [DSO__ORIG_JAVA_JIT] = 'j', | ||
| 724 | [DSO__ORIG_FEDORA] = 'f', | ||
| 725 | [DSO__ORIG_UBUNTU] = 'u', | ||
| 726 | [DSO__ORIG_BUILDID] = 'b', | ||
| 727 | [DSO__ORIG_DSO] = 'd', | ||
| 728 | }; | ||
| 729 | |||
| 730 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | ||
| 731 | return '!'; | ||
| 732 | return origin[self->origin]; | ||
| 733 | } | ||
| 734 | |||
| 735 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose) | ||
| 736 | { | ||
| 737 | int size = PATH_MAX; | ||
| 738 | char *name = malloc(size), *build_id = NULL; | ||
| 739 | int ret = -1; | ||
| 740 | int fd; | ||
| 741 | |||
| 742 | if (!name) | ||
| 743 | return -1; | ||
| 744 | |||
| 745 | self->adjust_symbols = 0; | ||
| 746 | |||
| 747 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { | ||
| 748 | ret = dso__load_perf_map(self, filter, verbose); | ||
| 749 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | ||
| 750 | DSO__ORIG_NOT_FOUND; | ||
| 751 | return ret; | ||
| 752 | } | ||
| 753 | |||
| 754 | self->origin = DSO__ORIG_FEDORA - 1; | ||
| 755 | |||
| 756 | more: | ||
| 757 | do { | ||
| 758 | self->origin++; | ||
| 759 | switch (self->origin) { | ||
| 760 | case DSO__ORIG_FEDORA: | ||
| 761 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | ||
| 762 | break; | ||
| 763 | case DSO__ORIG_UBUNTU: | ||
| 764 | snprintf(name, size, "/usr/lib/debug%s", self->name); | ||
| 765 | break; | ||
| 766 | case DSO__ORIG_BUILDID: | ||
| 767 | build_id = dso__read_build_id(self, verbose); | ||
| 768 | if (build_id != NULL) { | ||
| 769 | snprintf(name, size, | ||
| 770 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | ||
| 771 | build_id, build_id + 2); | ||
| 772 | free(build_id); | ||
| 773 | break; | ||
| 774 | } | ||
| 775 | self->origin++; | ||
| 776 | /* Fall thru */ | ||
| 777 | case DSO__ORIG_DSO: | ||
| 778 | snprintf(name, size, "%s", self->name); | ||
| 779 | break; | ||
| 780 | |||
| 781 | default: | ||
| 782 | goto out; | ||
| 783 | } | ||
| 784 | |||
| 785 | fd = open(name, O_RDONLY); | ||
| 786 | } while (fd < 0); | ||
| 787 | |||
| 788 | ret = dso__load_sym(self, fd, name, filter, verbose, NULL); | ||
| 789 | close(fd); | ||
| 790 | |||
| 791 | /* | ||
| 792 | * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? | ||
| 793 | */ | ||
| 794 | if (!ret) | ||
| 795 | goto more; | ||
| 796 | |||
| 797 | if (ret > 0) { | ||
| 798 | int nr_plt = dso__synthesize_plt_symbols(self, verbose); | ||
| 799 | if (nr_plt > 0) | ||
| 800 | ret += nr_plt; | ||
| 801 | } | ||
| 802 | out: | ||
| 803 | free(name); | ||
| 804 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) | ||
| 805 | return 0; | ||
| 806 | return ret; | ||
| 807 | } | ||
| 808 | |||
| 809 | static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name, | ||
| 810 | symbol_filter_t filter, int verbose) | ||
| 811 | { | ||
| 812 | struct module *mod = mod_dso__find_module(mods, name); | ||
| 813 | int err = 0, fd; | ||
| 814 | |||
| 815 | if (mod == NULL || !mod->active) | ||
| 816 | return err; | ||
| 817 | |||
| 818 | fd = open(mod->path, O_RDONLY); | ||
| 819 | |||
| 820 | if (fd < 0) | ||
| 821 | return err; | ||
| 822 | |||
| 823 | err = dso__load_sym(self, fd, name, filter, verbose, mod); | ||
| 824 | close(fd); | ||
| 825 | |||
| 826 | return err; | ||
| 827 | } | ||
| 828 | |||
| 829 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose) | ||
| 830 | { | ||
| 831 | struct mod_dso *mods = mod_dso__new_dso("modules"); | ||
| 832 | struct module *pos; | ||
| 833 | struct rb_node *next; | ||
| 834 | int err; | ||
| 835 | |||
| 836 | err = mod_dso__load_modules(mods); | ||
| 837 | |||
| 838 | if (err <= 0) | ||
| 839 | return err; | ||
| 840 | |||
| 841 | /* | ||
| 842 | * Iterate over modules, and load active symbols. | ||
| 843 | */ | ||
| 844 | next = rb_first(&mods->mods); | ||
| 845 | while (next) { | ||
| 846 | pos = rb_entry(next, struct module, rb_node); | ||
| 847 | err = dso__load_module(self, mods, pos->name, filter, verbose); | ||
| 848 | |||
| 849 | if (err < 0) | ||
| 850 | break; | ||
| 851 | |||
| 852 | next = rb_next(&pos->rb_node); | ||
| 853 | } | ||
| 854 | |||
| 855 | if (err < 0) { | ||
| 856 | mod_dso__delete_modules(mods); | ||
| 857 | mod_dso__delete_self(mods); | ||
| 858 | } | ||
| 859 | |||
| 860 | return err; | ||
| 861 | } | ||
| 862 | |||
| 863 | static inline void dso__fill_symbol_holes(struct dso *self) | ||
| 864 | { | ||
| 865 | struct symbol *prev = NULL; | ||
| 866 | struct rb_node *nd; | ||
| 867 | |||
| 868 | for (nd = rb_last(&self->syms); nd; nd = rb_prev(nd)) { | ||
| 869 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
| 870 | |||
| 871 | if (prev) { | ||
| 872 | u64 hole = 0; | ||
| 873 | int alias = pos->start == prev->start; | ||
| 874 | |||
| 875 | if (!alias) | ||
| 876 | hole = prev->start - pos->end - 1; | ||
| 877 | |||
| 878 | if (hole || alias) { | ||
| 879 | if (alias) | ||
| 880 | pos->end = prev->end; | ||
| 881 | else if (hole) | ||
| 882 | pos->end = prev->start - 1; | ||
| 883 | } | ||
| 884 | } | ||
| 885 | prev = pos; | ||
| 886 | } | ||
| 887 | } | ||
| 888 | |||
| 889 | static int dso__load_vmlinux(struct dso *self, const char *vmlinux, | ||
| 890 | symbol_filter_t filter, int verbose) | ||
| 891 | { | ||
| 892 | int err, fd = open(vmlinux, O_RDONLY); | ||
| 893 | |||
| 894 | if (fd < 0) | ||
| 895 | return -1; | ||
| 896 | |||
| 897 | err = dso__load_sym(self, fd, vmlinux, filter, verbose, NULL); | ||
| 898 | |||
| 899 | if (err > 0) | ||
| 900 | dso__fill_symbol_holes(self); | ||
| 901 | |||
| 902 | close(fd); | ||
| 903 | |||
| 904 | return err; | ||
| 905 | } | ||
| 906 | |||
| 907 | int dso__load_kernel(struct dso *self, const char *vmlinux, | ||
| 908 | symbol_filter_t filter, int verbose, int modules) | ||
| 909 | { | ||
| 910 | int err = -1; | ||
| 911 | |||
| 912 | if (vmlinux) { | ||
| 913 | err = dso__load_vmlinux(self, vmlinux, filter, verbose); | ||
| 914 | if (err > 0 && modules) | ||
| 915 | err = dso__load_modules(self, filter, verbose); | ||
| 916 | } | ||
| 917 | |||
| 918 | if (err <= 0) | ||
| 919 | err = dso__load_kallsyms(self, filter, verbose); | ||
| 920 | |||
| 921 | if (err > 0) | ||
| 922 | self->origin = DSO__ORIG_KERNEL; | ||
| 923 | |||
| 924 | return err; | ||
| 925 | } | ||
| 926 | |||
| 927 | void symbol__init(void) | ||
| 928 | { | ||
| 929 | elf_version(EV_CURRENT); | ||
| 930 | } | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h new file mode 100644 index 000000000000..b53bf0125c1b --- /dev/null +++ b/tools/perf/util/symbol.h | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | #ifndef _PERF_SYMBOL_ | ||
| 2 | #define _PERF_SYMBOL_ 1 | ||
| 3 | |||
| 4 | #include <linux/types.h> | ||
| 5 | #include "types.h" | ||
| 6 | #include <linux/list.h> | ||
| 7 | #include <linux/rbtree.h> | ||
| 8 | #include "module.h" | ||
| 9 | |||
| 10 | #ifdef HAVE_CPLUS_DEMANGLE | ||
| 11 | extern char *cplus_demangle(const char *, int); | ||
| 12 | |||
| 13 | static inline char *bfd_demangle(void __used *v, const char *c, int i) | ||
| 14 | { | ||
| 15 | return cplus_demangle(c, i); | ||
| 16 | } | ||
| 17 | #else | ||
| 18 | #ifdef NO_DEMANGLE | ||
| 19 | static inline char *bfd_demangle(void __used *v, const char __used *c, | ||
| 20 | int __used i) | ||
| 21 | { | ||
| 22 | return NULL; | ||
| 23 | } | ||
| 24 | #else | ||
| 25 | #include <bfd.h> | ||
| 26 | #endif | ||
| 27 | #endif | ||
| 28 | |||
| 29 | #ifndef DMGL_PARAMS | ||
| 30 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | ||
| 31 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | ||
| 32 | #endif | ||
| 33 | |||
| 34 | struct symbol { | ||
| 35 | struct rb_node rb_node; | ||
| 36 | u64 start; | ||
| 37 | u64 end; | ||
| 38 | u64 obj_start; | ||
| 39 | u64 hist_sum; | ||
| 40 | u64 *hist; | ||
| 41 | struct module *module; | ||
| 42 | void *priv; | ||
| 43 | char name[0]; | ||
| 44 | }; | ||
| 45 | |||
| 46 | struct dso { | ||
| 47 | struct list_head node; | ||
| 48 | struct rb_root syms; | ||
| 49 | struct symbol *(*find_symbol)(struct dso *, u64 ip); | ||
| 50 | unsigned int sym_priv_size; | ||
| 51 | unsigned char adjust_symbols; | ||
| 52 | unsigned char slen_calculated; | ||
| 53 | unsigned char origin; | ||
| 54 | char name[0]; | ||
| 55 | }; | ||
| 56 | |||
| 57 | const char *sym_hist_filter; | ||
| 58 | |||
| 59 | typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym); | ||
| 60 | |||
| 61 | struct dso *dso__new(const char *name, unsigned int sym_priv_size); | ||
| 62 | void dso__delete(struct dso *self); | ||
| 63 | |||
| 64 | static inline void *dso__sym_priv(struct dso *self, struct symbol *sym) | ||
| 65 | { | ||
| 66 | return ((void *)sym) - self->sym_priv_size; | ||
| 67 | } | ||
| 68 | |||
| 69 | struct symbol *dso__find_symbol(struct dso *self, u64 ip); | ||
| 70 | |||
| 71 | int dso__load_kernel(struct dso *self, const char *vmlinux, | ||
| 72 | symbol_filter_t filter, int verbose, int modules); | ||
| 73 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose); | ||
| 74 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose); | ||
| 75 | |||
| 76 | size_t dso__fprintf(struct dso *self, FILE *fp); | ||
| 77 | char dso__symtab_origin(const struct dso *self); | ||
| 78 | |||
| 79 | void symbol__init(void); | ||
| 80 | #endif /* _PERF_SYMBOL_ */ | ||
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h new file mode 100644 index 000000000000..5e75f9005940 --- /dev/null +++ b/tools/perf/util/types.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #ifndef _PERF_TYPES_H | ||
| 2 | #define _PERF_TYPES_H | ||
| 3 | |||
| 4 | /* | ||
| 5 | * We define u64 as unsigned long long for every architecture | ||
| 6 | * so that we can print it with %Lx without getting warnings. | ||
| 7 | */ | ||
| 8 | typedef unsigned long long u64; | ||
| 9 | typedef signed long long s64; | ||
| 10 | typedef unsigned int u32; | ||
| 11 | typedef signed int s32; | ||
| 12 | typedef unsigned short u16; | ||
| 13 | typedef signed short s16; | ||
| 14 | typedef unsigned char u8; | ||
| 15 | typedef signed char s8; | ||
| 16 | |||
| 17 | #endif /* _PERF_TYPES_H */ | ||
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c new file mode 100644 index 000000000000..e16bf9a707e8 --- /dev/null +++ b/tools/perf/util/usage.c | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | /* | ||
| 2 | * GIT - The information manager from hell | ||
| 3 | * | ||
| 4 | * Copyright (C) Linus Torvalds, 2005 | ||
| 5 | */ | ||
| 6 | #include "util.h" | ||
| 7 | |||
| 8 | static void report(const char *prefix, const char *err, va_list params) | ||
| 9 | { | ||
| 10 | char msg[1024]; | ||
| 11 | vsnprintf(msg, sizeof(msg), err, params); | ||
| 12 | fprintf(stderr, " %s%s\n", prefix, msg); | ||
| 13 | } | ||
| 14 | |||
| 15 | static NORETURN void usage_builtin(const char *err) | ||
| 16 | { | ||
| 17 | fprintf(stderr, "\n Usage: %s\n", err); | ||
| 18 | exit(129); | ||
| 19 | } | ||
| 20 | |||
| 21 | static NORETURN void die_builtin(const char *err, va_list params) | ||
| 22 | { | ||
| 23 | report(" Fatal: ", err, params); | ||
| 24 | exit(128); | ||
| 25 | } | ||
| 26 | |||
| 27 | static void error_builtin(const char *err, va_list params) | ||
| 28 | { | ||
| 29 | report(" Error: ", err, params); | ||
| 30 | } | ||
| 31 | |||
| 32 | static void warn_builtin(const char *warn, va_list params) | ||
| 33 | { | ||
| 34 | report(" Warning: ", warn, params); | ||
| 35 | } | ||
| 36 | |||
| 37 | /* If we are in a dlopen()ed .so write to a global variable would segfault | ||
| 38 | * (ugh), so keep things static. */ | ||
| 39 | static void (*usage_routine)(const char *err) NORETURN = usage_builtin; | ||
| 40 | static void (*die_routine)(const char *err, va_list params) NORETURN = die_builtin; | ||
| 41 | static void (*error_routine)(const char *err, va_list params) = error_builtin; | ||
| 42 | static void (*warn_routine)(const char *err, va_list params) = warn_builtin; | ||
| 43 | |||
| 44 | void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN) | ||
| 45 | { | ||
| 46 | die_routine = routine; | ||
| 47 | } | ||
| 48 | |||
| 49 | void usage(const char *err) | ||
| 50 | { | ||
| 51 | usage_routine(err); | ||
| 52 | } | ||
| 53 | |||
| 54 | void die(const char *err, ...) | ||
| 55 | { | ||
| 56 | va_list params; | ||
| 57 | |||
| 58 | va_start(params, err); | ||
| 59 | die_routine(err, params); | ||
| 60 | va_end(params); | ||
| 61 | } | ||
| 62 | |||
| 63 | int error(const char *err, ...) | ||
| 64 | { | ||
| 65 | va_list params; | ||
| 66 | |||
| 67 | va_start(params, err); | ||
| 68 | error_routine(err, params); | ||
| 69 | va_end(params); | ||
| 70 | return -1; | ||
| 71 | } | ||
| 72 | |||
| 73 | void warning(const char *warn, ...) | ||
| 74 | { | ||
| 75 | va_list params; | ||
| 76 | |||
| 77 | va_start(params, warn); | ||
| 78 | warn_routine(warn, params); | ||
| 79 | va_end(params); | ||
| 80 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h new file mode 100644 index 000000000000..68fe157d72fb --- /dev/null +++ b/tools/perf/util/util.h | |||
| @@ -0,0 +1,395 @@ | |||
| 1 | #ifndef GIT_COMPAT_UTIL_H | ||
| 2 | #define GIT_COMPAT_UTIL_H | ||
| 3 | |||
| 4 | #define _FILE_OFFSET_BITS 64 | ||
| 5 | |||
| 6 | #ifndef FLEX_ARRAY | ||
| 7 | /* | ||
| 8 | * See if our compiler is known to support flexible array members. | ||
| 9 | */ | ||
| 10 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | ||
| 11 | # define FLEX_ARRAY /* empty */ | ||
| 12 | #elif defined(__GNUC__) | ||
| 13 | # if (__GNUC__ >= 3) | ||
| 14 | # define FLEX_ARRAY /* empty */ | ||
| 15 | # else | ||
| 16 | # define FLEX_ARRAY 0 /* older GNU extension */ | ||
| 17 | # endif | ||
| 18 | #endif | ||
| 19 | |||
| 20 | /* | ||
| 21 | * Otherwise, default to safer but a bit wasteful traditional style | ||
| 22 | */ | ||
| 23 | #ifndef FLEX_ARRAY | ||
| 24 | # define FLEX_ARRAY 1 | ||
| 25 | #endif | ||
| 26 | #endif | ||
| 27 | |||
| 28 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) | ||
| 29 | |||
| 30 | #ifdef __GNUC__ | ||
| 31 | #define TYPEOF(x) (__typeof__(x)) | ||
| 32 | #else | ||
| 33 | #define TYPEOF(x) | ||
| 34 | #endif | ||
| 35 | |||
| 36 | #define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (sizeof(x) * 8 - (bits)))) | ||
| 37 | #define HAS_MULTI_BITS(i) ((i) & ((i) - 1)) /* checks if an integer has more than 1 bit set */ | ||
| 38 | |||
| 39 | /* Approximation of the length of the decimal representation of this type. */ | ||
| 40 | #define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) | ||
| 41 | |||
| 42 | #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) | ||
| 43 | #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */ | ||
| 44 | #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */ | ||
| 45 | #endif | ||
| 46 | #define _ALL_SOURCE 1 | ||
| 47 | #define _GNU_SOURCE 1 | ||
| 48 | #define _BSD_SOURCE 1 | ||
| 49 | |||
| 50 | #include <unistd.h> | ||
| 51 | #include <stdio.h> | ||
| 52 | #include <sys/stat.h> | ||
| 53 | #include <sys/statfs.h> | ||
| 54 | #include <fcntl.h> | ||
| 55 | #include <stddef.h> | ||
| 56 | #include <stdlib.h> | ||
| 57 | #include <stdarg.h> | ||
| 58 | #include <string.h> | ||
| 59 | #include <errno.h> | ||
| 60 | #include <limits.h> | ||
| 61 | #include <sys/param.h> | ||
| 62 | #include <sys/types.h> | ||
| 63 | #include <dirent.h> | ||
| 64 | #include <sys/time.h> | ||
| 65 | #include <time.h> | ||
| 66 | #include <signal.h> | ||
| 67 | #include <fnmatch.h> | ||
| 68 | #include <assert.h> | ||
| 69 | #include <regex.h> | ||
| 70 | #include <utime.h> | ||
| 71 | #include <sys/wait.h> | ||
| 72 | #include <sys/poll.h> | ||
| 73 | #include <sys/socket.h> | ||
| 74 | #include <sys/ioctl.h> | ||
| 75 | #ifndef NO_SYS_SELECT_H | ||
| 76 | #include <sys/select.h> | ||
| 77 | #endif | ||
| 78 | #include <netinet/in.h> | ||
| 79 | #include <netinet/tcp.h> | ||
| 80 | #include <arpa/inet.h> | ||
| 81 | #include <netdb.h> | ||
| 82 | #include <pwd.h> | ||
| 83 | #include <inttypes.h> | ||
| 84 | #include "../../../include/linux/magic.h" | ||
| 85 | |||
| 86 | #ifndef NO_ICONV | ||
| 87 | #include <iconv.h> | ||
| 88 | #endif | ||
| 89 | |||
| 90 | /* On most systems <limits.h> would have given us this, but | ||
| 91 | * not on some systems (e.g. GNU/Hurd). | ||
| 92 | */ | ||
| 93 | #ifndef PATH_MAX | ||
| 94 | #define PATH_MAX 4096 | ||
| 95 | #endif | ||
| 96 | |||
| 97 | #ifndef PRIuMAX | ||
| 98 | #define PRIuMAX "llu" | ||
| 99 | #endif | ||
| 100 | |||
| 101 | #ifndef PRIu32 | ||
| 102 | #define PRIu32 "u" | ||
| 103 | #endif | ||
| 104 | |||
| 105 | #ifndef PRIx32 | ||
| 106 | #define PRIx32 "x" | ||
| 107 | #endif | ||
| 108 | |||
| 109 | #ifndef PATH_SEP | ||
| 110 | #define PATH_SEP ':' | ||
| 111 | #endif | ||
| 112 | |||
| 113 | #ifndef STRIP_EXTENSION | ||
| 114 | #define STRIP_EXTENSION "" | ||
| 115 | #endif | ||
| 116 | |||
| 117 | #ifndef has_dos_drive_prefix | ||
| 118 | #define has_dos_drive_prefix(path) 0 | ||
| 119 | #endif | ||
| 120 | |||
| 121 | #ifndef is_dir_sep | ||
| 122 | #define is_dir_sep(c) ((c) == '/') | ||
| 123 | #endif | ||
| 124 | |||
| 125 | #ifdef __GNUC__ | ||
| 126 | #define NORETURN __attribute__((__noreturn__)) | ||
| 127 | #else | ||
| 128 | #define NORETURN | ||
| 129 | #ifndef __attribute__ | ||
| 130 | #define __attribute__(x) | ||
| 131 | #endif | ||
| 132 | #endif | ||
| 133 | |||
| 134 | /* General helper functions */ | ||
| 135 | extern void usage(const char *err) NORETURN; | ||
| 136 | extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); | ||
| 137 | extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); | ||
| 138 | extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); | ||
| 139 | |||
| 140 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); | ||
| 141 | |||
| 142 | extern int prefixcmp(const char *str, const char *prefix); | ||
| 143 | extern time_t tm_to_time_t(const struct tm *tm); | ||
| 144 | |||
| 145 | static inline const char *skip_prefix(const char *str, const char *prefix) | ||
| 146 | { | ||
| 147 | size_t len = strlen(prefix); | ||
| 148 | return strncmp(str, prefix, len) ? NULL : str + len; | ||
| 149 | } | ||
| 150 | |||
| 151 | #if defined(NO_MMAP) || defined(USE_WIN32_MMAP) | ||
| 152 | |||
| 153 | #ifndef PROT_READ | ||
| 154 | #define PROT_READ 1 | ||
| 155 | #define PROT_WRITE 2 | ||
| 156 | #define MAP_PRIVATE 1 | ||
| 157 | #define MAP_FAILED ((void*)-1) | ||
| 158 | #endif | ||
| 159 | |||
| 160 | #define mmap git_mmap | ||
| 161 | #define munmap git_munmap | ||
| 162 | extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); | ||
| 163 | extern int git_munmap(void *start, size_t length); | ||
| 164 | |||
| 165 | #else /* NO_MMAP || USE_WIN32_MMAP */ | ||
| 166 | |||
| 167 | #include <sys/mman.h> | ||
| 168 | |||
| 169 | #endif /* NO_MMAP || USE_WIN32_MMAP */ | ||
| 170 | |||
| 171 | #ifdef NO_MMAP | ||
| 172 | |||
| 173 | /* This value must be multiple of (pagesize * 2) */ | ||
| 174 | #define DEFAULT_PACKED_GIT_WINDOW_SIZE (1 * 1024 * 1024) | ||
| 175 | |||
| 176 | #else /* NO_MMAP */ | ||
| 177 | |||
| 178 | /* This value must be multiple of (pagesize * 2) */ | ||
| 179 | #define DEFAULT_PACKED_GIT_WINDOW_SIZE \ | ||
| 180 | (sizeof(void*) >= 8 \ | ||
| 181 | ? 1 * 1024 * 1024 * 1024 \ | ||
| 182 | : 32 * 1024 * 1024) | ||
| 183 | |||
| 184 | #endif /* NO_MMAP */ | ||
| 185 | |||
| 186 | #ifdef NO_ST_BLOCKS_IN_STRUCT_STAT | ||
| 187 | #define on_disk_bytes(st) ((st).st_size) | ||
| 188 | #else | ||
| 189 | #define on_disk_bytes(st) ((st).st_blocks * 512) | ||
| 190 | #endif | ||
| 191 | |||
| 192 | #define DEFAULT_PACKED_GIT_LIMIT \ | ||
| 193 | ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256)) | ||
| 194 | |||
| 195 | #ifdef NO_PREAD | ||
| 196 | #define pread git_pread | ||
| 197 | extern ssize_t git_pread(int fd, void *buf, size_t count, off_t offset); | ||
| 198 | #endif | ||
| 199 | /* | ||
| 200 | * Forward decl that will remind us if its twin in cache.h changes. | ||
| 201 | * This function is used in compat/pread.c. But we can't include | ||
| 202 | * cache.h there. | ||
| 203 | */ | ||
| 204 | extern ssize_t read_in_full(int fd, void *buf, size_t count); | ||
| 205 | |||
| 206 | #ifdef NO_SETENV | ||
| 207 | #define setenv gitsetenv | ||
| 208 | extern int gitsetenv(const char *, const char *, int); | ||
| 209 | #endif | ||
| 210 | |||
| 211 | #ifdef NO_MKDTEMP | ||
| 212 | #define mkdtemp gitmkdtemp | ||
| 213 | extern char *gitmkdtemp(char *); | ||
| 214 | #endif | ||
| 215 | |||
| 216 | #ifdef NO_UNSETENV | ||
| 217 | #define unsetenv gitunsetenv | ||
| 218 | extern void gitunsetenv(const char *); | ||
| 219 | #endif | ||
| 220 | |||
| 221 | #ifdef NO_STRCASESTR | ||
| 222 | #define strcasestr gitstrcasestr | ||
| 223 | extern char *gitstrcasestr(const char *haystack, const char *needle); | ||
| 224 | #endif | ||
| 225 | |||
| 226 | #ifdef NO_STRLCPY | ||
| 227 | #define strlcpy gitstrlcpy | ||
| 228 | extern size_t gitstrlcpy(char *, const char *, size_t); | ||
| 229 | #endif | ||
| 230 | |||
| 231 | #ifdef NO_STRTOUMAX | ||
| 232 | #define strtoumax gitstrtoumax | ||
| 233 | extern uintmax_t gitstrtoumax(const char *, char **, int); | ||
| 234 | #endif | ||
| 235 | |||
| 236 | #ifdef NO_HSTRERROR | ||
| 237 | #define hstrerror githstrerror | ||
| 238 | extern const char *githstrerror(int herror); | ||
| 239 | #endif | ||
| 240 | |||
| 241 | #ifdef NO_MEMMEM | ||
| 242 | #define memmem gitmemmem | ||
| 243 | void *gitmemmem(const void *haystack, size_t haystacklen, | ||
| 244 | const void *needle, size_t needlelen); | ||
| 245 | #endif | ||
| 246 | |||
| 247 | #ifdef FREAD_READS_DIRECTORIES | ||
| 248 | #ifdef fopen | ||
| 249 | #undef fopen | ||
| 250 | #endif | ||
| 251 | #define fopen(a,b) git_fopen(a,b) | ||
| 252 | extern FILE *git_fopen(const char*, const char*); | ||
| 253 | #endif | ||
| 254 | |||
| 255 | #ifdef SNPRINTF_RETURNS_BOGUS | ||
| 256 | #define snprintf git_snprintf | ||
| 257 | extern int git_snprintf(char *str, size_t maxsize, | ||
| 258 | const char *format, ...); | ||
| 259 | #define vsnprintf git_vsnprintf | ||
| 260 | extern int git_vsnprintf(char *str, size_t maxsize, | ||
| 261 | const char *format, va_list ap); | ||
| 262 | #endif | ||
| 263 | |||
| 264 | #ifdef __GLIBC_PREREQ | ||
| 265 | #if __GLIBC_PREREQ(2, 1) | ||
| 266 | #define HAVE_STRCHRNUL | ||
| 267 | #endif | ||
| 268 | #endif | ||
| 269 | |||
| 270 | #ifndef HAVE_STRCHRNUL | ||
| 271 | #define strchrnul gitstrchrnul | ||
| 272 | static inline char *gitstrchrnul(const char *s, int c) | ||
| 273 | { | ||
| 274 | while (*s && *s != c) | ||
| 275 | s++; | ||
| 276 | return (char *)s; | ||
| 277 | } | ||
| 278 | #endif | ||
| 279 | |||
| 280 | /* | ||
| 281 | * Wrappers: | ||
| 282 | */ | ||
| 283 | extern char *xstrdup(const char *str); | ||
| 284 | extern void *xmalloc(size_t size); | ||
| 285 | extern void *xmemdupz(const void *data, size_t len); | ||
| 286 | extern char *xstrndup(const char *str, size_t len); | ||
| 287 | extern void *xrealloc(void *ptr, size_t size); | ||
| 288 | extern void *xcalloc(size_t nmemb, size_t size); | ||
| 289 | extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); | ||
| 290 | extern ssize_t xread(int fd, void *buf, size_t len); | ||
| 291 | extern ssize_t xwrite(int fd, const void *buf, size_t len); | ||
| 292 | extern int xdup(int fd); | ||
| 293 | extern FILE *xfdopen(int fd, const char *mode); | ||
| 294 | extern int xmkstemp(char *template); | ||
| 295 | |||
| 296 | static inline size_t xsize_t(off_t len) | ||
| 297 | { | ||
| 298 | return (size_t)len; | ||
| 299 | } | ||
| 300 | |||
| 301 | static inline int has_extension(const char *filename, const char *ext) | ||
| 302 | { | ||
| 303 | size_t len = strlen(filename); | ||
| 304 | size_t extlen = strlen(ext); | ||
| 305 | return len > extlen && !memcmp(filename + len - extlen, ext, extlen); | ||
| 306 | } | ||
| 307 | |||
| 308 | /* Sane ctype - no locale, and works with signed chars */ | ||
| 309 | #undef isascii | ||
| 310 | #undef isspace | ||
| 311 | #undef isdigit | ||
| 312 | #undef isalpha | ||
| 313 | #undef isalnum | ||
| 314 | #undef tolower | ||
| 315 | #undef toupper | ||
| 316 | extern unsigned char sane_ctype[256]; | ||
| 317 | #define GIT_SPACE 0x01 | ||
| 318 | #define GIT_DIGIT 0x02 | ||
| 319 | #define GIT_ALPHA 0x04 | ||
| 320 | #define GIT_GLOB_SPECIAL 0x08 | ||
| 321 | #define GIT_REGEX_SPECIAL 0x10 | ||
| 322 | #define GIT_PRINT_EXTRA 0x20 | ||
| 323 | #define GIT_PRINT 0x3E | ||
| 324 | #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0) | ||
| 325 | #define isascii(x) (((x) & ~0x7f) == 0) | ||
| 326 | #define isspace(x) sane_istest(x,GIT_SPACE) | ||
| 327 | #define isdigit(x) sane_istest(x,GIT_DIGIT) | ||
| 328 | #define isalpha(x) sane_istest(x,GIT_ALPHA) | ||
| 329 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) | ||
| 330 | #define isprint(x) sane_istest(x,GIT_PRINT) | ||
| 331 | #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL) | ||
| 332 | #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL) | ||
| 333 | #define tolower(x) sane_case((unsigned char)(x), 0x20) | ||
| 334 | #define toupper(x) sane_case((unsigned char)(x), 0) | ||
| 335 | |||
| 336 | static inline int sane_case(int x, int high) | ||
| 337 | { | ||
| 338 | if (sane_istest(x, GIT_ALPHA)) | ||
| 339 | x = (x & ~0x20) | high; | ||
| 340 | return x; | ||
| 341 | } | ||
| 342 | |||
| 343 | static inline int strtoul_ui(char const *s, int base, unsigned int *result) | ||
| 344 | { | ||
| 345 | unsigned long ul; | ||
| 346 | char *p; | ||
| 347 | |||
| 348 | errno = 0; | ||
| 349 | ul = strtoul(s, &p, base); | ||
| 350 | if (errno || *p || p == s || (unsigned int) ul != ul) | ||
| 351 | return -1; | ||
| 352 | *result = ul; | ||
| 353 | return 0; | ||
| 354 | } | ||
| 355 | |||
| 356 | static inline int strtol_i(char const *s, int base, int *result) | ||
| 357 | { | ||
| 358 | long ul; | ||
| 359 | char *p; | ||
| 360 | |||
| 361 | errno = 0; | ||
| 362 | ul = strtol(s, &p, base); | ||
| 363 | if (errno || *p || p == s || (int) ul != ul) | ||
| 364 | return -1; | ||
| 365 | *result = ul; | ||
| 366 | return 0; | ||
| 367 | } | ||
| 368 | |||
| 369 | #ifdef INTERNAL_QSORT | ||
| 370 | void git_qsort(void *base, size_t nmemb, size_t size, | ||
| 371 | int(*compar)(const void *, const void *)); | ||
| 372 | #define qsort git_qsort | ||
| 373 | #endif | ||
| 374 | |||
| 375 | #ifndef DIR_HAS_BSD_GROUP_SEMANTICS | ||
| 376 | # define FORCE_DIR_SET_GID S_ISGID | ||
| 377 | #else | ||
| 378 | # define FORCE_DIR_SET_GID 0 | ||
| 379 | #endif | ||
| 380 | |||
| 381 | #ifdef NO_NSEC | ||
| 382 | #undef USE_NSEC | ||
| 383 | #define ST_CTIME_NSEC(st) 0 | ||
| 384 | #define ST_MTIME_NSEC(st) 0 | ||
| 385 | #else | ||
| 386 | #ifdef USE_ST_TIMESPEC | ||
| 387 | #define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec)) | ||
| 388 | #define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec)) | ||
| 389 | #else | ||
| 390 | #define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec)) | ||
| 391 | #define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec)) | ||
| 392 | #endif | ||
| 393 | #endif | ||
| 394 | |||
| 395 | #endif | ||
diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c new file mode 100644 index 000000000000..4574ac28396f --- /dev/null +++ b/tools/perf/util/wrapper.c | |||
| @@ -0,0 +1,207 @@ | |||
| 1 | /* | ||
| 2 | * Various trivial helper wrappers around standard functions | ||
| 3 | */ | ||
| 4 | #include "cache.h" | ||
| 5 | |||
| 6 | /* | ||
| 7 | * There's no pack memory to release - but stay close to the Git | ||
| 8 | * version so wrap this away: | ||
| 9 | */ | ||
| 10 | static inline void release_pack_memory(size_t size __used, int flag __used) | ||
| 11 | { | ||
| 12 | } | ||
| 13 | |||
| 14 | char *xstrdup(const char *str) | ||
| 15 | { | ||
| 16 | char *ret = strdup(str); | ||
| 17 | if (!ret) { | ||
| 18 | release_pack_memory(strlen(str) + 1, -1); | ||
| 19 | ret = strdup(str); | ||
| 20 | if (!ret) | ||
| 21 | die("Out of memory, strdup failed"); | ||
| 22 | } | ||
| 23 | return ret; | ||
| 24 | } | ||
| 25 | |||
| 26 | void *xmalloc(size_t size) | ||
| 27 | { | ||
| 28 | void *ret = malloc(size); | ||
| 29 | if (!ret && !size) | ||
| 30 | ret = malloc(1); | ||
| 31 | if (!ret) { | ||
| 32 | release_pack_memory(size, -1); | ||
| 33 | ret = malloc(size); | ||
| 34 | if (!ret && !size) | ||
| 35 | ret = malloc(1); | ||
| 36 | if (!ret) | ||
| 37 | die("Out of memory, malloc failed"); | ||
| 38 | } | ||
| 39 | #ifdef XMALLOC_POISON | ||
| 40 | memset(ret, 0xA5, size); | ||
| 41 | #endif | ||
| 42 | return ret; | ||
| 43 | } | ||
| 44 | |||
| 45 | /* | ||
| 46 | * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of | ||
| 47 | * "data" to the allocated memory, zero terminates the allocated memory, | ||
| 48 | * and returns a pointer to the allocated memory. If the allocation fails, | ||
| 49 | * the program dies. | ||
| 50 | */ | ||
| 51 | void *xmemdupz(const void *data, size_t len) | ||
| 52 | { | ||
| 53 | char *p = xmalloc(len + 1); | ||
| 54 | memcpy(p, data, len); | ||
| 55 | p[len] = '\0'; | ||
| 56 | return p; | ||
| 57 | } | ||
| 58 | |||
| 59 | char *xstrndup(const char *str, size_t len) | ||
| 60 | { | ||
| 61 | char *p = memchr(str, '\0', len); | ||
| 62 | |||
| 63 | return xmemdupz(str, p ? (size_t)(p - str) : len); | ||
| 64 | } | ||
| 65 | |||
| 66 | void *xrealloc(void *ptr, size_t size) | ||
| 67 | { | ||
| 68 | void *ret = realloc(ptr, size); | ||
| 69 | if (!ret && !size) | ||
| 70 | ret = realloc(ptr, 1); | ||
| 71 | if (!ret) { | ||
| 72 | release_pack_memory(size, -1); | ||
| 73 | ret = realloc(ptr, size); | ||
| 74 | if (!ret && !size) | ||
| 75 | ret = realloc(ptr, 1); | ||
| 76 | if (!ret) | ||
| 77 | die("Out of memory, realloc failed"); | ||
| 78 | } | ||
| 79 | return ret; | ||
| 80 | } | ||
| 81 | |||
| 82 | void *xcalloc(size_t nmemb, size_t size) | ||
| 83 | { | ||
| 84 | void *ret = calloc(nmemb, size); | ||
| 85 | if (!ret && (!nmemb || !size)) | ||
| 86 | ret = calloc(1, 1); | ||
| 87 | if (!ret) { | ||
| 88 | release_pack_memory(nmemb * size, -1); | ||
| 89 | ret = calloc(nmemb, size); | ||
| 90 | if (!ret && (!nmemb || !size)) | ||
| 91 | ret = calloc(1, 1); | ||
| 92 | if (!ret) | ||
| 93 | die("Out of memory, calloc failed"); | ||
| 94 | } | ||
| 95 | return ret; | ||
| 96 | } | ||
| 97 | |||
| 98 | void *xmmap(void *start, size_t length, | ||
| 99 | int prot, int flags, int fd, off_t offset) | ||
| 100 | { | ||
| 101 | void *ret = mmap(start, length, prot, flags, fd, offset); | ||
| 102 | if (ret == MAP_FAILED) { | ||
| 103 | if (!length) | ||
| 104 | return NULL; | ||
| 105 | release_pack_memory(length, fd); | ||
| 106 | ret = mmap(start, length, prot, flags, fd, offset); | ||
| 107 | if (ret == MAP_FAILED) | ||
| 108 | die("Out of memory? mmap failed: %s", strerror(errno)); | ||
| 109 | } | ||
| 110 | return ret; | ||
| 111 | } | ||
| 112 | |||
| 113 | /* | ||
| 114 | * xread() is the same a read(), but it automatically restarts read() | ||
| 115 | * operations with a recoverable error (EAGAIN and EINTR). xread() | ||
| 116 | * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. | ||
| 117 | */ | ||
| 118 | ssize_t xread(int fd, void *buf, size_t len) | ||
| 119 | { | ||
| 120 | ssize_t nr; | ||
| 121 | while (1) { | ||
| 122 | nr = read(fd, buf, len); | ||
| 123 | if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) | ||
| 124 | continue; | ||
| 125 | return nr; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | /* | ||
| 130 | * xwrite() is the same a write(), but it automatically restarts write() | ||
| 131 | * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT | ||
| 132 | * GUARANTEE that "len" bytes is written even if the operation is successful. | ||
| 133 | */ | ||
| 134 | ssize_t xwrite(int fd, const void *buf, size_t len) | ||
| 135 | { | ||
| 136 | ssize_t nr; | ||
| 137 | while (1) { | ||
| 138 | nr = write(fd, buf, len); | ||
| 139 | if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) | ||
| 140 | continue; | ||
| 141 | return nr; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | ssize_t read_in_full(int fd, void *buf, size_t count) | ||
| 146 | { | ||
| 147 | char *p = buf; | ||
| 148 | ssize_t total = 0; | ||
| 149 | |||
| 150 | while (count > 0) { | ||
| 151 | ssize_t loaded = xread(fd, p, count); | ||
| 152 | if (loaded <= 0) | ||
| 153 | return total ? total : loaded; | ||
| 154 | count -= loaded; | ||
| 155 | p += loaded; | ||
| 156 | total += loaded; | ||
| 157 | } | ||
| 158 | |||
| 159 | return total; | ||
| 160 | } | ||
| 161 | |||
| 162 | ssize_t write_in_full(int fd, const void *buf, size_t count) | ||
| 163 | { | ||
| 164 | const char *p = buf; | ||
| 165 | ssize_t total = 0; | ||
| 166 | |||
| 167 | while (count > 0) { | ||
| 168 | ssize_t written = xwrite(fd, p, count); | ||
| 169 | if (written < 0) | ||
| 170 | return -1; | ||
| 171 | if (!written) { | ||
| 172 | errno = ENOSPC; | ||
| 173 | return -1; | ||
| 174 | } | ||
| 175 | count -= written; | ||
| 176 | p += written; | ||
| 177 | total += written; | ||
| 178 | } | ||
| 179 | |||
| 180 | return total; | ||
| 181 | } | ||
| 182 | |||
| 183 | int xdup(int fd) | ||
| 184 | { | ||
| 185 | int ret = dup(fd); | ||
| 186 | if (ret < 0) | ||
| 187 | die("dup failed: %s", strerror(errno)); | ||
| 188 | return ret; | ||
| 189 | } | ||
| 190 | |||
| 191 | FILE *xfdopen(int fd, const char *mode) | ||
| 192 | { | ||
| 193 | FILE *stream = fdopen(fd, mode); | ||
| 194 | if (stream == NULL) | ||
| 195 | die("Out of memory? fdopen failed: %s", strerror(errno)); | ||
| 196 | return stream; | ||
| 197 | } | ||
| 198 | |||
| 199 | int xmkstemp(char *template) | ||
| 200 | { | ||
| 201 | int fd; | ||
| 202 | |||
| 203 | fd = mkstemp(template); | ||
| 204 | if (fd < 0) | ||
| 205 | die("Unable to create temporary file: %s", strerror(errno)); | ||
| 206 | return fd; | ||
| 207 | } | ||
