diff options
| author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-12-26 09:52:54 -0500 |
|---|---|---|
| committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-12-26 09:52:54 -0500 |
| commit | 7f50548abb5454bd82c25aae15f0a3bf6a530f46 (patch) | |
| tree | 175b5d695437151f0f9f778ad8eb7f274468842f /tools/perf/util | |
| parent | b3172f222ab5afdc91ea058bd11c42cf169728f3 (diff) | |
| parent | 6b7b284958d47b77d06745b36bc7f36dab769d9b (diff) | |
Merge commit 'v2.6.33-rc2' into for-2.6.33
Diffstat (limited to 'tools/perf/util')
70 files changed, 7691 insertions, 1475 deletions
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 6f8ea9d210b6..918eb376abe3 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
| @@ -1,10 +1,15 @@ | |||
| 1 | #ifndef CACHE_H | 1 | #ifndef __PERF_CACHE_H |
| 2 | #define CACHE_H | 2 | #define __PERF_CACHE_H |
| 3 | 3 | ||
| 4 | #include "util.h" | 4 | #include "util.h" |
| 5 | #include "strbuf.h" | 5 | #include "strbuf.h" |
| 6 | #include "../perf.h" | 6 | #include "../perf.h" |
| 7 | 7 | ||
| 8 | #define CMD_EXEC_PATH "--exec-path" | ||
| 9 | #define CMD_PERF_DIR "--perf-dir=" | ||
| 10 | #define CMD_WORK_TREE "--work-tree=" | ||
| 11 | #define CMD_DEBUGFS_DIR "--debugfs-dir=" | ||
| 12 | |||
| 8 | #define PERF_DIR_ENVIRONMENT "PERF_DIR" | 13 | #define PERF_DIR_ENVIRONMENT "PERF_DIR" |
| 9 | #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" | 14 | #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" |
| 10 | #define DEFAULT_PERF_DIR_ENVIRONMENT ".perf" | 15 | #define DEFAULT_PERF_DIR_ENVIRONMENT ".perf" |
| @@ -117,4 +122,4 @@ extern char *perf_pathdup(const char *fmt, ...) | |||
| 117 | 122 | ||
| 118 | extern size_t strlcpy(char *dest, const char *src, size_t size); | 123 | extern size_t strlcpy(char *dest, const char *src, size_t size); |
| 119 | 124 | ||
| 120 | #endif /* CACHE_H */ | 125 | #endif /* __PERF_CACHE_H */ |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 3b8380f1b478..b3b71258272a 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
| @@ -206,7 +206,7 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, | |||
| 206 | } | 206 | } |
| 207 | node->val_nr = chain->nr - start; | 207 | node->val_nr = chain->nr - start; |
| 208 | if (!node->val_nr) | 208 | if (!node->val_nr) |
| 209 | printf("Warning: empty node in callchain tree\n"); | 209 | pr_warning("Warning: empty node in callchain tree\n"); |
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | static void | 212 | static void |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 43cf3ea9e088..ad4626de4c2b 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
| @@ -58,4 +58,4 @@ static inline u64 cumul_hits(struct callchain_node *node) | |||
| 58 | int register_callchain_param(struct callchain_param *param); | 58 | int register_callchain_param(struct callchain_param *param); |
| 59 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 59 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, |
| 60 | struct symbol **syms); | 60 | struct symbol **syms); |
| 61 | #endif | 61 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 58d597564b99..24e8809210bb 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef COLOR_H | 1 | #ifndef __PERF_COLOR_H |
| 2 | #define COLOR_H | 2 | #define __PERF_COLOR_H |
| 3 | 3 | ||
| 4 | /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ | 4 | /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ |
| 5 | #define COLOR_MAXLEN 24 | 5 | #define COLOR_MAXLEN 24 |
| @@ -39,4 +39,4 @@ int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *bu | |||
| 39 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); | 39 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); |
| 40 | const char *get_percent_color(double percent); | 40 | const char *get_percent_color(double percent); |
| 41 | 41 | ||
| 42 | #endif /* COLOR_H */ | 42 | #endif /* __PERF_COLOR_H */ |
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c index 0b791bd346bc..35073621e5de 100644 --- a/tools/perf/util/ctype.c +++ b/tools/perf/util/ctype.c | |||
| @@ -29,3 +29,11 @@ unsigned char sane_ctype[256] = { | |||
| 29 | A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */ | 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 */ | 30 | /* Nothing in the 128.. range */ |
| 31 | }; | 31 | }; |
| 32 | |||
| 33 | const char *graph_line = | ||
| 34 | "_____________________________________________________________________" | ||
| 35 | "_____________________________________________________________________"; | ||
| 36 | const char *graph_dotted_line = | ||
| 37 | "---------------------------------------------------------------------" | ||
| 38 | "---------------------------------------------------------------------" | ||
| 39 | "---------------------------------------------------------------------"; | ||
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c new file mode 100644 index 000000000000..b557b836de3d --- /dev/null +++ b/tools/perf/util/data_map.c | |||
| @@ -0,0 +1,252 @@ | |||
| 1 | #include "symbol.h" | ||
| 2 | #include "util.h" | ||
| 3 | #include "debug.h" | ||
| 4 | #include "thread.h" | ||
| 5 | #include "session.h" | ||
| 6 | |||
| 7 | static int process_event_stub(event_t *event __used, | ||
| 8 | struct perf_session *session __used) | ||
| 9 | { | ||
| 10 | dump_printf(": unhandled!\n"); | ||
| 11 | return 0; | ||
| 12 | } | ||
| 13 | |||
| 14 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | ||
| 15 | { | ||
| 16 | if (!handler->process_sample_event) | ||
| 17 | handler->process_sample_event = process_event_stub; | ||
| 18 | if (!handler->process_mmap_event) | ||
| 19 | handler->process_mmap_event = process_event_stub; | ||
| 20 | if (!handler->process_comm_event) | ||
| 21 | handler->process_comm_event = process_event_stub; | ||
| 22 | if (!handler->process_fork_event) | ||
| 23 | handler->process_fork_event = process_event_stub; | ||
| 24 | if (!handler->process_exit_event) | ||
| 25 | handler->process_exit_event = process_event_stub; | ||
| 26 | if (!handler->process_lost_event) | ||
| 27 | handler->process_lost_event = process_event_stub; | ||
| 28 | if (!handler->process_read_event) | ||
| 29 | handler->process_read_event = process_event_stub; | ||
| 30 | if (!handler->process_throttle_event) | ||
| 31 | handler->process_throttle_event = process_event_stub; | ||
| 32 | if (!handler->process_unthrottle_event) | ||
| 33 | handler->process_unthrottle_event = process_event_stub; | ||
| 34 | } | ||
| 35 | |||
| 36 | static const char *event__name[] = { | ||
| 37 | [0] = "TOTAL", | ||
| 38 | [PERF_RECORD_MMAP] = "MMAP", | ||
| 39 | [PERF_RECORD_LOST] = "LOST", | ||
| 40 | [PERF_RECORD_COMM] = "COMM", | ||
| 41 | [PERF_RECORD_EXIT] = "EXIT", | ||
| 42 | [PERF_RECORD_THROTTLE] = "THROTTLE", | ||
| 43 | [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", | ||
| 44 | [PERF_RECORD_FORK] = "FORK", | ||
| 45 | [PERF_RECORD_READ] = "READ", | ||
| 46 | [PERF_RECORD_SAMPLE] = "SAMPLE", | ||
| 47 | }; | ||
| 48 | |||
| 49 | unsigned long event__total[PERF_RECORD_MAX]; | ||
| 50 | |||
| 51 | void event__print_totals(void) | ||
| 52 | { | ||
| 53 | int i; | ||
| 54 | for (i = 0; i < PERF_RECORD_MAX; ++i) | ||
| 55 | pr_info("%10s events: %10ld\n", | ||
| 56 | event__name[i], event__total[i]); | ||
| 57 | } | ||
| 58 | |||
| 59 | static int process_event(event_t *event, struct perf_session *session, | ||
| 60 | struct perf_event_ops *ops, | ||
| 61 | unsigned long offset, unsigned long head) | ||
| 62 | { | ||
| 63 | trace_event(event); | ||
| 64 | |||
| 65 | if (event->header.type < PERF_RECORD_MAX) { | ||
| 66 | dump_printf("%p [%p]: PERF_RECORD_%s", | ||
| 67 | (void *)(offset + head), | ||
| 68 | (void *)(long)(event->header.size), | ||
| 69 | event__name[event->header.type]); | ||
| 70 | ++event__total[0]; | ||
| 71 | ++event__total[event->header.type]; | ||
| 72 | } | ||
| 73 | |||
| 74 | switch (event->header.type) { | ||
| 75 | case PERF_RECORD_SAMPLE: | ||
| 76 | return ops->process_sample_event(event, session); | ||
| 77 | case PERF_RECORD_MMAP: | ||
| 78 | return ops->process_mmap_event(event, session); | ||
| 79 | case PERF_RECORD_COMM: | ||
| 80 | return ops->process_comm_event(event, session); | ||
| 81 | case PERF_RECORD_FORK: | ||
| 82 | return ops->process_fork_event(event, session); | ||
| 83 | case PERF_RECORD_EXIT: | ||
| 84 | return ops->process_exit_event(event, session); | ||
| 85 | case PERF_RECORD_LOST: | ||
| 86 | return ops->process_lost_event(event, session); | ||
| 87 | case PERF_RECORD_READ: | ||
| 88 | return ops->process_read_event(event, session); | ||
| 89 | case PERF_RECORD_THROTTLE: | ||
| 90 | return ops->process_throttle_event(event, session); | ||
| 91 | case PERF_RECORD_UNTHROTTLE: | ||
| 92 | return ops->process_unthrottle_event(event, session); | ||
| 93 | default: | ||
| 94 | ops->total_unknown++; | ||
| 95 | return -1; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | int perf_header__read_build_ids(int input, u64 offset, u64 size) | ||
| 100 | { | ||
| 101 | struct build_id_event bev; | ||
| 102 | char filename[PATH_MAX]; | ||
| 103 | u64 limit = offset + size; | ||
| 104 | int err = -1; | ||
| 105 | |||
| 106 | while (offset < limit) { | ||
| 107 | struct dso *dso; | ||
| 108 | ssize_t len; | ||
| 109 | |||
| 110 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
| 111 | goto out; | ||
| 112 | |||
| 113 | len = bev.header.size - sizeof(bev); | ||
| 114 | if (read(input, filename, len) != len) | ||
| 115 | goto out; | ||
| 116 | |||
| 117 | dso = dsos__findnew(filename); | ||
| 118 | if (dso != NULL) | ||
| 119 | dso__set_build_id(dso, &bev.build_id); | ||
| 120 | |||
| 121 | offset += bev.header.size; | ||
| 122 | } | ||
| 123 | err = 0; | ||
| 124 | out: | ||
| 125 | return err; | ||
| 126 | } | ||
| 127 | |||
| 128 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | ||
| 129 | { | ||
| 130 | struct thread *thread = perf_session__findnew(self, 0); | ||
| 131 | |||
| 132 | if (!thread || thread__set_comm(thread, "swapper")) { | ||
| 133 | pr_err("problem inserting idle task.\n"); | ||
| 134 | thread = NULL; | ||
| 135 | } | ||
| 136 | |||
| 137 | return thread; | ||
| 138 | } | ||
| 139 | |||
| 140 | int perf_session__process_events(struct perf_session *self, | ||
| 141 | struct perf_event_ops *ops) | ||
| 142 | { | ||
| 143 | int err; | ||
| 144 | unsigned long head, shift; | ||
| 145 | unsigned long offset = 0; | ||
| 146 | size_t page_size; | ||
| 147 | event_t *event; | ||
| 148 | uint32_t size; | ||
| 149 | char *buf; | ||
| 150 | |||
| 151 | if (perf_session__register_idle_thread(self) == NULL) | ||
| 152 | return -ENOMEM; | ||
| 153 | |||
| 154 | perf_event_ops__fill_defaults(ops); | ||
| 155 | |||
| 156 | page_size = getpagesize(); | ||
| 157 | |||
| 158 | head = self->header.data_offset; | ||
| 159 | self->sample_type = perf_header__sample_type(&self->header); | ||
| 160 | |||
| 161 | err = -EINVAL; | ||
| 162 | if (ops->sample_type_check && ops->sample_type_check(self) < 0) | ||
| 163 | goto out_err; | ||
| 164 | |||
| 165 | if (!ops->full_paths) { | ||
| 166 | char bf[PATH_MAX]; | ||
| 167 | |||
| 168 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
| 169 | err = -errno; | ||
| 170 | out_getcwd_err: | ||
| 171 | pr_err("failed to get the current directory\n"); | ||
| 172 | goto out_err; | ||
| 173 | } | ||
| 174 | self->cwd = strdup(bf); | ||
| 175 | if (self->cwd == NULL) { | ||
| 176 | err = -ENOMEM; | ||
| 177 | goto out_getcwd_err; | ||
| 178 | } | ||
| 179 | self->cwdlen = strlen(self->cwd); | ||
| 180 | } | ||
| 181 | |||
| 182 | shift = page_size * (head / page_size); | ||
| 183 | offset += shift; | ||
| 184 | head -= shift; | ||
| 185 | |||
| 186 | remap: | ||
| 187 | buf = mmap(NULL, page_size * self->mmap_window, PROT_READ, | ||
| 188 | MAP_SHARED, self->fd, offset); | ||
| 189 | if (buf == MAP_FAILED) { | ||
| 190 | pr_err("failed to mmap file\n"); | ||
| 191 | err = -errno; | ||
| 192 | goto out_err; | ||
| 193 | } | ||
| 194 | |||
| 195 | more: | ||
| 196 | event = (event_t *)(buf + head); | ||
| 197 | |||
| 198 | size = event->header.size; | ||
| 199 | if (!size) | ||
| 200 | size = 8; | ||
| 201 | |||
| 202 | if (head + event->header.size >= page_size * self->mmap_window) { | ||
| 203 | int munmap_ret; | ||
| 204 | |||
| 205 | shift = page_size * (head / page_size); | ||
| 206 | |||
| 207 | munmap_ret = munmap(buf, page_size * self->mmap_window); | ||
| 208 | assert(munmap_ret == 0); | ||
| 209 | |||
| 210 | offset += shift; | ||
| 211 | head -= shift; | ||
| 212 | goto remap; | ||
| 213 | } | ||
| 214 | |||
| 215 | size = event->header.size; | ||
| 216 | |||
| 217 | dump_printf("\n%p [%p]: event: %d\n", | ||
| 218 | (void *)(offset + head), | ||
| 219 | (void *)(long)event->header.size, | ||
| 220 | event->header.type); | ||
| 221 | |||
| 222 | if (!size || process_event(event, self, ops, offset, head) < 0) { | ||
| 223 | |||
| 224 | dump_printf("%p [%p]: skipping unknown header type: %d\n", | ||
| 225 | (void *)(offset + head), | ||
| 226 | (void *)(long)(event->header.size), | ||
| 227 | event->header.type); | ||
| 228 | |||
| 229 | /* | ||
| 230 | * assume we lost track of the stream, check alignment, and | ||
| 231 | * increment a single u64 in the hope to catch on again 'soon'. | ||
| 232 | */ | ||
| 233 | |||
| 234 | if (unlikely(head & 7)) | ||
| 235 | head &= ~7ULL; | ||
| 236 | |||
| 237 | size = 8; | ||
| 238 | } | ||
| 239 | |||
| 240 | head += size; | ||
| 241 | |||
| 242 | if (offset + head >= self->header.data_offset + self->header.data_size) | ||
| 243 | goto done; | ||
| 244 | |||
| 245 | if (offset + head < self->size) | ||
| 246 | goto more; | ||
| 247 | |||
| 248 | done: | ||
| 249 | err = 0; | ||
| 250 | out_err: | ||
| 251 | return err; | ||
| 252 | } | ||
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index e8ca98fe0bd4..28d520d5a1fb 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
| @@ -13,12 +13,12 @@ | |||
| 13 | int verbose = 0; | 13 | int verbose = 0; |
| 14 | int dump_trace = 0; | 14 | int dump_trace = 0; |
| 15 | 15 | ||
| 16 | int eprintf(const char *fmt, ...) | 16 | int eprintf(int level, const char *fmt, ...) |
| 17 | { | 17 | { |
| 18 | va_list args; | 18 | va_list args; |
| 19 | int ret = 0; | 19 | int ret = 0; |
| 20 | 20 | ||
| 21 | if (verbose) { | 21 | if (verbose >= level) { |
| 22 | va_start(args, fmt); | 22 | va_start(args, fmt); |
| 23 | ret = vfprintf(stderr, fmt, args); | 23 | ret = vfprintf(stderr, fmt, args); |
| 24 | va_end(args); | 24 | va_end(args); |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 437eea58ce40..c6c24c522dea 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
| @@ -1,8 +1,15 @@ | |||
| 1 | /* For debugging general purposes */ | 1 | /* For debugging general purposes */ |
| 2 | #ifndef __PERF_DEBUG_H | ||
| 3 | #define __PERF_DEBUG_H | ||
| 4 | |||
| 5 | #include "event.h" | ||
| 2 | 6 | ||
| 3 | extern int verbose; | 7 | extern int verbose; |
| 4 | extern int dump_trace; | 8 | extern int dump_trace; |
| 5 | 9 | ||
| 6 | int eprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 10 | int eprintf(int level, |
| 11 | const char *fmt, ...) __attribute__((format(printf, 2, 3))); | ||
| 7 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 12 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
| 8 | void trace_event(event_t *event); | 13 | void trace_event(event_t *event); |
| 14 | |||
| 15 | #endif /* __PERF_DEBUG_H */ | ||
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c new file mode 100644 index 000000000000..06b73ee02c49 --- /dev/null +++ b/tools/perf/util/debugfs.c | |||
| @@ -0,0 +1,241 @@ | |||
| 1 | #include "util.h" | ||
| 2 | #include "debugfs.h" | ||
| 3 | #include "cache.h" | ||
| 4 | |||
| 5 | static int debugfs_premounted; | ||
| 6 | static char debugfs_mountpoint[MAX_PATH+1]; | ||
| 7 | |||
| 8 | static const char *debugfs_known_mountpoints[] = { | ||
| 9 | "/sys/kernel/debug/", | ||
| 10 | "/debug/", | ||
| 11 | 0, | ||
| 12 | }; | ||
| 13 | |||
| 14 | /* use this to force a umount */ | ||
| 15 | void debugfs_force_cleanup(void) | ||
| 16 | { | ||
| 17 | debugfs_find_mountpoint(); | ||
| 18 | debugfs_premounted = 0; | ||
| 19 | debugfs_umount(); | ||
| 20 | } | ||
| 21 | |||
| 22 | /* construct a full path to a debugfs element */ | ||
| 23 | int debugfs_make_path(const char *element, char *buffer, int size) | ||
| 24 | { | ||
| 25 | int len; | ||
| 26 | |||
| 27 | if (strlen(debugfs_mountpoint) == 0) { | ||
| 28 | buffer[0] = '\0'; | ||
| 29 | return -1; | ||
| 30 | } | ||
| 31 | |||
| 32 | len = strlen(debugfs_mountpoint) + strlen(element) + 1; | ||
| 33 | if (len >= size) | ||
| 34 | return len+1; | ||
| 35 | |||
| 36 | snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element); | ||
| 37 | return 0; | ||
| 38 | } | ||
| 39 | |||
| 40 | static int debugfs_found; | ||
| 41 | |||
| 42 | /* find the path to the mounted debugfs */ | ||
| 43 | const char *debugfs_find_mountpoint(void) | ||
| 44 | { | ||
| 45 | const char **ptr; | ||
| 46 | char type[100]; | ||
| 47 | FILE *fp; | ||
| 48 | |||
| 49 | if (debugfs_found) | ||
| 50 | return (const char *) debugfs_mountpoint; | ||
| 51 | |||
| 52 | ptr = debugfs_known_mountpoints; | ||
| 53 | while (*ptr) { | ||
| 54 | if (debugfs_valid_mountpoint(*ptr) == 0) { | ||
| 55 | debugfs_found = 1; | ||
| 56 | strcpy(debugfs_mountpoint, *ptr); | ||
| 57 | return debugfs_mountpoint; | ||
| 58 | } | ||
| 59 | ptr++; | ||
| 60 | } | ||
| 61 | |||
| 62 | /* give up and parse /proc/mounts */ | ||
| 63 | fp = fopen("/proc/mounts", "r"); | ||
| 64 | if (fp == NULL) | ||
| 65 | die("Can't open /proc/mounts for read"); | ||
| 66 | |||
| 67 | while (fscanf(fp, "%*s %" | ||
| 68 | STR(MAX_PATH) | ||
| 69 | "s %99s %*s %*d %*d\n", | ||
| 70 | debugfs_mountpoint, type) == 2) { | ||
| 71 | if (strcmp(type, "debugfs") == 0) | ||
| 72 | break; | ||
| 73 | } | ||
| 74 | fclose(fp); | ||
| 75 | |||
| 76 | if (strcmp(type, "debugfs") != 0) | ||
| 77 | return NULL; | ||
| 78 | |||
| 79 | debugfs_found = 1; | ||
| 80 | |||
| 81 | return debugfs_mountpoint; | ||
| 82 | } | ||
| 83 | |||
| 84 | /* verify that a mountpoint is actually a debugfs instance */ | ||
| 85 | |||
| 86 | int debugfs_valid_mountpoint(const char *debugfs) | ||
| 87 | { | ||
| 88 | struct statfs st_fs; | ||
| 89 | |||
| 90 | if (statfs(debugfs, &st_fs) < 0) | ||
| 91 | return -ENOENT; | ||
| 92 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
| 93 | return -ENOENT; | ||
| 94 | |||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | |||
| 99 | int debugfs_valid_entry(const char *path) | ||
| 100 | { | ||
| 101 | struct stat st; | ||
| 102 | |||
| 103 | if (stat(path, &st)) | ||
| 104 | return -errno; | ||
| 105 | |||
| 106 | return 0; | ||
| 107 | } | ||
| 108 | |||
| 109 | /* mount the debugfs somewhere */ | ||
| 110 | |||
| 111 | int debugfs_mount(const char *mountpoint) | ||
| 112 | { | ||
| 113 | char mountcmd[128]; | ||
| 114 | |||
| 115 | /* see if it's already mounted */ | ||
| 116 | if (debugfs_find_mountpoint()) { | ||
| 117 | debugfs_premounted = 1; | ||
| 118 | return 0; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* if not mounted and no argument */ | ||
| 122 | if (mountpoint == NULL) { | ||
| 123 | /* see if environment variable set */ | ||
| 124 | mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT); | ||
| 125 | /* if no environment variable, use default */ | ||
| 126 | if (mountpoint == NULL) | ||
| 127 | mountpoint = "/sys/kernel/debug"; | ||
| 128 | } | ||
| 129 | |||
| 130 | /* save the mountpoint */ | ||
| 131 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); | ||
| 132 | |||
| 133 | /* mount it */ | ||
| 134 | snprintf(mountcmd, sizeof(mountcmd), | ||
| 135 | "/bin/mount -t debugfs debugfs %s", mountpoint); | ||
| 136 | return system(mountcmd); | ||
| 137 | } | ||
| 138 | |||
| 139 | /* umount the debugfs */ | ||
| 140 | |||
| 141 | int debugfs_umount(void) | ||
| 142 | { | ||
| 143 | char umountcmd[128]; | ||
| 144 | int ret; | ||
| 145 | |||
| 146 | /* if it was already mounted, leave it */ | ||
| 147 | if (debugfs_premounted) | ||
| 148 | return 0; | ||
| 149 | |||
| 150 | /* make sure it's a valid mount point */ | ||
| 151 | ret = debugfs_valid_mountpoint(debugfs_mountpoint); | ||
| 152 | if (ret) | ||
| 153 | return ret; | ||
| 154 | |||
| 155 | snprintf(umountcmd, sizeof(umountcmd), | ||
| 156 | "/bin/umount %s", debugfs_mountpoint); | ||
| 157 | return system(umountcmd); | ||
| 158 | } | ||
| 159 | |||
| 160 | int debugfs_write(const char *entry, const char *value) | ||
| 161 | { | ||
| 162 | char path[MAX_PATH+1]; | ||
| 163 | int ret, count; | ||
| 164 | int fd; | ||
| 165 | |||
| 166 | /* construct the path */ | ||
| 167 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
| 168 | |||
| 169 | /* verify that it exists */ | ||
| 170 | ret = debugfs_valid_entry(path); | ||
| 171 | if (ret) | ||
| 172 | return ret; | ||
| 173 | |||
| 174 | /* get how many chars we're going to write */ | ||
| 175 | count = strlen(value); | ||
| 176 | |||
| 177 | /* open the debugfs entry */ | ||
| 178 | fd = open(path, O_RDWR); | ||
| 179 | if (fd < 0) | ||
| 180 | return -errno; | ||
| 181 | |||
| 182 | while (count > 0) { | ||
| 183 | /* write it */ | ||
| 184 | ret = write(fd, value, count); | ||
| 185 | if (ret <= 0) { | ||
| 186 | if (ret == EAGAIN) | ||
| 187 | continue; | ||
| 188 | close(fd); | ||
| 189 | return -errno; | ||
| 190 | } | ||
| 191 | count -= ret; | ||
| 192 | } | ||
| 193 | |||
| 194 | /* close it */ | ||
| 195 | close(fd); | ||
| 196 | |||
| 197 | /* return success */ | ||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | /* | ||
| 202 | * read a debugfs entry | ||
| 203 | * returns the number of chars read or a negative errno | ||
| 204 | */ | ||
| 205 | int debugfs_read(const char *entry, char *buffer, size_t size) | ||
| 206 | { | ||
| 207 | char path[MAX_PATH+1]; | ||
| 208 | int ret; | ||
| 209 | int fd; | ||
| 210 | |||
| 211 | /* construct the path */ | ||
| 212 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
| 213 | |||
| 214 | /* verify that it exists */ | ||
| 215 | ret = debugfs_valid_entry(path); | ||
| 216 | if (ret) | ||
| 217 | return ret; | ||
| 218 | |||
| 219 | /* open the debugfs entry */ | ||
| 220 | fd = open(path, O_RDONLY); | ||
| 221 | if (fd < 0) | ||
| 222 | return -errno; | ||
| 223 | |||
| 224 | do { | ||
| 225 | /* read it */ | ||
| 226 | ret = read(fd, buffer, size); | ||
| 227 | if (ret == 0) { | ||
| 228 | close(fd); | ||
| 229 | return EOF; | ||
| 230 | } | ||
| 231 | } while (ret < 0 && errno == EAGAIN); | ||
| 232 | |||
| 233 | /* close it */ | ||
| 234 | close(fd); | ||
| 235 | |||
| 236 | /* make *sure* there's a null character at the end */ | ||
| 237 | buffer[ret] = '\0'; | ||
| 238 | |||
| 239 | /* return the number of chars read */ | ||
| 240 | return ret; | ||
| 241 | } | ||
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h new file mode 100644 index 000000000000..3cd14f9ae784 --- /dev/null +++ b/tools/perf/util/debugfs.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #ifndef __DEBUGFS_H__ | ||
| 2 | #define __DEBUGFS_H__ | ||
| 3 | |||
| 4 | #include <sys/mount.h> | ||
| 5 | |||
| 6 | #ifndef MAX_PATH | ||
| 7 | # define MAX_PATH 256 | ||
| 8 | #endif | ||
| 9 | |||
| 10 | #ifndef STR | ||
| 11 | # define _STR(x) #x | ||
| 12 | # define STR(x) _STR(x) | ||
| 13 | #endif | ||
| 14 | |||
| 15 | extern const char *debugfs_find_mountpoint(void); | ||
| 16 | extern int debugfs_valid_mountpoint(const char *debugfs); | ||
| 17 | extern int debugfs_valid_entry(const char *path); | ||
| 18 | extern int debugfs_mount(const char *mountpoint); | ||
| 19 | extern int debugfs_umount(void); | ||
| 20 | extern int debugfs_write(const char *entry, const char *value); | ||
| 21 | extern int debugfs_read(const char *entry, char *buffer, size_t size); | ||
| 22 | extern void debugfs_force_cleanup(void); | ||
| 23 | extern int debugfs_make_path(const char *element, char *buffer, int size); | ||
| 24 | |||
| 25 | #endif /* __DEBUGFS_H__ */ | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c new file mode 100644 index 000000000000..bb0fd6da2d56 --- /dev/null +++ b/tools/perf/util/event.c | |||
| @@ -0,0 +1,456 @@ | |||
| 1 | #include <linux/types.h> | ||
| 2 | #include "event.h" | ||
| 3 | #include "debug.h" | ||
| 4 | #include "session.h" | ||
| 5 | #include "sort.h" | ||
| 6 | #include "string.h" | ||
| 7 | #include "strlist.h" | ||
| 8 | #include "thread.h" | ||
| 9 | |||
| 10 | static pid_t event__synthesize_comm(pid_t pid, int full, | ||
| 11 | int (*process)(event_t *event, | ||
| 12 | struct perf_session *session), | ||
| 13 | struct perf_session *session) | ||
| 14 | { | ||
| 15 | event_t ev; | ||
| 16 | char filename[PATH_MAX]; | ||
| 17 | char bf[BUFSIZ]; | ||
| 18 | FILE *fp; | ||
| 19 | size_t size = 0; | ||
| 20 | DIR *tasks; | ||
| 21 | struct dirent dirent, *next; | ||
| 22 | pid_t tgid = 0; | ||
| 23 | |||
| 24 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); | ||
| 25 | |||
| 26 | fp = fopen(filename, "r"); | ||
| 27 | if (fp == NULL) { | ||
| 28 | out_race: | ||
| 29 | /* | ||
| 30 | * We raced with a task exiting - just return: | ||
| 31 | */ | ||
| 32 | pr_debug("couldn't open %s\n", filename); | ||
| 33 | return 0; | ||
| 34 | } | ||
| 35 | |||
| 36 | memset(&ev.comm, 0, sizeof(ev.comm)); | ||
| 37 | while (!ev.comm.comm[0] || !ev.comm.pid) { | ||
| 38 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
| 39 | goto out_failure; | ||
| 40 | |||
| 41 | if (memcmp(bf, "Name:", 5) == 0) { | ||
| 42 | char *name = bf + 5; | ||
| 43 | while (*name && isspace(*name)) | ||
| 44 | ++name; | ||
| 45 | size = strlen(name) - 1; | ||
| 46 | memcpy(ev.comm.comm, name, size++); | ||
| 47 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | ||
| 48 | char *tgids = bf + 5; | ||
| 49 | while (*tgids && isspace(*tgids)) | ||
| 50 | ++tgids; | ||
| 51 | tgid = ev.comm.pid = atoi(tgids); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | ev.comm.header.type = PERF_RECORD_COMM; | ||
| 56 | size = ALIGN(size, sizeof(u64)); | ||
| 57 | ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size); | ||
| 58 | |||
| 59 | if (!full) { | ||
| 60 | ev.comm.tid = pid; | ||
| 61 | |||
| 62 | process(&ev, session); | ||
| 63 | goto out_fclose; | ||
| 64 | } | ||
| 65 | |||
| 66 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | ||
| 67 | |||
| 68 | tasks = opendir(filename); | ||
| 69 | if (tasks == NULL) | ||
| 70 | goto out_race; | ||
| 71 | |||
| 72 | while (!readdir_r(tasks, &dirent, &next) && next) { | ||
| 73 | char *end; | ||
| 74 | pid = strtol(dirent.d_name, &end, 10); | ||
| 75 | if (*end) | ||
| 76 | continue; | ||
| 77 | |||
| 78 | ev.comm.tid = pid; | ||
| 79 | |||
| 80 | process(&ev, session); | ||
| 81 | } | ||
| 82 | closedir(tasks); | ||
| 83 | |||
| 84 | out_fclose: | ||
| 85 | fclose(fp); | ||
| 86 | return tgid; | ||
| 87 | |||
| 88 | out_failure: | ||
| 89 | pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); | ||
| 90 | return -1; | ||
| 91 | } | ||
| 92 | |||
| 93 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | ||
| 94 | int (*process)(event_t *event, | ||
| 95 | struct perf_session *session), | ||
| 96 | struct perf_session *session) | ||
| 97 | { | ||
| 98 | char filename[PATH_MAX]; | ||
| 99 | FILE *fp; | ||
| 100 | |||
| 101 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); | ||
| 102 | |||
| 103 | fp = fopen(filename, "r"); | ||
| 104 | if (fp == NULL) { | ||
| 105 | /* | ||
| 106 | * We raced with a task exiting - just return: | ||
| 107 | */ | ||
| 108 | pr_debug("couldn't open %s\n", filename); | ||
| 109 | return -1; | ||
| 110 | } | ||
| 111 | |||
| 112 | while (1) { | ||
| 113 | char bf[BUFSIZ], *pbf = bf; | ||
| 114 | event_t ev = { | ||
| 115 | .header = { .type = PERF_RECORD_MMAP }, | ||
| 116 | }; | ||
| 117 | int n; | ||
| 118 | size_t size; | ||
| 119 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
| 120 | break; | ||
| 121 | |||
| 122 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | ||
| 123 | n = hex2u64(pbf, &ev.mmap.start); | ||
| 124 | if (n < 0) | ||
| 125 | continue; | ||
| 126 | pbf += n + 1; | ||
| 127 | n = hex2u64(pbf, &ev.mmap.len); | ||
| 128 | if (n < 0) | ||
| 129 | continue; | ||
| 130 | pbf += n + 3; | ||
| 131 | if (*pbf == 'x') { /* vm_exec */ | ||
| 132 | char *execname = strchr(bf, '/'); | ||
| 133 | |||
| 134 | /* Catch VDSO */ | ||
| 135 | if (execname == NULL) | ||
| 136 | execname = strstr(bf, "[vdso]"); | ||
| 137 | |||
| 138 | if (execname == NULL) | ||
| 139 | continue; | ||
| 140 | |||
| 141 | size = strlen(execname); | ||
| 142 | execname[size - 1] = '\0'; /* Remove \n */ | ||
| 143 | memcpy(ev.mmap.filename, execname, size); | ||
| 144 | size = ALIGN(size, sizeof(u64)); | ||
| 145 | ev.mmap.len -= ev.mmap.start; | ||
| 146 | ev.mmap.header.size = (sizeof(ev.mmap) - | ||
| 147 | (sizeof(ev.mmap.filename) - size)); | ||
| 148 | ev.mmap.pid = tgid; | ||
| 149 | ev.mmap.tid = pid; | ||
| 150 | |||
| 151 | process(&ev, session); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | fclose(fp); | ||
| 156 | return 0; | ||
| 157 | } | ||
| 158 | |||
| 159 | int event__synthesize_thread(pid_t pid, | ||
| 160 | int (*process)(event_t *event, | ||
| 161 | struct perf_session *session), | ||
| 162 | struct perf_session *session) | ||
| 163 | { | ||
| 164 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); | ||
| 165 | if (tgid == -1) | ||
| 166 | return -1; | ||
| 167 | return event__synthesize_mmap_events(pid, tgid, process, session); | ||
| 168 | } | ||
| 169 | |||
| 170 | void event__synthesize_threads(int (*process)(event_t *event, | ||
| 171 | struct perf_session *session), | ||
| 172 | struct perf_session *session) | ||
| 173 | { | ||
| 174 | DIR *proc; | ||
| 175 | struct dirent dirent, *next; | ||
| 176 | |||
| 177 | proc = opendir("/proc"); | ||
| 178 | |||
| 179 | while (!readdir_r(proc, &dirent, &next) && next) { | ||
| 180 | char *end; | ||
| 181 | pid_t pid = strtol(dirent.d_name, &end, 10); | ||
| 182 | |||
| 183 | if (*end) /* only interested in proper numerical dirents */ | ||
| 184 | continue; | ||
| 185 | |||
| 186 | event__synthesize_thread(pid, process, session); | ||
| 187 | } | ||
| 188 | |||
| 189 | closedir(proc); | ||
| 190 | } | ||
| 191 | |||
| 192 | static void thread__comm_adjust(struct thread *self) | ||
| 193 | { | ||
| 194 | char *comm = self->comm; | ||
| 195 | |||
| 196 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
| 197 | (!symbol_conf.comm_list || | ||
| 198 | strlist__has_entry(symbol_conf.comm_list, comm))) { | ||
| 199 | unsigned int slen = strlen(comm); | ||
| 200 | |||
| 201 | if (slen > comms__col_width) { | ||
| 202 | comms__col_width = slen; | ||
| 203 | threads__col_width = slen + 6; | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | static int thread__set_comm_adjust(struct thread *self, const char *comm) | ||
| 209 | { | ||
| 210 | int ret = thread__set_comm(self, comm); | ||
| 211 | |||
| 212 | if (ret) | ||
| 213 | return ret; | ||
| 214 | |||
| 215 | thread__comm_adjust(self); | ||
| 216 | |||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | int event__process_comm(event_t *self, struct perf_session *session) | ||
| 221 | { | ||
| 222 | struct thread *thread = perf_session__findnew(session, self->comm.pid); | ||
| 223 | |||
| 224 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); | ||
| 225 | |||
| 226 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { | ||
| 227 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
| 228 | return -1; | ||
| 229 | } | ||
| 230 | |||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | int event__process_lost(event_t *self, struct perf_session *session) | ||
| 235 | { | ||
| 236 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); | ||
| 237 | session->events_stats.lost += self->lost.lost; | ||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | int event__process_mmap(event_t *self, struct perf_session *session) | ||
| 242 | { | ||
| 243 | struct thread *thread = perf_session__findnew(session, self->mmap.pid); | ||
| 244 | struct map *map = map__new(&self->mmap, MAP__FUNCTION, | ||
| 245 | session->cwd, session->cwdlen); | ||
| 246 | |||
| 247 | dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", | ||
| 248 | self->mmap.pid, self->mmap.tid, | ||
| 249 | (void *)(long)self->mmap.start, | ||
| 250 | (void *)(long)self->mmap.len, | ||
| 251 | (void *)(long)self->mmap.pgoff, | ||
| 252 | self->mmap.filename); | ||
| 253 | |||
| 254 | if (thread == NULL || map == NULL) | ||
| 255 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
| 256 | else | ||
| 257 | thread__insert_map(thread, map); | ||
| 258 | |||
| 259 | return 0; | ||
| 260 | } | ||
| 261 | |||
| 262 | int event__process_task(event_t *self, struct perf_session *session) | ||
| 263 | { | ||
| 264 | struct thread *thread = perf_session__findnew(session, self->fork.pid); | ||
| 265 | struct thread *parent = perf_session__findnew(session, self->fork.ppid); | ||
| 266 | |||
| 267 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | ||
| 268 | self->fork.ppid, self->fork.ptid); | ||
| 269 | /* | ||
| 270 | * A thread clone will have the same PID for both parent and child. | ||
| 271 | */ | ||
| 272 | if (thread == parent) | ||
| 273 | return 0; | ||
| 274 | |||
| 275 | if (self->header.type == PERF_RECORD_EXIT) | ||
| 276 | return 0; | ||
| 277 | |||
| 278 | if (thread == NULL || parent == NULL || | ||
| 279 | thread__fork(thread, parent) < 0) { | ||
| 280 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
| 281 | return -1; | ||
| 282 | } | ||
| 283 | |||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | void thread__find_addr_location(struct thread *self, | ||
| 288 | struct perf_session *session, u8 cpumode, | ||
| 289 | enum map_type type, u64 addr, | ||
| 290 | struct addr_location *al, | ||
| 291 | symbol_filter_t filter) | ||
| 292 | { | ||
| 293 | struct map_groups *mg = &self->mg; | ||
| 294 | |||
| 295 | al->thread = self; | ||
| 296 | al->addr = addr; | ||
| 297 | |||
| 298 | if (cpumode & PERF_RECORD_MISC_KERNEL) { | ||
| 299 | al->level = 'k'; | ||
| 300 | mg = &session->kmaps; | ||
| 301 | } else if (cpumode & PERF_RECORD_MISC_USER) | ||
| 302 | al->level = '.'; | ||
| 303 | else { | ||
| 304 | al->level = 'H'; | ||
| 305 | al->map = NULL; | ||
| 306 | al->sym = NULL; | ||
| 307 | return; | ||
| 308 | } | ||
| 309 | try_again: | ||
| 310 | al->map = map_groups__find(mg, type, al->addr); | ||
| 311 | if (al->map == NULL) { | ||
| 312 | /* | ||
| 313 | * If this is outside of all known maps, and is a negative | ||
| 314 | * address, try to look it up in the kernel dso, as it might be | ||
| 315 | * a vsyscall or vdso (which executes in user-mode). | ||
| 316 | * | ||
| 317 | * XXX This is nasty, we should have a symbol list in the | ||
| 318 | * "[vdso]" dso, but for now lets use the old trick of looking | ||
| 319 | * in the whole kernel symbol list. | ||
| 320 | */ | ||
| 321 | if ((long long)al->addr < 0 && mg != &session->kmaps) { | ||
| 322 | mg = &session->kmaps; | ||
| 323 | goto try_again; | ||
| 324 | } | ||
| 325 | al->sym = NULL; | ||
| 326 | } else { | ||
| 327 | al->addr = al->map->map_ip(al->map, al->addr); | ||
| 328 | al->sym = map__find_symbol(al->map, session, al->addr, filter); | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | static void dso__calc_col_width(struct dso *self) | ||
| 333 | { | ||
| 334 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
| 335 | (!symbol_conf.dso_list || | ||
| 336 | strlist__has_entry(symbol_conf.dso_list, self->name))) { | ||
| 337 | unsigned int slen = strlen(self->name); | ||
| 338 | if (slen > dsos__col_width) | ||
| 339 | dsos__col_width = slen; | ||
| 340 | } | ||
| 341 | |||
| 342 | self->slen_calculated = 1; | ||
| 343 | } | ||
| 344 | |||
| 345 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | ||
| 346 | struct addr_location *al, symbol_filter_t filter) | ||
| 347 | { | ||
| 348 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
| 349 | struct thread *thread = perf_session__findnew(session, self->ip.pid); | ||
| 350 | |||
| 351 | if (thread == NULL) | ||
| 352 | return -1; | ||
| 353 | |||
| 354 | if (symbol_conf.comm_list && | ||
| 355 | !strlist__has_entry(symbol_conf.comm_list, thread->comm)) | ||
| 356 | goto out_filtered; | ||
| 357 | |||
| 358 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | ||
| 359 | |||
| 360 | thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION, | ||
| 361 | self->ip.ip, al, filter); | ||
| 362 | dump_printf(" ...... dso: %s\n", | ||
| 363 | al->map ? al->map->dso->long_name : | ||
| 364 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | ||
| 365 | /* | ||
| 366 | * We have to do this here as we may have a dso with no symbol hit that | ||
| 367 | * has a name longer than the ones with symbols sampled. | ||
| 368 | */ | ||
| 369 | if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated) | ||
| 370 | dso__calc_col_width(al->map->dso); | ||
| 371 | |||
| 372 | if (symbol_conf.dso_list && | ||
| 373 | (!al->map || !al->map->dso || | ||
| 374 | !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) || | ||
| 375 | (al->map->dso->short_name != al->map->dso->long_name && | ||
| 376 | strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name))))) | ||
| 377 | goto out_filtered; | ||
| 378 | |||
| 379 | if (symbol_conf.sym_list && al->sym && | ||
| 380 | !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) | ||
| 381 | goto out_filtered; | ||
| 382 | |||
| 383 | al->filtered = false; | ||
| 384 | return 0; | ||
| 385 | |||
| 386 | out_filtered: | ||
| 387 | al->filtered = true; | ||
| 388 | return 0; | ||
| 389 | } | ||
| 390 | |||
| 391 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data) | ||
| 392 | { | ||
| 393 | u64 *array = event->sample.array; | ||
| 394 | |||
| 395 | if (type & PERF_SAMPLE_IP) { | ||
| 396 | data->ip = event->ip.ip; | ||
| 397 | array++; | ||
| 398 | } | ||
| 399 | |||
| 400 | if (type & PERF_SAMPLE_TID) { | ||
| 401 | u32 *p = (u32 *)array; | ||
| 402 | data->pid = p[0]; | ||
| 403 | data->tid = p[1]; | ||
| 404 | array++; | ||
| 405 | } | ||
| 406 | |||
| 407 | if (type & PERF_SAMPLE_TIME) { | ||
| 408 | data->time = *array; | ||
| 409 | array++; | ||
| 410 | } | ||
| 411 | |||
| 412 | if (type & PERF_SAMPLE_ADDR) { | ||
| 413 | data->addr = *array; | ||
| 414 | array++; | ||
| 415 | } | ||
| 416 | |||
| 417 | if (type & PERF_SAMPLE_ID) { | ||
| 418 | data->id = *array; | ||
| 419 | array++; | ||
| 420 | } | ||
| 421 | |||
| 422 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
| 423 | data->stream_id = *array; | ||
| 424 | array++; | ||
| 425 | } | ||
| 426 | |||
| 427 | if (type & PERF_SAMPLE_CPU) { | ||
| 428 | u32 *p = (u32 *)array; | ||
| 429 | data->cpu = *p; | ||
| 430 | array++; | ||
| 431 | } | ||
| 432 | |||
| 433 | if (type & PERF_SAMPLE_PERIOD) { | ||
| 434 | data->period = *array; | ||
| 435 | array++; | ||
| 436 | } | ||
| 437 | |||
| 438 | if (type & PERF_SAMPLE_READ) { | ||
| 439 | pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); | ||
| 440 | return -1; | ||
| 441 | } | ||
| 442 | |||
| 443 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
| 444 | data->callchain = (struct ip_callchain *)array; | ||
| 445 | array += 1 + data->callchain->nr; | ||
| 446 | } | ||
| 447 | |||
| 448 | if (type & PERF_SAMPLE_RAW) { | ||
| 449 | u32 *p = (u32 *)array; | ||
| 450 | data->raw_size = *p; | ||
| 451 | p++; | ||
| 452 | data->raw_data = p; | ||
| 453 | } | ||
| 454 | |||
| 455 | return 0; | ||
| 456 | } | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 2c9c26d6ded0..690a96d0467c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
| @@ -1,14 +1,10 @@ | |||
| 1 | #ifndef __PERF_RECORD_H | 1 | #ifndef __PERF_RECORD_H |
| 2 | #define __PERF_RECORD_H | 2 | #define __PERF_RECORD_H |
| 3 | |||
| 3 | #include "../perf.h" | 4 | #include "../perf.h" |
| 4 | #include "util.h" | 5 | #include "util.h" |
| 5 | #include <linux/list.h> | 6 | #include <linux/list.h> |
| 6 | 7 | #include <linux/rbtree.h> | |
| 7 | enum { | ||
| 8 | SHOW_KERNEL = 1, | ||
| 9 | SHOW_USER = 2, | ||
| 10 | SHOW_HV = 4, | ||
| 11 | }; | ||
| 12 | 8 | ||
| 13 | /* | 9 | /* |
| 14 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * | 10 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * |
| @@ -60,11 +56,32 @@ struct read_event { | |||
| 60 | u64 id; | 56 | u64 id; |
| 61 | }; | 57 | }; |
| 62 | 58 | ||
| 63 | struct sample_event{ | 59 | struct sample_event { |
| 64 | struct perf_event_header header; | 60 | struct perf_event_header header; |
| 65 | u64 array[]; | 61 | u64 array[]; |
| 66 | }; | 62 | }; |
| 67 | 63 | ||
| 64 | struct sample_data { | ||
| 65 | u64 ip; | ||
| 66 | u32 pid, tid; | ||
| 67 | u64 time; | ||
| 68 | u64 addr; | ||
| 69 | u64 id; | ||
| 70 | u64 stream_id; | ||
| 71 | u32 cpu; | ||
| 72 | u64 period; | ||
| 73 | struct ip_callchain *callchain; | ||
| 74 | u32 raw_size; | ||
| 75 | void *raw_data; | ||
| 76 | }; | ||
| 77 | |||
| 78 | #define BUILD_ID_SIZE 20 | ||
| 79 | |||
| 80 | struct build_id_event { | ||
| 81 | struct perf_event_header header; | ||
| 82 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | ||
| 83 | char filename[]; | ||
| 84 | }; | ||
| 68 | 85 | ||
| 69 | typedef union event_union { | 86 | typedef union event_union { |
| 70 | struct perf_event_header header; | 87 | struct perf_event_header header; |
| @@ -77,12 +94,31 @@ typedef union event_union { | |||
| 77 | struct sample_event sample; | 94 | struct sample_event sample; |
| 78 | } event_t; | 95 | } event_t; |
| 79 | 96 | ||
| 97 | struct events_stats { | ||
| 98 | u64 total; | ||
| 99 | u64 lost; | ||
| 100 | }; | ||
| 101 | |||
| 102 | void event__print_totals(void); | ||
| 103 | |||
| 104 | enum map_type { | ||
| 105 | MAP__FUNCTION = 0, | ||
| 106 | MAP__VARIABLE, | ||
| 107 | }; | ||
| 108 | |||
| 109 | #define MAP__NR_TYPES (MAP__VARIABLE + 1) | ||
| 110 | |||
| 80 | struct map { | 111 | struct map { |
| 81 | struct list_head node; | 112 | union { |
| 113 | struct rb_node rb_node; | ||
| 114 | struct list_head node; | ||
| 115 | }; | ||
| 82 | u64 start; | 116 | u64 start; |
| 83 | u64 end; | 117 | u64 end; |
| 118 | enum map_type type; | ||
| 84 | u64 pgoff; | 119 | u64 pgoff; |
| 85 | u64 (*map_ip)(struct map *, u64); | 120 | u64 (*map_ip)(struct map *, u64); |
| 121 | u64 (*unmap_ip)(struct map *, u64); | ||
| 86 | struct dso *dso; | 122 | struct dso *dso; |
| 87 | }; | 123 | }; |
| 88 | 124 | ||
| @@ -91,14 +127,57 @@ static inline u64 map__map_ip(struct map *map, u64 ip) | |||
| 91 | return ip - map->start + map->pgoff; | 127 | return ip - map->start + map->pgoff; |
| 92 | } | 128 | } |
| 93 | 129 | ||
| 94 | static inline u64 vdso__map_ip(struct map *map __used, u64 ip) | 130 | static inline u64 map__unmap_ip(struct map *map, u64 ip) |
| 131 | { | ||
| 132 | return ip + map->start - map->pgoff; | ||
| 133 | } | ||
| 134 | |||
| 135 | static inline u64 identity__map_ip(struct map *map __used, u64 ip) | ||
| 95 | { | 136 | { |
| 96 | return ip; | 137 | return ip; |
| 97 | } | 138 | } |
| 98 | 139 | ||
| 99 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen); | 140 | struct symbol; |
| 141 | |||
| 142 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | ||
| 143 | |||
| 144 | void map__init(struct map *self, enum map_type type, | ||
| 145 | u64 start, u64 end, u64 pgoff, struct dso *dso); | ||
| 146 | struct map *map__new(struct mmap_event *event, enum map_type, | ||
| 147 | char *cwd, int cwdlen); | ||
| 148 | void map__delete(struct map *self); | ||
| 100 | struct map *map__clone(struct map *self); | 149 | struct map *map__clone(struct map *self); |
| 101 | int map__overlap(struct map *l, struct map *r); | 150 | int map__overlap(struct map *l, struct map *r); |
| 102 | size_t map__fprintf(struct map *self, FILE *fp); | 151 | size_t map__fprintf(struct map *self, FILE *fp); |
| 103 | 152 | ||
| 104 | #endif | 153 | struct perf_session; |
| 154 | |||
| 155 | int map__load(struct map *self, struct perf_session *session, | ||
| 156 | symbol_filter_t filter); | ||
| 157 | struct symbol *map__find_symbol(struct map *self, struct perf_session *session, | ||
| 158 | u64 addr, symbol_filter_t filter); | ||
| 159 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
| 160 | struct perf_session *session, | ||
| 161 | symbol_filter_t filter); | ||
| 162 | void map__fixup_start(struct map *self); | ||
| 163 | void map__fixup_end(struct map *self); | ||
| 164 | |||
| 165 | int event__synthesize_thread(pid_t pid, | ||
| 166 | int (*process)(event_t *event, | ||
| 167 | struct perf_session *session), | ||
| 168 | struct perf_session *session); | ||
| 169 | void event__synthesize_threads(int (*process)(event_t *event, | ||
| 170 | struct perf_session *session), | ||
| 171 | struct perf_session *session); | ||
| 172 | |||
| 173 | int event__process_comm(event_t *self, struct perf_session *session); | ||
| 174 | int event__process_lost(event_t *self, struct perf_session *session); | ||
| 175 | int event__process_mmap(event_t *self, struct perf_session *session); | ||
| 176 | int event__process_task(event_t *self, struct perf_session *session); | ||
| 177 | |||
| 178 | struct addr_location; | ||
| 179 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | ||
| 180 | struct addr_location *al, symbol_filter_t filter); | ||
| 181 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data); | ||
| 182 | |||
| 183 | #endif /* __PERF_RECORD_H */ | ||
diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h index effe25eb1545..31647ac92ed1 100644 --- a/tools/perf/util/exec_cmd.h +++ b/tools/perf/util/exec_cmd.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef PERF_EXEC_CMD_H | 1 | #ifndef __PERF_EXEC_CMD_H |
| 2 | #define PERF_EXEC_CMD_H | 2 | #define __PERF_EXEC_CMD_H |
| 3 | 3 | ||
| 4 | extern void perf_set_argv_exec_path(const char *exec_path); | 4 | extern void perf_set_argv_exec_path(const char *exec_path); |
| 5 | extern const char *perf_extract_argv0_path(const char *path); | 5 | extern const char *perf_extract_argv0_path(const char *path); |
| @@ -10,4 +10,4 @@ extern int execv_perf_cmd(const char **argv); /* NULL terminated */ | |||
| 10 | extern int execl_perf_cmd(const char *cmd, ...); | 10 | extern int execl_perf_cmd(const char *cmd, ...); |
| 11 | extern const char *system_path(const char *path); | 11 | extern const char *system_path(const char *path); |
| 12 | 12 | ||
| 13 | #endif /* PERF_EXEC_CMD_H */ | 13 | #endif /* __PERF_EXEC_CMD_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index e306857b2c2b..8a0bca55106f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -2,9 +2,15 @@ | |||
| 2 | #include <unistd.h> | 2 | #include <unistd.h> |
| 3 | #include <stdio.h> | 3 | #include <stdio.h> |
| 4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
| 5 | #include <linux/list.h> | ||
| 5 | 6 | ||
| 6 | #include "util.h" | 7 | #include "util.h" |
| 7 | #include "header.h" | 8 | #include "header.h" |
| 9 | #include "../perf.h" | ||
| 10 | #include "trace-event.h" | ||
| 11 | #include "session.h" | ||
| 12 | #include "symbol.h" | ||
| 13 | #include "debug.h" | ||
| 8 | 14 | ||
| 9 | /* | 15 | /* |
| 10 | * Create new perf.data header attribute: | 16 | * Create new perf.data header attribute: |
| @@ -13,75 +19,80 @@ struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr) | |||
| 13 | { | 19 | { |
| 14 | struct perf_header_attr *self = malloc(sizeof(*self)); | 20 | struct perf_header_attr *self = malloc(sizeof(*self)); |
| 15 | 21 | ||
| 16 | if (!self) | 22 | if (self != NULL) { |
| 17 | die("nomem"); | 23 | self->attr = *attr; |
| 18 | 24 | self->ids = 0; | |
| 19 | self->attr = *attr; | 25 | self->size = 1; |
| 20 | self->ids = 0; | 26 | self->id = malloc(sizeof(u64)); |
| 21 | self->size = 1; | 27 | if (self->id == NULL) { |
| 22 | self->id = malloc(sizeof(u64)); | 28 | free(self); |
| 23 | 29 | self = NULL; | |
| 24 | if (!self->id) | 30 | } |
| 25 | die("nomem"); | 31 | } |
| 26 | 32 | ||
| 27 | return self; | 33 | return self; |
| 28 | } | 34 | } |
| 29 | 35 | ||
| 30 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | 36 | void perf_header_attr__delete(struct perf_header_attr *self) |
| 37 | { | ||
| 38 | free(self->id); | ||
| 39 | free(self); | ||
| 40 | } | ||
| 41 | |||
| 42 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | ||
| 31 | { | 43 | { |
| 32 | int pos = self->ids; | 44 | int pos = self->ids; |
| 33 | 45 | ||
| 34 | self->ids++; | 46 | self->ids++; |
| 35 | if (self->ids > self->size) { | 47 | if (self->ids > self->size) { |
| 36 | self->size *= 2; | 48 | int nsize = self->size * 2; |
| 37 | self->id = realloc(self->id, self->size * sizeof(u64)); | 49 | u64 *nid = realloc(self->id, nsize * sizeof(u64)); |
| 38 | if (!self->id) | 50 | |
| 39 | die("nomem"); | 51 | if (nid == NULL) |
| 52 | return -1; | ||
| 53 | |||
| 54 | self->size = nsize; | ||
| 55 | self->id = nid; | ||
| 40 | } | 56 | } |
| 41 | self->id[pos] = id; | 57 | self->id[pos] = id; |
| 58 | return 0; | ||
| 42 | } | 59 | } |
| 43 | 60 | ||
| 44 | /* | 61 | int perf_header__init(struct perf_header *self) |
| 45 | * Create new perf.data header: | ||
| 46 | */ | ||
| 47 | struct perf_header *perf_header__new(void) | ||
| 48 | { | 62 | { |
| 49 | struct perf_header *self = malloc(sizeof(*self)); | ||
| 50 | |||
| 51 | if (!self) | ||
| 52 | die("nomem"); | ||
| 53 | |||
| 54 | self->frozen = 0; | ||
| 55 | |||
| 56 | self->attrs = 0; | ||
| 57 | self->size = 1; | 63 | self->size = 1; |
| 58 | self->attr = malloc(sizeof(void *)); | 64 | self->attr = malloc(sizeof(void *)); |
| 59 | 65 | return self->attr == NULL ? -ENOMEM : 0; | |
| 60 | if (!self->attr) | ||
| 61 | die("nomem"); | ||
| 62 | |||
| 63 | self->data_offset = 0; | ||
| 64 | self->data_size = 0; | ||
| 65 | |||
| 66 | return self; | ||
| 67 | } | 66 | } |
| 68 | 67 | ||
| 69 | void perf_header__add_attr(struct perf_header *self, | 68 | void perf_header__exit(struct perf_header *self) |
| 70 | struct perf_header_attr *attr) | ||
| 71 | { | 69 | { |
| 72 | int pos = self->attrs; | 70 | int i; |
| 71 | for (i = 0; i < self->attrs; ++i) | ||
| 72 | perf_header_attr__delete(self->attr[i]); | ||
| 73 | free(self->attr); | ||
| 74 | } | ||
| 73 | 75 | ||
| 76 | int perf_header__add_attr(struct perf_header *self, | ||
| 77 | struct perf_header_attr *attr) | ||
| 78 | { | ||
| 74 | if (self->frozen) | 79 | if (self->frozen) |
| 75 | die("frozen"); | 80 | return -1; |
| 76 | 81 | ||
| 77 | self->attrs++; | 82 | if (self->attrs == self->size) { |
| 78 | if (self->attrs > self->size) { | 83 | int nsize = self->size * 2; |
| 79 | self->size *= 2; | 84 | struct perf_header_attr **nattr; |
| 80 | self->attr = realloc(self->attr, self->size * sizeof(void *)); | 85 | |
| 81 | if (!self->attr) | 86 | nattr = realloc(self->attr, nsize * sizeof(void *)); |
| 82 | die("nomem"); | 87 | if (nattr == NULL) |
| 88 | return -1; | ||
| 89 | |||
| 90 | self->size = nsize; | ||
| 91 | self->attr = nattr; | ||
| 83 | } | 92 | } |
| 84 | self->attr[pos] = attr; | 93 | |
| 94 | self->attr[self->attrs++] = attr; | ||
| 95 | return 0; | ||
| 85 | } | 96 | } |
| 86 | 97 | ||
| 87 | #define MAX_EVENT_NAME 64 | 98 | #define MAX_EVENT_NAME 64 |
| @@ -97,7 +108,7 @@ static struct perf_trace_event_type *events; | |||
| 97 | void perf_header__push_event(u64 id, const char *name) | 108 | void perf_header__push_event(u64 id, const char *name) |
| 98 | { | 109 | { |
| 99 | if (strlen(name) > MAX_EVENT_NAME) | 110 | if (strlen(name) > MAX_EVENT_NAME) |
| 100 | printf("Event %s will be truncated\n", name); | 111 | pr_warning("Event %s will be truncated\n", name); |
| 101 | 112 | ||
| 102 | if (!events) { | 113 | if (!events) { |
| 103 | events = malloc(sizeof(struct perf_trace_event_type)); | 114 | events = malloc(sizeof(struct perf_trace_event_type)); |
| @@ -128,44 +139,142 @@ static const char *__perf_magic = "PERFFILE"; | |||
| 128 | 139 | ||
| 129 | #define PERF_MAGIC (*(u64 *)__perf_magic) | 140 | #define PERF_MAGIC (*(u64 *)__perf_magic) |
| 130 | 141 | ||
| 131 | struct perf_file_section { | ||
| 132 | u64 offset; | ||
| 133 | u64 size; | ||
| 134 | }; | ||
| 135 | |||
| 136 | struct perf_file_attr { | 142 | struct perf_file_attr { |
| 137 | struct perf_event_attr attr; | 143 | struct perf_event_attr attr; |
| 138 | struct perf_file_section ids; | 144 | struct perf_file_section ids; |
| 139 | }; | 145 | }; |
| 140 | 146 | ||
| 141 | struct perf_file_header { | 147 | void perf_header__set_feat(struct perf_header *self, int feat) |
| 142 | u64 magic; | 148 | { |
| 143 | u64 size; | 149 | set_bit(feat, self->adds_features); |
| 144 | u64 attr_size; | 150 | } |
| 145 | struct perf_file_section attrs; | 151 | |
| 146 | struct perf_file_section data; | 152 | bool perf_header__has_feat(const struct perf_header *self, int feat) |
| 147 | struct perf_file_section event_types; | 153 | { |
| 148 | }; | 154 | return test_bit(feat, self->adds_features); |
| 155 | } | ||
| 149 | 156 | ||
| 150 | static void do_write(int fd, void *buf, size_t size) | 157 | static int do_write(int fd, const void *buf, size_t size) |
| 151 | { | 158 | { |
| 152 | while (size) { | 159 | while (size) { |
| 153 | int ret = write(fd, buf, size); | 160 | int ret = write(fd, buf, size); |
| 154 | 161 | ||
| 155 | if (ret < 0) | 162 | if (ret < 0) |
| 156 | die("failed to write"); | 163 | return -errno; |
| 157 | 164 | ||
| 158 | size -= ret; | 165 | size -= ret; |
| 159 | buf += ret; | 166 | buf += ret; |
| 160 | } | 167 | } |
| 168 | |||
| 169 | return 0; | ||
| 170 | } | ||
| 171 | |||
| 172 | static int __dsos__write_buildid_table(struct list_head *head, int fd) | ||
| 173 | { | ||
| 174 | #define NAME_ALIGN 64 | ||
| 175 | struct dso *pos; | ||
| 176 | static const char zero_buf[NAME_ALIGN]; | ||
| 177 | |||
| 178 | list_for_each_entry(pos, head, node) { | ||
| 179 | int err; | ||
| 180 | struct build_id_event b; | ||
| 181 | size_t len; | ||
| 182 | |||
| 183 | if (!pos->has_build_id) | ||
| 184 | continue; | ||
| 185 | len = pos->long_name_len + 1; | ||
| 186 | len = ALIGN(len, NAME_ALIGN); | ||
| 187 | memset(&b, 0, sizeof(b)); | ||
| 188 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | ||
| 189 | b.header.size = sizeof(b) + len; | ||
| 190 | err = do_write(fd, &b, sizeof(b)); | ||
| 191 | if (err < 0) | ||
| 192 | return err; | ||
| 193 | err = do_write(fd, pos->long_name, pos->long_name_len + 1); | ||
| 194 | if (err < 0) | ||
| 195 | return err; | ||
| 196 | err = do_write(fd, zero_buf, len - pos->long_name_len - 1); | ||
| 197 | if (err < 0) | ||
| 198 | return err; | ||
| 199 | } | ||
| 200 | |||
| 201 | return 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | static int dsos__write_buildid_table(int fd) | ||
| 205 | { | ||
| 206 | int err = __dsos__write_buildid_table(&dsos__kernel, fd); | ||
| 207 | if (err == 0) | ||
| 208 | err = __dsos__write_buildid_table(&dsos__user, fd); | ||
| 209 | return err; | ||
| 210 | } | ||
| 211 | |||
| 212 | static int perf_header__adds_write(struct perf_header *self, int fd) | ||
| 213 | { | ||
| 214 | int nr_sections; | ||
| 215 | struct perf_file_section *feat_sec; | ||
| 216 | int sec_size; | ||
| 217 | u64 sec_start; | ||
| 218 | int idx = 0, err; | ||
| 219 | |||
| 220 | if (dsos__read_build_ids()) | ||
| 221 | perf_header__set_feat(self, HEADER_BUILD_ID); | ||
| 222 | |||
| 223 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | ||
| 224 | if (!nr_sections) | ||
| 225 | return 0; | ||
| 226 | |||
| 227 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | ||
| 228 | if (feat_sec == NULL) | ||
| 229 | return -ENOMEM; | ||
| 230 | |||
| 231 | sec_size = sizeof(*feat_sec) * nr_sections; | ||
| 232 | |||
| 233 | sec_start = self->data_offset + self->data_size; | ||
| 234 | lseek(fd, sec_start + sec_size, SEEK_SET); | ||
| 235 | |||
| 236 | if (perf_header__has_feat(self, HEADER_TRACE_INFO)) { | ||
| 237 | struct perf_file_section *trace_sec; | ||
| 238 | |||
| 239 | trace_sec = &feat_sec[idx++]; | ||
| 240 | |||
| 241 | /* Write trace info */ | ||
| 242 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | ||
| 243 | read_tracing_data(fd, attrs, nr_counters); | ||
| 244 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | ||
| 245 | } | ||
| 246 | |||
| 247 | |||
| 248 | if (perf_header__has_feat(self, HEADER_BUILD_ID)) { | ||
| 249 | struct perf_file_section *buildid_sec; | ||
| 250 | |||
| 251 | buildid_sec = &feat_sec[idx++]; | ||
| 252 | |||
| 253 | /* Write build-ids */ | ||
| 254 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | ||
| 255 | err = dsos__write_buildid_table(fd); | ||
| 256 | if (err < 0) { | ||
| 257 | pr_debug("failed to write buildid table\n"); | ||
| 258 | goto out_free; | ||
| 259 | } | ||
| 260 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; | ||
| 261 | } | ||
| 262 | |||
| 263 | lseek(fd, sec_start, SEEK_SET); | ||
| 264 | err = do_write(fd, feat_sec, sec_size); | ||
| 265 | if (err < 0) | ||
| 266 | pr_debug("failed to write feature section\n"); | ||
| 267 | out_free: | ||
| 268 | free(feat_sec); | ||
| 269 | return err; | ||
| 161 | } | 270 | } |
| 162 | 271 | ||
| 163 | void perf_header__write(struct perf_header *self, int fd) | 272 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) |
| 164 | { | 273 | { |
| 165 | struct perf_file_header f_header; | 274 | struct perf_file_header f_header; |
| 166 | struct perf_file_attr f_attr; | 275 | struct perf_file_attr f_attr; |
| 167 | struct perf_header_attr *attr; | 276 | struct perf_header_attr *attr; |
| 168 | int i; | 277 | int i, err; |
| 169 | 278 | ||
| 170 | lseek(fd, sizeof(f_header), SEEK_SET); | 279 | lseek(fd, sizeof(f_header), SEEK_SET); |
| 171 | 280 | ||
| @@ -174,7 +283,11 @@ void perf_header__write(struct perf_header *self, int fd) | |||
| 174 | attr = self->attr[i]; | 283 | attr = self->attr[i]; |
| 175 | 284 | ||
| 176 | attr->id_offset = lseek(fd, 0, SEEK_CUR); | 285 | attr->id_offset = lseek(fd, 0, SEEK_CUR); |
| 177 | do_write(fd, attr->id, attr->ids * sizeof(u64)); | 286 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); |
| 287 | if (err < 0) { | ||
| 288 | pr_debug("failed to write perf header\n"); | ||
| 289 | return err; | ||
| 290 | } | ||
| 178 | } | 291 | } |
| 179 | 292 | ||
| 180 | 293 | ||
| @@ -190,17 +303,31 @@ void perf_header__write(struct perf_header *self, int fd) | |||
| 190 | .size = attr->ids * sizeof(u64), | 303 | .size = attr->ids * sizeof(u64), |
| 191 | } | 304 | } |
| 192 | }; | 305 | }; |
| 193 | do_write(fd, &f_attr, sizeof(f_attr)); | 306 | err = do_write(fd, &f_attr, sizeof(f_attr)); |
| 307 | if (err < 0) { | ||
| 308 | pr_debug("failed to write perf header attribute\n"); | ||
| 309 | return err; | ||
| 310 | } | ||
| 194 | } | 311 | } |
| 195 | 312 | ||
| 196 | self->event_offset = lseek(fd, 0, SEEK_CUR); | 313 | self->event_offset = lseek(fd, 0, SEEK_CUR); |
| 197 | self->event_size = event_count * sizeof(struct perf_trace_event_type); | 314 | self->event_size = event_count * sizeof(struct perf_trace_event_type); |
| 198 | if (events) | 315 | if (events) { |
| 199 | do_write(fd, events, self->event_size); | 316 | err = do_write(fd, events, self->event_size); |
| 200 | 317 | if (err < 0) { | |
| 318 | pr_debug("failed to write perf header events\n"); | ||
| 319 | return err; | ||
| 320 | } | ||
| 321 | } | ||
| 201 | 322 | ||
| 202 | self->data_offset = lseek(fd, 0, SEEK_CUR); | 323 | self->data_offset = lseek(fd, 0, SEEK_CUR); |
| 203 | 324 | ||
| 325 | if (at_exit) { | ||
| 326 | err = perf_header__adds_write(self, fd); | ||
| 327 | if (err < 0) | ||
| 328 | return err; | ||
| 329 | } | ||
| 330 | |||
| 204 | f_header = (struct perf_file_header){ | 331 | f_header = (struct perf_file_header){ |
| 205 | .magic = PERF_MAGIC, | 332 | .magic = PERF_MAGIC, |
| 206 | .size = sizeof(f_header), | 333 | .size = sizeof(f_header), |
| @@ -219,11 +346,18 @@ void perf_header__write(struct perf_header *self, int fd) | |||
| 219 | }, | 346 | }, |
| 220 | }; | 347 | }; |
| 221 | 348 | ||
| 349 | memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features)); | ||
| 350 | |||
| 222 | lseek(fd, 0, SEEK_SET); | 351 | lseek(fd, 0, SEEK_SET); |
| 223 | do_write(fd, &f_header, sizeof(f_header)); | 352 | err = do_write(fd, &f_header, sizeof(f_header)); |
| 353 | if (err < 0) { | ||
| 354 | pr_debug("failed to write perf header\n"); | ||
| 355 | return err; | ||
| 356 | } | ||
| 224 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 357 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); |
| 225 | 358 | ||
| 226 | self->frozen = 1; | 359 | self->frozen = 1; |
| 360 | return 0; | ||
| 227 | } | 361 | } |
| 228 | 362 | ||
| 229 | static void do_read(int fd, void *buf, size_t size) | 363 | static void do_read(int fd, void *buf, size_t size) |
| @@ -241,22 +375,109 @@ static void do_read(int fd, void *buf, size_t size) | |||
| 241 | } | 375 | } |
| 242 | } | 376 | } |
| 243 | 377 | ||
| 244 | struct perf_header *perf_header__read(int fd) | 378 | int perf_header__process_sections(struct perf_header *self, int fd, |
| 379 | int (*process)(struct perf_file_section *self, | ||
| 380 | int feat, int fd)) | ||
| 381 | { | ||
| 382 | struct perf_file_section *feat_sec; | ||
| 383 | int nr_sections; | ||
| 384 | int sec_size; | ||
| 385 | int idx = 0; | ||
| 386 | int err = 0, feat = 1; | ||
| 387 | |||
| 388 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | ||
| 389 | if (!nr_sections) | ||
| 390 | return 0; | ||
| 391 | |||
| 392 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | ||
| 393 | if (!feat_sec) | ||
| 394 | return -1; | ||
| 395 | |||
| 396 | sec_size = sizeof(*feat_sec) * nr_sections; | ||
| 397 | |||
| 398 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | ||
| 399 | |||
| 400 | do_read(fd, feat_sec, sec_size); | ||
| 401 | |||
| 402 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { | ||
| 403 | if (perf_header__has_feat(self, feat)) { | ||
| 404 | struct perf_file_section *sec = &feat_sec[idx++]; | ||
| 405 | |||
| 406 | err = process(sec, feat, fd); | ||
| 407 | if (err < 0) | ||
| 408 | break; | ||
| 409 | } | ||
| 410 | ++feat; | ||
| 411 | } | ||
| 412 | |||
| 413 | free(feat_sec); | ||
| 414 | return err; | ||
| 415 | }; | ||
| 416 | |||
| 417 | int perf_file_header__read(struct perf_file_header *self, | ||
| 418 | struct perf_header *ph, int fd) | ||
| 419 | { | ||
| 420 | lseek(fd, 0, SEEK_SET); | ||
| 421 | do_read(fd, self, sizeof(*self)); | ||
| 422 | |||
| 423 | if (self->magic != PERF_MAGIC || | ||
| 424 | self->attr_size != sizeof(struct perf_file_attr)) | ||
| 425 | return -1; | ||
| 426 | |||
| 427 | if (self->size != sizeof(*self)) { | ||
| 428 | /* Support the previous format */ | ||
| 429 | if (self->size == offsetof(typeof(*self), adds_features)) | ||
| 430 | bitmap_zero(self->adds_features, HEADER_FEAT_BITS); | ||
| 431 | else | ||
| 432 | return -1; | ||
| 433 | } | ||
| 434 | |||
| 435 | memcpy(&ph->adds_features, &self->adds_features, | ||
| 436 | sizeof(self->adds_features)); | ||
| 437 | |||
| 438 | ph->event_offset = self->event_types.offset; | ||
| 439 | ph->event_size = self->event_types.size; | ||
| 440 | ph->data_offset = self->data.offset; | ||
| 441 | ph->data_size = self->data.size; | ||
| 442 | return 0; | ||
| 443 | } | ||
| 444 | |||
| 445 | static int perf_file_section__process(struct perf_file_section *self, | ||
| 446 | int feat, int fd) | ||
| 447 | { | ||
| 448 | if (lseek(fd, self->offset, SEEK_SET) < 0) { | ||
| 449 | pr_debug("Failed to lseek to %Ld offset for feature %d, " | ||
| 450 | "continuing...\n", self->offset, feat); | ||
| 451 | return 0; | ||
| 452 | } | ||
| 453 | |||
| 454 | switch (feat) { | ||
| 455 | case HEADER_TRACE_INFO: | ||
| 456 | trace_report(fd); | ||
| 457 | break; | ||
| 458 | |||
| 459 | case HEADER_BUILD_ID: | ||
| 460 | if (perf_header__read_build_ids(fd, self->offset, self->size)) | ||
| 461 | pr_debug("Failed to read buildids, continuing...\n"); | ||
| 462 | break; | ||
| 463 | default: | ||
| 464 | pr_debug("unknown feature %d, continuing...\n", feat); | ||
| 465 | } | ||
| 466 | |||
| 467 | return 0; | ||
| 468 | } | ||
| 469 | |||
| 470 | int perf_header__read(struct perf_header *self, int fd) | ||
| 245 | { | 471 | { |
| 246 | struct perf_header *self = perf_header__new(); | ||
| 247 | struct perf_file_header f_header; | 472 | struct perf_file_header f_header; |
| 248 | struct perf_file_attr f_attr; | 473 | struct perf_file_attr f_attr; |
| 249 | u64 f_id; | 474 | u64 f_id; |
| 250 | |||
| 251 | int nr_attrs, nr_ids, i, j; | 475 | int nr_attrs, nr_ids, i, j; |
| 252 | 476 | ||
| 253 | lseek(fd, 0, SEEK_SET); | 477 | if (perf_file_header__read(&f_header, self, fd) < 0) { |
| 254 | do_read(fd, &f_header, sizeof(f_header)); | 478 | pr_debug("incompatible file format\n"); |
| 255 | 479 | return -EINVAL; | |
| 256 | if (f_header.magic != PERF_MAGIC || | 480 | } |
| 257 | f_header.size != sizeof(f_header) || | ||
| 258 | f_header.attr_size != sizeof(f_attr)) | ||
| 259 | die("incompatible file format"); | ||
| 260 | 481 | ||
| 261 | nr_attrs = f_header.attrs.size / sizeof(f_attr); | 482 | nr_attrs = f_header.attrs.size / sizeof(f_attr); |
| 262 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 483 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
| @@ -269,6 +490,8 @@ struct perf_header *perf_header__read(int fd) | |||
| 269 | tmp = lseek(fd, 0, SEEK_CUR); | 490 | tmp = lseek(fd, 0, SEEK_CUR); |
| 270 | 491 | ||
| 271 | attr = perf_header_attr__new(&f_attr.attr); | 492 | attr = perf_header_attr__new(&f_attr.attr); |
| 493 | if (attr == NULL) | ||
| 494 | return -ENOMEM; | ||
| 272 | 495 | ||
| 273 | nr_ids = f_attr.ids.size / sizeof(u64); | 496 | nr_ids = f_attr.ids.size / sizeof(u64); |
| 274 | lseek(fd, f_attr.ids.offset, SEEK_SET); | 497 | lseek(fd, f_attr.ids.offset, SEEK_SET); |
| @@ -276,31 +499,34 @@ struct perf_header *perf_header__read(int fd) | |||
| 276 | for (j = 0; j < nr_ids; j++) { | 499 | for (j = 0; j < nr_ids; j++) { |
| 277 | do_read(fd, &f_id, sizeof(f_id)); | 500 | do_read(fd, &f_id, sizeof(f_id)); |
| 278 | 501 | ||
| 279 | perf_header_attr__add_id(attr, f_id); | 502 | if (perf_header_attr__add_id(attr, f_id) < 0) { |
| 503 | perf_header_attr__delete(attr); | ||
| 504 | return -ENOMEM; | ||
| 505 | } | ||
| 506 | } | ||
| 507 | if (perf_header__add_attr(self, attr) < 0) { | ||
| 508 | perf_header_attr__delete(attr); | ||
| 509 | return -ENOMEM; | ||
| 280 | } | 510 | } |
| 281 | perf_header__add_attr(self, attr); | 511 | |
| 282 | lseek(fd, tmp, SEEK_SET); | 512 | lseek(fd, tmp, SEEK_SET); |
| 283 | } | 513 | } |
| 284 | 514 | ||
| 285 | if (f_header.event_types.size) { | 515 | if (f_header.event_types.size) { |
| 286 | lseek(fd, f_header.event_types.offset, SEEK_SET); | 516 | lseek(fd, f_header.event_types.offset, SEEK_SET); |
| 287 | events = malloc(f_header.event_types.size); | 517 | events = malloc(f_header.event_types.size); |
| 288 | if (!events) | 518 | if (events == NULL) |
| 289 | die("nomem"); | 519 | return -ENOMEM; |
| 290 | do_read(fd, events, f_header.event_types.size); | 520 | do_read(fd, events, f_header.event_types.size); |
| 291 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 521 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
| 292 | } | 522 | } |
| 293 | self->event_offset = f_header.event_types.offset; | ||
| 294 | self->event_size = f_header.event_types.size; | ||
| 295 | 523 | ||
| 296 | self->data_offset = f_header.data.offset; | 524 | perf_header__process_sections(self, fd, perf_file_section__process); |
| 297 | self->data_size = f_header.data.size; | ||
| 298 | 525 | ||
| 299 | lseek(fd, self->data_offset, SEEK_SET); | 526 | lseek(fd, self->data_offset, SEEK_SET); |
| 300 | 527 | ||
| 301 | self->frozen = 1; | 528 | self->frozen = 1; |
| 302 | 529 | return 0; | |
| 303 | return self; | ||
| 304 | } | 530 | } |
| 305 | 531 | ||
| 306 | u64 perf_header__sample_type(struct perf_header *header) | 532 | u64 perf_header__sample_type(struct perf_header *header) |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index a0761bc7863c..d118d05d3abe 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
| @@ -1,10 +1,13 @@ | |||
| 1 | #ifndef _PERF_HEADER_H | 1 | #ifndef __PERF_HEADER_H |
| 2 | #define _PERF_HEADER_H | 2 | #define __PERF_HEADER_H |
| 3 | 3 | ||
| 4 | #include "../../../include/linux/perf_event.h" | 4 | #include "../../../include/linux/perf_event.h" |
| 5 | #include <sys/types.h> | 5 | #include <sys/types.h> |
| 6 | #include <stdbool.h> | ||
| 6 | #include "types.h" | 7 | #include "types.h" |
| 7 | 8 | ||
| 9 | #include <linux/bitmap.h> | ||
| 10 | |||
| 8 | struct perf_header_attr { | 11 | struct perf_header_attr { |
| 9 | struct perf_event_attr attr; | 12 | struct perf_event_attr attr; |
| 10 | int ids, size; | 13 | int ids, size; |
| @@ -12,36 +15,71 @@ struct perf_header_attr { | |||
| 12 | off_t id_offset; | 15 | off_t id_offset; |
| 13 | }; | 16 | }; |
| 14 | 17 | ||
| 18 | enum { | ||
| 19 | HEADER_TRACE_INFO = 1, | ||
| 20 | HEADER_BUILD_ID, | ||
| 21 | HEADER_LAST_FEATURE, | ||
| 22 | }; | ||
| 23 | |||
| 24 | #define HEADER_FEAT_BITS 256 | ||
| 25 | |||
| 26 | struct perf_file_section { | ||
| 27 | u64 offset; | ||
| 28 | u64 size; | ||
| 29 | }; | ||
| 30 | |||
| 31 | struct perf_file_header { | ||
| 32 | u64 magic; | ||
| 33 | u64 size; | ||
| 34 | u64 attr_size; | ||
| 35 | struct perf_file_section attrs; | ||
| 36 | struct perf_file_section data; | ||
| 37 | struct perf_file_section event_types; | ||
| 38 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct perf_header; | ||
| 42 | |||
| 43 | int perf_file_header__read(struct perf_file_header *self, | ||
| 44 | struct perf_header *ph, int fd); | ||
| 45 | |||
| 15 | struct perf_header { | 46 | struct perf_header { |
| 16 | int frozen; | 47 | int frozen; |
| 17 | int attrs, size; | 48 | int attrs, size; |
| 18 | struct perf_header_attr **attr; | 49 | struct perf_header_attr **attr; |
| 19 | s64 attr_offset; | 50 | s64 attr_offset; |
| 20 | u64 data_offset; | 51 | u64 data_offset; |
| 21 | u64 data_size; | 52 | u64 data_size; |
| 22 | u64 event_offset; | 53 | u64 event_offset; |
| 23 | u64 event_size; | 54 | u64 event_size; |
| 55 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | ||
| 24 | }; | 56 | }; |
| 25 | 57 | ||
| 26 | struct perf_header *perf_header__read(int fd); | 58 | int perf_header__init(struct perf_header *self); |
| 27 | void perf_header__write(struct perf_header *self, int fd); | 59 | void perf_header__exit(struct perf_header *self); |
| 60 | |||
| 61 | int perf_header__read(struct perf_header *self, int fd); | ||
| 62 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); | ||
| 28 | 63 | ||
| 29 | void perf_header__add_attr(struct perf_header *self, | 64 | int perf_header__add_attr(struct perf_header *self, |
| 30 | struct perf_header_attr *attr); | 65 | struct perf_header_attr *attr); |
| 31 | 66 | ||
| 32 | void perf_header__push_event(u64 id, const char *name); | 67 | void perf_header__push_event(u64 id, const char *name); |
| 33 | char *perf_header__find_event(u64 id); | 68 | char *perf_header__find_event(u64 id); |
| 34 | 69 | ||
| 70 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); | ||
| 71 | void perf_header_attr__delete(struct perf_header_attr *self); | ||
| 35 | 72 | ||
| 36 | struct perf_header_attr * | 73 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); |
| 37 | perf_header_attr__new(struct perf_event_attr *attr); | ||
| 38 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id); | ||
| 39 | 74 | ||
| 40 | u64 perf_header__sample_type(struct perf_header *header); | 75 | u64 perf_header__sample_type(struct perf_header *header); |
| 41 | struct perf_event_attr * | 76 | struct perf_event_attr * |
| 42 | perf_header__find_attr(u64 id, struct perf_header *header); | 77 | perf_header__find_attr(u64 id, struct perf_header *header); |
| 78 | void perf_header__set_feat(struct perf_header *self, int feat); | ||
| 79 | bool perf_header__has_feat(const struct perf_header *self, int feat); | ||
| 43 | 80 | ||
| 81 | int perf_header__process_sections(struct perf_header *self, int fd, | ||
| 82 | int (*process)(struct perf_file_section *self, | ||
| 83 | int feat, int fd)); | ||
| 44 | 84 | ||
| 45 | struct perf_header *perf_header__new(void); | 85 | #endif /* __PERF_HEADER_H */ |
| 46 | |||
| 47 | #endif /* _PERF_HEADER_H */ | ||
diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h index 7128783637b4..7f5c6dedd714 100644 --- a/tools/perf/util/help.h +++ b/tools/perf/util/help.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef HELP_H | 1 | #ifndef __PERF_HELP_H |
| 2 | #define HELP_H | 2 | #define __PERF_HELP_H |
| 3 | 3 | ||
| 4 | struct cmdnames { | 4 | struct cmdnames { |
| 5 | size_t alloc; | 5 | size_t alloc; |
| @@ -26,4 +26,4 @@ int is_in_cmdlist(struct cmdnames *c, const char *s); | |||
| 26 | void list_commands(const char *title, struct cmdnames *main_cmds, | 26 | void list_commands(const char *title, struct cmdnames *main_cmds, |
| 27 | struct cmdnames *other_cmds); | 27 | struct cmdnames *other_cmds); |
| 28 | 28 | ||
| 29 | #endif /* HELP_H */ | 29 | #endif /* __PERF_HELP_H */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c new file mode 100644 index 000000000000..e8daf5ca6fd2 --- /dev/null +++ b/tools/perf/util/hist.c | |||
| @@ -0,0 +1,662 @@ | |||
| 1 | #include "hist.h" | ||
| 2 | #include "session.h" | ||
| 3 | #include "sort.h" | ||
| 4 | #include <math.h> | ||
| 5 | |||
| 6 | struct callchain_param callchain_param = { | ||
| 7 | .mode = CHAIN_GRAPH_REL, | ||
| 8 | .min_percent = 0.5 | ||
| 9 | }; | ||
| 10 | |||
| 11 | /* | ||
| 12 | * histogram, sorted on item, collects counts | ||
| 13 | */ | ||
| 14 | |||
| 15 | struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, | ||
| 16 | struct addr_location *al, | ||
| 17 | struct symbol *sym_parent, | ||
| 18 | u64 count, bool *hit) | ||
| 19 | { | ||
| 20 | struct rb_node **p = &self->hists.rb_node; | ||
| 21 | struct rb_node *parent = NULL; | ||
| 22 | struct hist_entry *he; | ||
| 23 | struct hist_entry entry = { | ||
| 24 | .thread = al->thread, | ||
| 25 | .map = al->map, | ||
| 26 | .sym = al->sym, | ||
| 27 | .ip = al->addr, | ||
| 28 | .level = al->level, | ||
| 29 | .count = count, | ||
| 30 | .parent = sym_parent, | ||
| 31 | }; | ||
| 32 | int cmp; | ||
| 33 | |||
| 34 | while (*p != NULL) { | ||
| 35 | parent = *p; | ||
| 36 | he = rb_entry(parent, struct hist_entry, rb_node); | ||
| 37 | |||
| 38 | cmp = hist_entry__cmp(&entry, he); | ||
| 39 | |||
| 40 | if (!cmp) { | ||
| 41 | *hit = true; | ||
| 42 | return he; | ||
| 43 | } | ||
| 44 | |||
| 45 | if (cmp < 0) | ||
| 46 | p = &(*p)->rb_left; | ||
| 47 | else | ||
| 48 | p = &(*p)->rb_right; | ||
| 49 | } | ||
| 50 | |||
| 51 | he = malloc(sizeof(*he)); | ||
| 52 | if (!he) | ||
| 53 | return NULL; | ||
| 54 | *he = entry; | ||
| 55 | rb_link_node(&he->rb_node, parent, p); | ||
| 56 | rb_insert_color(&he->rb_node, &self->hists); | ||
| 57 | *hit = false; | ||
| 58 | return he; | ||
| 59 | } | ||
| 60 | |||
| 61 | int64_t | ||
| 62 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 63 | { | ||
| 64 | struct sort_entry *se; | ||
| 65 | int64_t cmp = 0; | ||
| 66 | |||
| 67 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 68 | cmp = se->cmp(left, right); | ||
| 69 | if (cmp) | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | |||
| 73 | return cmp; | ||
| 74 | } | ||
| 75 | |||
| 76 | int64_t | ||
| 77 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | ||
| 78 | { | ||
| 79 | struct sort_entry *se; | ||
| 80 | int64_t cmp = 0; | ||
| 81 | |||
| 82 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 83 | int64_t (*f)(struct hist_entry *, struct hist_entry *); | ||
| 84 | |||
| 85 | f = se->collapse ?: se->cmp; | ||
| 86 | |||
| 87 | cmp = f(left, right); | ||
| 88 | if (cmp) | ||
| 89 | break; | ||
| 90 | } | ||
| 91 | |||
| 92 | return cmp; | ||
| 93 | } | ||
| 94 | |||
| 95 | void hist_entry__free(struct hist_entry *he) | ||
| 96 | { | ||
| 97 | free(he); | ||
| 98 | } | ||
| 99 | |||
| 100 | /* | ||
| 101 | * collapse the histogram | ||
| 102 | */ | ||
| 103 | |||
| 104 | static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | ||
| 105 | { | ||
| 106 | struct rb_node **p = &root->rb_node; | ||
| 107 | struct rb_node *parent = NULL; | ||
| 108 | struct hist_entry *iter; | ||
| 109 | int64_t cmp; | ||
| 110 | |||
| 111 | while (*p != NULL) { | ||
| 112 | parent = *p; | ||
| 113 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
| 114 | |||
| 115 | cmp = hist_entry__collapse(iter, he); | ||
| 116 | |||
| 117 | if (!cmp) { | ||
| 118 | iter->count += he->count; | ||
| 119 | hist_entry__free(he); | ||
| 120 | return; | ||
| 121 | } | ||
| 122 | |||
| 123 | if (cmp < 0) | ||
| 124 | p = &(*p)->rb_left; | ||
| 125 | else | ||
| 126 | p = &(*p)->rb_right; | ||
| 127 | } | ||
| 128 | |||
| 129 | rb_link_node(&he->rb_node, parent, p); | ||
| 130 | rb_insert_color(&he->rb_node, root); | ||
| 131 | } | ||
| 132 | |||
| 133 | void perf_session__collapse_resort(struct perf_session *self) | ||
| 134 | { | ||
| 135 | struct rb_root tmp; | ||
| 136 | struct rb_node *next; | ||
| 137 | struct hist_entry *n; | ||
| 138 | |||
| 139 | if (!sort__need_collapse) | ||
| 140 | return; | ||
| 141 | |||
| 142 | tmp = RB_ROOT; | ||
| 143 | next = rb_first(&self->hists); | ||
| 144 | |||
| 145 | while (next) { | ||
| 146 | n = rb_entry(next, struct hist_entry, rb_node); | ||
| 147 | next = rb_next(&n->rb_node); | ||
| 148 | |||
| 149 | rb_erase(&n->rb_node, &self->hists); | ||
| 150 | collapse__insert_entry(&tmp, n); | ||
| 151 | } | ||
| 152 | |||
| 153 | self->hists = tmp; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* | ||
| 157 | * reverse the map, sort on count. | ||
| 158 | */ | ||
| 159 | |||
| 160 | static void perf_session__insert_output_hist_entry(struct rb_root *root, | ||
| 161 | struct hist_entry *he, | ||
| 162 | u64 min_callchain_hits) | ||
| 163 | { | ||
| 164 | struct rb_node **p = &root->rb_node; | ||
| 165 | struct rb_node *parent = NULL; | ||
| 166 | struct hist_entry *iter; | ||
| 167 | |||
| 168 | if (symbol_conf.use_callchain) | ||
| 169 | callchain_param.sort(&he->sorted_chain, &he->callchain, | ||
| 170 | min_callchain_hits, &callchain_param); | ||
| 171 | |||
| 172 | while (*p != NULL) { | ||
| 173 | parent = *p; | ||
| 174 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
| 175 | |||
| 176 | if (he->count > iter->count) | ||
| 177 | p = &(*p)->rb_left; | ||
| 178 | else | ||
| 179 | p = &(*p)->rb_right; | ||
| 180 | } | ||
| 181 | |||
| 182 | rb_link_node(&he->rb_node, parent, p); | ||
| 183 | rb_insert_color(&he->rb_node, root); | ||
| 184 | } | ||
| 185 | |||
| 186 | void perf_session__output_resort(struct perf_session *self, u64 total_samples) | ||
| 187 | { | ||
| 188 | struct rb_root tmp; | ||
| 189 | struct rb_node *next; | ||
| 190 | struct hist_entry *n; | ||
| 191 | u64 min_callchain_hits; | ||
| 192 | |||
| 193 | min_callchain_hits = | ||
| 194 | total_samples * (callchain_param.min_percent / 100); | ||
| 195 | |||
| 196 | tmp = RB_ROOT; | ||
| 197 | next = rb_first(&self->hists); | ||
| 198 | |||
| 199 | while (next) { | ||
| 200 | n = rb_entry(next, struct hist_entry, rb_node); | ||
| 201 | next = rb_next(&n->rb_node); | ||
| 202 | |||
| 203 | rb_erase(&n->rb_node, &self->hists); | ||
| 204 | perf_session__insert_output_hist_entry(&tmp, n, | ||
| 205 | min_callchain_hits); | ||
| 206 | } | ||
| 207 | |||
| 208 | self->hists = tmp; | ||
| 209 | } | ||
| 210 | |||
| 211 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | ||
| 212 | { | ||
| 213 | int i; | ||
| 214 | int ret = fprintf(fp, " "); | ||
| 215 | |||
| 216 | for (i = 0; i < left_margin; i++) | ||
| 217 | ret += fprintf(fp, " "); | ||
| 218 | |||
| 219 | return ret; | ||
| 220 | } | ||
| 221 | |||
| 222 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | ||
| 223 | int left_margin) | ||
| 224 | { | ||
| 225 | int i; | ||
| 226 | size_t ret = callchain__fprintf_left_margin(fp, left_margin); | ||
| 227 | |||
| 228 | for (i = 0; i < depth; i++) | ||
| 229 | if (depth_mask & (1 << i)) | ||
| 230 | ret += fprintf(fp, "| "); | ||
| 231 | else | ||
| 232 | ret += fprintf(fp, " "); | ||
| 233 | |||
| 234 | ret += fprintf(fp, "\n"); | ||
| 235 | |||
| 236 | return ret; | ||
| 237 | } | ||
| 238 | |||
| 239 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | ||
| 240 | int depth, int depth_mask, int count, | ||
| 241 | u64 total_samples, int hits, | ||
| 242 | int left_margin) | ||
| 243 | { | ||
| 244 | int i; | ||
| 245 | size_t ret = 0; | ||
| 246 | |||
| 247 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 248 | for (i = 0; i < depth; i++) { | ||
| 249 | if (depth_mask & (1 << i)) | ||
| 250 | ret += fprintf(fp, "|"); | ||
| 251 | else | ||
| 252 | ret += fprintf(fp, " "); | ||
| 253 | if (!count && i == depth - 1) { | ||
| 254 | double percent; | ||
| 255 | |||
| 256 | percent = hits * 100.0 / total_samples; | ||
| 257 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
| 258 | } else | ||
| 259 | ret += fprintf(fp, "%s", " "); | ||
| 260 | } | ||
| 261 | if (chain->sym) | ||
| 262 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
| 263 | else | ||
| 264 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
| 265 | |||
| 266 | return ret; | ||
| 267 | } | ||
| 268 | |||
| 269 | static struct symbol *rem_sq_bracket; | ||
| 270 | static struct callchain_list rem_hits; | ||
| 271 | |||
| 272 | static void init_rem_hits(void) | ||
| 273 | { | ||
| 274 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
| 275 | if (!rem_sq_bracket) { | ||
| 276 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
| 277 | return; | ||
| 278 | } | ||
| 279 | |||
| 280 | strcpy(rem_sq_bracket->name, "[...]"); | ||
| 281 | rem_hits.sym = rem_sq_bracket; | ||
| 282 | } | ||
| 283 | |||
| 284 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
| 285 | u64 total_samples, int depth, | ||
| 286 | int depth_mask, int left_margin) | ||
| 287 | { | ||
| 288 | struct rb_node *node, *next; | ||
| 289 | struct callchain_node *child; | ||
| 290 | struct callchain_list *chain; | ||
| 291 | int new_depth_mask = depth_mask; | ||
| 292 | u64 new_total; | ||
| 293 | u64 remaining; | ||
| 294 | size_t ret = 0; | ||
| 295 | int i; | ||
| 296 | |||
| 297 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
| 298 | new_total = self->children_hit; | ||
| 299 | else | ||
| 300 | new_total = total_samples; | ||
| 301 | |||
| 302 | remaining = new_total; | ||
| 303 | |||
| 304 | node = rb_first(&self->rb_root); | ||
| 305 | while (node) { | ||
| 306 | u64 cumul; | ||
| 307 | |||
| 308 | child = rb_entry(node, struct callchain_node, rb_node); | ||
| 309 | cumul = cumul_hits(child); | ||
| 310 | remaining -= cumul; | ||
| 311 | |||
| 312 | /* | ||
| 313 | * The depth mask manages the output of pipes that show | ||
| 314 | * the depth. We don't want to keep the pipes of the current | ||
| 315 | * level for the last child of this depth. | ||
| 316 | * Except if we have remaining filtered hits. They will | ||
| 317 | * supersede the last child | ||
| 318 | */ | ||
| 319 | next = rb_next(node); | ||
| 320 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
| 321 | new_depth_mask &= ~(1 << (depth - 1)); | ||
| 322 | |||
| 323 | /* | ||
| 324 | * But we keep the older depth mask for the line seperator | ||
| 325 | * to keep the level link until we reach the last child | ||
| 326 | */ | ||
| 327 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | ||
| 328 | left_margin); | ||
| 329 | i = 0; | ||
| 330 | list_for_each_entry(chain, &child->val, list) { | ||
| 331 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 332 | continue; | ||
| 333 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
| 334 | new_depth_mask, i++, | ||
| 335 | new_total, | ||
| 336 | cumul, | ||
| 337 | left_margin); | ||
| 338 | } | ||
| 339 | ret += __callchain__fprintf_graph(fp, child, new_total, | ||
| 340 | depth + 1, | ||
| 341 | new_depth_mask | (1 << depth), | ||
| 342 | left_margin); | ||
| 343 | node = next; | ||
| 344 | } | ||
| 345 | |||
| 346 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
| 347 | remaining && remaining != new_total) { | ||
| 348 | |||
| 349 | if (!rem_sq_bracket) | ||
| 350 | return ret; | ||
| 351 | |||
| 352 | new_depth_mask &= ~(1 << (depth - 1)); | ||
| 353 | |||
| 354 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
| 355 | new_depth_mask, 0, new_total, | ||
| 356 | remaining, left_margin); | ||
| 357 | } | ||
| 358 | |||
| 359 | return ret; | ||
| 360 | } | ||
| 361 | |||
| 362 | static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
| 363 | u64 total_samples, int left_margin) | ||
| 364 | { | ||
| 365 | struct callchain_list *chain; | ||
| 366 | bool printed = false; | ||
| 367 | int i = 0; | ||
| 368 | int ret = 0; | ||
| 369 | |||
| 370 | list_for_each_entry(chain, &self->val, list) { | ||
| 371 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 372 | continue; | ||
| 373 | |||
| 374 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
| 375 | continue; | ||
| 376 | |||
| 377 | if (!printed) { | ||
| 378 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 379 | ret += fprintf(fp, "|\n"); | ||
| 380 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 381 | ret += fprintf(fp, "---"); | ||
| 382 | |||
| 383 | left_margin += 3; | ||
| 384 | printed = true; | ||
| 385 | } else | ||
| 386 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 387 | |||
| 388 | if (chain->sym) | ||
| 389 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
| 390 | else | ||
| 391 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
| 392 | } | ||
| 393 | |||
| 394 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | ||
| 395 | |||
| 396 | return ret; | ||
| 397 | } | ||
| 398 | |||
| 399 | static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
| 400 | u64 total_samples) | ||
| 401 | { | ||
| 402 | struct callchain_list *chain; | ||
| 403 | size_t ret = 0; | ||
| 404 | |||
| 405 | if (!self) | ||
| 406 | return 0; | ||
| 407 | |||
| 408 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | ||
| 409 | |||
| 410 | |||
| 411 | list_for_each_entry(chain, &self->val, list) { | ||
| 412 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 413 | continue; | ||
| 414 | if (chain->sym) | ||
| 415 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
| 416 | else | ||
| 417 | ret += fprintf(fp, " %p\n", | ||
| 418 | (void *)(long)chain->ip); | ||
| 419 | } | ||
| 420 | |||
| 421 | return ret; | ||
| 422 | } | ||
| 423 | |||
| 424 | static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
| 425 | u64 total_samples, int left_margin) | ||
| 426 | { | ||
| 427 | struct rb_node *rb_node; | ||
| 428 | struct callchain_node *chain; | ||
| 429 | size_t ret = 0; | ||
| 430 | |||
| 431 | rb_node = rb_first(&self->sorted_chain); | ||
| 432 | while (rb_node) { | ||
| 433 | double percent; | ||
| 434 | |||
| 435 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
| 436 | percent = chain->hit * 100.0 / total_samples; | ||
| 437 | switch (callchain_param.mode) { | ||
| 438 | case CHAIN_FLAT: | ||
| 439 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
| 440 | percent); | ||
| 441 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
| 442 | break; | ||
| 443 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
| 444 | case CHAIN_GRAPH_REL: | ||
| 445 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
| 446 | left_margin); | ||
| 447 | case CHAIN_NONE: | ||
| 448 | default: | ||
| 449 | break; | ||
| 450 | } | ||
| 451 | ret += fprintf(fp, "\n"); | ||
| 452 | rb_node = rb_next(rb_node); | ||
| 453 | } | ||
| 454 | |||
| 455 | return ret; | ||
| 456 | } | ||
| 457 | |||
| 458 | static size_t hist_entry__fprintf(struct hist_entry *self, | ||
| 459 | struct perf_session *session, | ||
| 460 | struct perf_session *pair_session, | ||
| 461 | bool show_displacement, | ||
| 462 | long displacement, FILE *fp) | ||
| 463 | { | ||
| 464 | struct sort_entry *se; | ||
| 465 | u64 count, total; | ||
| 466 | const char *sep = symbol_conf.field_sep; | ||
| 467 | size_t ret; | ||
| 468 | |||
| 469 | if (symbol_conf.exclude_other && !self->parent) | ||
| 470 | return 0; | ||
| 471 | |||
| 472 | if (pair_session) { | ||
| 473 | count = self->pair ? self->pair->count : 0; | ||
| 474 | total = pair_session->events_stats.total; | ||
| 475 | } else { | ||
| 476 | count = self->count; | ||
| 477 | total = session->events_stats.total; | ||
| 478 | } | ||
| 479 | |||
| 480 | if (total) | ||
| 481 | ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", | ||
| 482 | (count * 100.0) / total); | ||
| 483 | else | ||
| 484 | ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); | ||
| 485 | |||
| 486 | if (symbol_conf.show_nr_samples) { | ||
| 487 | if (sep) | ||
| 488 | fprintf(fp, "%c%lld", *sep, count); | ||
| 489 | else | ||
| 490 | fprintf(fp, "%11lld", count); | ||
| 491 | } | ||
| 492 | |||
| 493 | if (pair_session) { | ||
| 494 | char bf[32]; | ||
| 495 | double old_percent = 0, new_percent = 0, diff; | ||
| 496 | |||
| 497 | if (total > 0) | ||
| 498 | old_percent = (count * 100.0) / total; | ||
| 499 | if (session->events_stats.total > 0) | ||
| 500 | new_percent = (self->count * 100.0) / session->events_stats.total; | ||
| 501 | |||
| 502 | diff = new_percent - old_percent; | ||
| 503 | |||
| 504 | if (fabs(diff) >= 0.01) | ||
| 505 | snprintf(bf, sizeof(bf), "%+4.2F%%", diff); | ||
| 506 | else | ||
| 507 | snprintf(bf, sizeof(bf), " "); | ||
| 508 | |||
| 509 | if (sep) | ||
| 510 | ret += fprintf(fp, "%c%s", *sep, bf); | ||
| 511 | else | ||
| 512 | ret += fprintf(fp, "%11.11s", bf); | ||
| 513 | |||
| 514 | if (show_displacement) { | ||
| 515 | if (displacement) | ||
| 516 | snprintf(bf, sizeof(bf), "%+4ld", displacement); | ||
| 517 | else | ||
| 518 | snprintf(bf, sizeof(bf), " "); | ||
| 519 | |||
| 520 | if (sep) | ||
| 521 | fprintf(fp, "%c%s", *sep, bf); | ||
| 522 | else | ||
| 523 | fprintf(fp, "%6.6s", bf); | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 528 | if (se->elide) | ||
| 529 | continue; | ||
| 530 | |||
| 531 | fprintf(fp, "%s", sep ?: " "); | ||
| 532 | ret += se->print(fp, self, se->width ? *se->width : 0); | ||
| 533 | } | ||
| 534 | |||
| 535 | ret += fprintf(fp, "\n"); | ||
| 536 | |||
| 537 | if (symbol_conf.use_callchain) { | ||
| 538 | int left_margin = 0; | ||
| 539 | |||
| 540 | if (sort__first_dimension == SORT_COMM) { | ||
| 541 | se = list_first_entry(&hist_entry__sort_list, typeof(*se), | ||
| 542 | list); | ||
| 543 | left_margin = se->width ? *se->width : 0; | ||
| 544 | left_margin -= thread__comm_len(self->thread); | ||
| 545 | } | ||
| 546 | |||
| 547 | hist_entry_callchain__fprintf(fp, self, session->events_stats.total, | ||
| 548 | left_margin); | ||
| 549 | } | ||
| 550 | |||
| 551 | return ret; | ||
| 552 | } | ||
| 553 | |||
| 554 | size_t perf_session__fprintf_hists(struct perf_session *self, | ||
| 555 | struct perf_session *pair, | ||
| 556 | bool show_displacement, FILE *fp) | ||
| 557 | { | ||
| 558 | struct sort_entry *se; | ||
| 559 | struct rb_node *nd; | ||
| 560 | size_t ret = 0; | ||
| 561 | unsigned long position = 1; | ||
| 562 | long displacement = 0; | ||
| 563 | unsigned int width; | ||
| 564 | const char *sep = symbol_conf.field_sep; | ||
| 565 | char *col_width = symbol_conf.col_width_list_str; | ||
| 566 | |||
| 567 | init_rem_hits(); | ||
| 568 | |||
| 569 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); | ||
| 570 | |||
| 571 | if (symbol_conf.show_nr_samples) { | ||
| 572 | if (sep) | ||
| 573 | fprintf(fp, "%cSamples", *sep); | ||
| 574 | else | ||
| 575 | fputs(" Samples ", fp); | ||
| 576 | } | ||
| 577 | |||
| 578 | if (pair) { | ||
| 579 | if (sep) | ||
| 580 | ret += fprintf(fp, "%cDelta", *sep); | ||
| 581 | else | ||
| 582 | ret += fprintf(fp, " Delta "); | ||
| 583 | |||
| 584 | if (show_displacement) { | ||
| 585 | if (sep) | ||
| 586 | ret += fprintf(fp, "%cDisplacement", *sep); | ||
| 587 | else | ||
| 588 | ret += fprintf(fp, " Displ"); | ||
| 589 | } | ||
| 590 | } | ||
| 591 | |||
| 592 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 593 | if (se->elide) | ||
| 594 | continue; | ||
| 595 | if (sep) { | ||
| 596 | fprintf(fp, "%c%s", *sep, se->header); | ||
| 597 | continue; | ||
| 598 | } | ||
| 599 | width = strlen(se->header); | ||
| 600 | if (se->width) { | ||
| 601 | if (symbol_conf.col_width_list_str) { | ||
| 602 | if (col_width) { | ||
| 603 | *se->width = atoi(col_width); | ||
| 604 | col_width = strchr(col_width, ','); | ||
| 605 | if (col_width) | ||
| 606 | ++col_width; | ||
| 607 | } | ||
| 608 | } | ||
| 609 | width = *se->width = max(*se->width, width); | ||
| 610 | } | ||
| 611 | fprintf(fp, " %*s", width, se->header); | ||
| 612 | } | ||
| 613 | fprintf(fp, "\n"); | ||
| 614 | |||
| 615 | if (sep) | ||
| 616 | goto print_entries; | ||
| 617 | |||
| 618 | fprintf(fp, "# ........"); | ||
| 619 | if (symbol_conf.show_nr_samples) | ||
| 620 | fprintf(fp, " .........."); | ||
| 621 | if (pair) { | ||
| 622 | fprintf(fp, " .........."); | ||
| 623 | if (show_displacement) | ||
| 624 | fprintf(fp, " ....."); | ||
| 625 | } | ||
| 626 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 627 | unsigned int i; | ||
| 628 | |||
| 629 | if (se->elide) | ||
| 630 | continue; | ||
| 631 | |||
| 632 | fprintf(fp, " "); | ||
| 633 | if (se->width) | ||
| 634 | width = *se->width; | ||
| 635 | else | ||
| 636 | width = strlen(se->header); | ||
| 637 | for (i = 0; i < width; i++) | ||
| 638 | fprintf(fp, "."); | ||
| 639 | } | ||
| 640 | |||
| 641 | fprintf(fp, "\n#\n"); | ||
| 642 | |||
| 643 | print_entries: | ||
| 644 | for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { | ||
| 645 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
| 646 | |||
| 647 | if (show_displacement) { | ||
| 648 | if (h->pair != NULL) | ||
| 649 | displacement = ((long)h->pair->position - | ||
| 650 | (long)position); | ||
| 651 | else | ||
| 652 | displacement = 0; | ||
| 653 | ++position; | ||
| 654 | } | ||
| 655 | ret += hist_entry__fprintf(h, self, pair, show_displacement, | ||
| 656 | displacement, fp); | ||
| 657 | } | ||
| 658 | |||
| 659 | free(rem_sq_bracket); | ||
| 660 | |||
| 661 | return ret; | ||
| 662 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h new file mode 100644 index 000000000000..e5f99b24048b --- /dev/null +++ b/tools/perf/util/hist.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #ifndef __PERF_HIST_H | ||
| 2 | #define __PERF_HIST_H | ||
| 3 | |||
| 4 | #include <linux/types.h> | ||
| 5 | #include "callchain.h" | ||
| 6 | |||
| 7 | extern struct callchain_param callchain_param; | ||
| 8 | |||
| 9 | struct perf_session; | ||
| 10 | struct hist_entry; | ||
| 11 | struct addr_location; | ||
| 12 | struct symbol; | ||
| 13 | |||
| 14 | struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, | ||
| 15 | struct addr_location *al, | ||
| 16 | struct symbol *parent, | ||
| 17 | u64 count, bool *hit); | ||
| 18 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | ||
| 19 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | ||
| 20 | void hist_entry__free(struct hist_entry *); | ||
| 21 | |||
| 22 | void perf_session__output_resort(struct perf_session *self, u64 total_samples); | ||
| 23 | void perf_session__collapse_resort(struct perf_session *self); | ||
| 24 | size_t perf_session__fprintf_hists(struct perf_session *self, | ||
| 25 | struct perf_session *pair, | ||
| 26 | bool show_displacement, FILE *fp); | ||
| 27 | #endif /* __PERF_HIST_H */ | ||
diff --git a/tools/perf/util/include/asm/asm-offsets.h b/tools/perf/util/include/asm/asm-offsets.h new file mode 100644 index 000000000000..ed538942523d --- /dev/null +++ b/tools/perf/util/include/asm/asm-offsets.h | |||
| @@ -0,0 +1 @@ | |||
| /* stub */ | |||
diff --git a/tools/perf/util/include/asm/bitops.h b/tools/perf/util/include/asm/bitops.h new file mode 100644 index 000000000000..58e9817ffae0 --- /dev/null +++ b/tools/perf/util/include/asm/bitops.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #ifndef _PERF_ASM_BITOPS_H_ | ||
| 2 | #define _PERF_ASM_BITOPS_H_ | ||
| 3 | |||
| 4 | #include <sys/types.h> | ||
| 5 | #include "../../types.h" | ||
| 6 | #include <linux/compiler.h> | ||
| 7 | |||
| 8 | /* CHECKME: Not sure both always match */ | ||
| 9 | #define BITS_PER_LONG __WORDSIZE | ||
| 10 | |||
| 11 | #include "../../../../include/asm-generic/bitops/__fls.h" | ||
| 12 | #include "../../../../include/asm-generic/bitops/fls.h" | ||
| 13 | #include "../../../../include/asm-generic/bitops/fls64.h" | ||
| 14 | #include "../../../../include/asm-generic/bitops/__ffs.h" | ||
| 15 | #include "../../../../include/asm-generic/bitops/ffz.h" | ||
| 16 | #include "../../../../include/asm-generic/bitops/hweight.h" | ||
| 17 | |||
| 18 | #endif | ||
diff --git a/tools/perf/util/include/asm/bug.h b/tools/perf/util/include/asm/bug.h new file mode 100644 index 000000000000..7fcc6810adc2 --- /dev/null +++ b/tools/perf/util/include/asm/bug.h | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | #ifndef _PERF_ASM_GENERIC_BUG_H | ||
| 2 | #define _PERF_ASM_GENERIC_BUG_H | ||
| 3 | |||
| 4 | #define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0) | ||
| 5 | |||
| 6 | #define WARN(condition, format...) ({ \ | ||
| 7 | int __ret_warn_on = !!(condition); \ | ||
| 8 | if (unlikely(__ret_warn_on)) \ | ||
| 9 | __WARN_printf(format); \ | ||
| 10 | unlikely(__ret_warn_on); \ | ||
| 11 | }) | ||
| 12 | |||
| 13 | #define WARN_ONCE(condition, format...) ({ \ | ||
| 14 | static int __warned; \ | ||
| 15 | int __ret_warn_once = !!(condition); \ | ||
| 16 | \ | ||
| 17 | if (unlikely(__ret_warn_once)) \ | ||
| 18 | if (WARN(!__warned, format)) \ | ||
| 19 | __warned = 1; \ | ||
| 20 | unlikely(__ret_warn_once); \ | ||
| 21 | }) | ||
| 22 | #endif | ||
diff --git a/tools/perf/util/include/asm/byteorder.h b/tools/perf/util/include/asm/byteorder.h new file mode 100644 index 000000000000..b722abe3a626 --- /dev/null +++ b/tools/perf/util/include/asm/byteorder.h | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | #include <asm/types.h> | ||
| 2 | #include "../../../../include/linux/swab.h" | ||
diff --git a/tools/perf/util/include/asm/swab.h b/tools/perf/util/include/asm/swab.h new file mode 100644 index 000000000000..ed538942523d --- /dev/null +++ b/tools/perf/util/include/asm/swab.h | |||
| @@ -0,0 +1 @@ | |||
| /* stub */ | |||
diff --git a/tools/perf/util/include/asm/uaccess.h b/tools/perf/util/include/asm/uaccess.h new file mode 100644 index 000000000000..d0f72b8fcc35 --- /dev/null +++ b/tools/perf/util/include/asm/uaccess.h | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | #ifndef _PERF_ASM_UACCESS_H_ | ||
| 2 | #define _PERF_ASM_UACCESS_H_ | ||
| 3 | |||
| 4 | #define __get_user(src, dest) \ | ||
| 5 | ({ \ | ||
| 6 | (src) = *dest; \ | ||
| 7 | 0; \ | ||
| 8 | }) | ||
| 9 | |||
| 10 | #define get_user __get_user | ||
| 11 | |||
| 12 | #define access_ok(type, addr, size) 1 | ||
| 13 | |||
| 14 | #endif | ||
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h new file mode 100644 index 000000000000..94507639a8c4 --- /dev/null +++ b/tools/perf/util/include/linux/bitmap.h | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #include "../../../../include/linux/bitmap.h" | ||
| 2 | #include "../../../../include/asm-generic/bitops/find.h" | ||
| 3 | #include <linux/errno.h> | ||
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h new file mode 100644 index 000000000000..8d63116e9435 --- /dev/null +++ b/tools/perf/util/include/linux/bitops.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #ifndef _PERF_LINUX_BITOPS_H_ | ||
| 2 | #define _PERF_LINUX_BITOPS_H_ | ||
| 3 | |||
| 4 | #define __KERNEL__ | ||
| 5 | |||
| 6 | #define CONFIG_GENERIC_FIND_NEXT_BIT | ||
| 7 | #define CONFIG_GENERIC_FIND_FIRST_BIT | ||
| 8 | #include "../../../../include/linux/bitops.h" | ||
| 9 | |||
| 10 | #undef __KERNEL__ | ||
| 11 | |||
| 12 | static inline void set_bit(int nr, unsigned long *addr) | ||
| 13 | { | ||
| 14 | addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG); | ||
| 15 | } | ||
| 16 | |||
| 17 | static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) | ||
| 18 | { | ||
| 19 | return ((1UL << (nr % BITS_PER_LONG)) & | ||
| 20 | (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0; | ||
| 21 | } | ||
| 22 | |||
| 23 | unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned | ||
| 24 | long size, unsigned long offset); | ||
| 25 | |||
| 26 | unsigned long generic_find_next_le_bit(const unsigned long *addr, unsigned | ||
| 27 | long size, unsigned long offset); | ||
| 28 | |||
| 29 | #endif | ||
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h new file mode 100644 index 000000000000..dfb0713ed47f --- /dev/null +++ b/tools/perf/util/include/linux/compiler.h | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | #ifndef _PERF_LINUX_COMPILER_H_ | ||
| 2 | #define _PERF_LINUX_COMPILER_H_ | ||
| 3 | |||
| 4 | #ifndef __always_inline | ||
| 5 | #define __always_inline inline | ||
| 6 | #endif | ||
| 7 | #define __user | ||
| 8 | #define __attribute_const__ | ||
| 9 | |||
| 10 | #endif | ||
diff --git a/tools/perf/util/include/linux/ctype.h b/tools/perf/util/include/linux/ctype.h new file mode 100644 index 000000000000..a53d4ee1e0b7 --- /dev/null +++ b/tools/perf/util/include/linux/ctype.h | |||
| @@ -0,0 +1 @@ | |||
| #include "../util.h" | |||
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index a6b87390cb52..21c0274c02fa 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h | |||
| @@ -1,6 +1,16 @@ | |||
| 1 | #ifndef PERF_LINUX_KERNEL_H_ | 1 | #ifndef PERF_LINUX_KERNEL_H_ |
| 2 | #define PERF_LINUX_KERNEL_H_ | 2 | #define PERF_LINUX_KERNEL_H_ |
| 3 | 3 | ||
| 4 | #include <stdarg.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include <assert.h> | ||
| 8 | |||
| 9 | #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) | ||
| 10 | |||
| 11 | #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) | ||
| 12 | #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) | ||
| 13 | |||
| 4 | #ifndef offsetof | 14 | #ifndef offsetof |
| 5 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) | 15 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) |
| 6 | #endif | 16 | #endif |
| @@ -26,4 +36,70 @@ | |||
| 26 | _max1 > _max2 ? _max1 : _max2; }) | 36 | _max1 > _max2 ? _max1 : _max2; }) |
| 27 | #endif | 37 | #endif |
| 28 | 38 | ||
| 39 | #ifndef min | ||
| 40 | #define min(x, y) ({ \ | ||
| 41 | typeof(x) _min1 = (x); \ | ||
| 42 | typeof(y) _min2 = (y); \ | ||
| 43 | (void) (&_min1 == &_min2); \ | ||
| 44 | _min1 < _min2 ? _min1 : _min2; }) | ||
| 45 | #endif | ||
| 46 | |||
| 47 | #ifndef BUG_ON | ||
| 48 | #define BUG_ON(cond) assert(!(cond)) | ||
| 49 | #endif | ||
| 50 | |||
| 51 | /* | ||
| 52 | * Both need more care to handle endianness | ||
| 53 | * (Don't use bitmap_copy_le() for now) | ||
| 54 | */ | ||
| 55 | #define cpu_to_le64(x) (x) | ||
| 56 | #define cpu_to_le32(x) (x) | ||
| 57 | |||
| 58 | static inline int | ||
| 59 | vscnprintf(char *buf, size_t size, const char *fmt, va_list args) | ||
| 60 | { | ||
| 61 | int i; | ||
| 62 | ssize_t ssize = size; | ||
| 63 | |||
| 64 | i = vsnprintf(buf, size, fmt, args); | ||
| 65 | |||
| 66 | return (i >= ssize) ? (ssize - 1) : i; | ||
| 67 | } | ||
| 68 | |||
| 69 | static inline int scnprintf(char * buf, size_t size, const char * fmt, ...) | ||
| 70 | { | ||
| 71 | va_list args; | ||
| 72 | ssize_t ssize = size; | ||
| 73 | int i; | ||
| 74 | |||
| 75 | va_start(args, fmt); | ||
| 76 | i = vsnprintf(buf, size, fmt, args); | ||
| 77 | va_end(args); | ||
| 78 | |||
| 79 | return (i >= ssize) ? (ssize - 1) : i; | ||
| 80 | } | ||
| 81 | |||
| 82 | static inline unsigned long | ||
| 83 | simple_strtoul(const char *nptr, char **endptr, int base) | ||
| 84 | { | ||
| 85 | return strtoul(nptr, endptr, base); | ||
| 86 | } | ||
| 87 | |||
| 88 | #ifndef pr_fmt | ||
| 89 | #define pr_fmt(fmt) fmt | ||
| 90 | #endif | ||
| 91 | |||
| 92 | #define pr_err(fmt, ...) \ | ||
| 93 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | ||
| 94 | #define pr_warning(fmt, ...) \ | ||
| 95 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | ||
| 96 | #define pr_info(fmt, ...) \ | ||
| 97 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | ||
| 98 | #define pr_debug(fmt, ...) \ | ||
| 99 | eprintf(1, pr_fmt(fmt), ##__VA_ARGS__) | ||
| 100 | #define pr_debugN(n, fmt, ...) \ | ||
| 101 | eprintf(n, pr_fmt(fmt), ##__VA_ARGS__) | ||
| 102 | #define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__) | ||
| 103 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) | ||
| 104 | |||
| 29 | #endif | 105 | #endif |
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h new file mode 100644 index 000000000000..3b2f5900276f --- /dev/null +++ b/tools/perf/util/include/linux/string.h | |||
| @@ -0,0 +1 @@ | |||
| #include <string.h> | |||
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h new file mode 100644 index 000000000000..196862a81a21 --- /dev/null +++ b/tools/perf/util/include/linux/types.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #ifndef _PERF_LINUX_TYPES_H_ | ||
| 2 | #define _PERF_LINUX_TYPES_H_ | ||
| 3 | |||
| 4 | #include <asm/types.h> | ||
| 5 | |||
| 6 | #define DECLARE_BITMAP(name,bits) \ | ||
| 7 | unsigned long name[BITS_TO_LONGS(bits)] | ||
| 8 | |||
| 9 | #endif | ||
diff --git a/tools/perf/util/levenshtein.h b/tools/perf/util/levenshtein.h index 0173abeef52c..b0fcb6d8a881 100644 --- a/tools/perf/util/levenshtein.h +++ b/tools/perf/util/levenshtein.h | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | #ifndef LEVENSHTEIN_H | 1 | #ifndef __PERF_LEVENSHTEIN_H |
| 2 | #define LEVENSHTEIN_H | 2 | #define __PERF_LEVENSHTEIN_H |
| 3 | 3 | ||
| 4 | int levenshtein(const char *string1, const char *string2, | 4 | int levenshtein(const char *string1, const char *string2, |
| 5 | int swap_penalty, int substition_penalty, | 5 | int swap_penalty, int substition_penalty, |
| 6 | int insertion_penalty, int deletion_penalty); | 6 | int insertion_penalty, int deletion_penalty); |
| 7 | 7 | ||
| 8 | #endif | 8 | #endif /* __PERF_LEVENSHTEIN_H */ |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 804e02382739..c4d55a0da2ea 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
| 4 | #include <string.h> | 4 | #include <string.h> |
| 5 | #include <stdio.h> | 5 | #include <stdio.h> |
| 6 | #include "debug.h" | ||
| 6 | 7 | ||
| 7 | static inline int is_anon_memory(const char *filename) | 8 | static inline int is_anon_memory(const char *filename) |
| 8 | { | 9 | { |
| @@ -19,13 +20,28 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen) | |||
| 19 | return n; | 20 | return n; |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen) | 23 | void map__init(struct map *self, enum map_type type, |
| 24 | u64 start, u64 end, u64 pgoff, struct dso *dso) | ||
| 25 | { | ||
| 26 | self->type = type; | ||
| 27 | self->start = start; | ||
| 28 | self->end = end; | ||
| 29 | self->pgoff = pgoff; | ||
| 30 | self->dso = dso; | ||
| 31 | self->map_ip = map__map_ip; | ||
| 32 | self->unmap_ip = map__unmap_ip; | ||
| 33 | RB_CLEAR_NODE(&self->rb_node); | ||
| 34 | } | ||
| 35 | |||
| 36 | struct map *map__new(struct mmap_event *event, enum map_type type, | ||
| 37 | char *cwd, int cwdlen) | ||
| 23 | { | 38 | { |
| 24 | struct map *self = malloc(sizeof(*self)); | 39 | struct map *self = malloc(sizeof(*self)); |
| 25 | 40 | ||
| 26 | if (self != NULL) { | 41 | if (self != NULL) { |
| 27 | const char *filename = event->filename; | 42 | const char *filename = event->filename; |
| 28 | char newfilename[PATH_MAX]; | 43 | char newfilename[PATH_MAX]; |
| 44 | struct dso *dso; | ||
| 29 | int anon; | 45 | int anon; |
| 30 | 46 | ||
| 31 | if (cwd) { | 47 | if (cwd) { |
| @@ -45,18 +61,15 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen) | |||
| 45 | filename = newfilename; | 61 | filename = newfilename; |
| 46 | } | 62 | } |
| 47 | 63 | ||
| 48 | self->start = event->start; | 64 | dso = dsos__findnew(filename); |
| 49 | self->end = event->start + event->len; | 65 | if (dso == NULL) |
| 50 | self->pgoff = event->pgoff; | ||
| 51 | |||
| 52 | self->dso = dsos__findnew(filename); | ||
| 53 | if (self->dso == NULL) | ||
| 54 | goto out_delete; | 66 | goto out_delete; |
| 55 | 67 | ||
| 68 | map__init(self, type, event->start, event->start + event->len, | ||
| 69 | event->pgoff, dso); | ||
| 70 | |||
| 56 | if (self->dso == vdso || anon) | 71 | if (self->dso == vdso || anon) |
| 57 | self->map_ip = vdso__map_ip; | 72 | self->map_ip = self->unmap_ip = identity__map_ip; |
| 58 | else | ||
| 59 | self->map_ip = map__map_ip; | ||
| 60 | } | 73 | } |
| 61 | return self; | 74 | return self; |
| 62 | out_delete: | 75 | out_delete: |
| @@ -64,6 +77,99 @@ out_delete: | |||
| 64 | return NULL; | 77 | return NULL; |
| 65 | } | 78 | } |
| 66 | 79 | ||
| 80 | void map__delete(struct map *self) | ||
| 81 | { | ||
| 82 | free(self); | ||
| 83 | } | ||
| 84 | |||
| 85 | void map__fixup_start(struct map *self) | ||
| 86 | { | ||
| 87 | struct rb_root *symbols = &self->dso->symbols[self->type]; | ||
| 88 | struct rb_node *nd = rb_first(symbols); | ||
| 89 | if (nd != NULL) { | ||
| 90 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | ||
| 91 | self->start = sym->start; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | void map__fixup_end(struct map *self) | ||
| 96 | { | ||
| 97 | struct rb_root *symbols = &self->dso->symbols[self->type]; | ||
| 98 | struct rb_node *nd = rb_last(symbols); | ||
| 99 | if (nd != NULL) { | ||
| 100 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | ||
| 101 | self->end = sym->end; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | #define DSO__DELETED "(deleted)" | ||
| 106 | |||
| 107 | int map__load(struct map *self, struct perf_session *session, | ||
| 108 | symbol_filter_t filter) | ||
| 109 | { | ||
| 110 | const char *name = self->dso->long_name; | ||
| 111 | int nr; | ||
| 112 | |||
| 113 | if (dso__loaded(self->dso, self->type)) | ||
| 114 | return 0; | ||
| 115 | |||
| 116 | nr = dso__load(self->dso, self, session, filter); | ||
| 117 | if (nr < 0) { | ||
| 118 | if (self->dso->has_build_id) { | ||
| 119 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
| 120 | |||
| 121 | build_id__sprintf(self->dso->build_id, | ||
| 122 | sizeof(self->dso->build_id), | ||
| 123 | sbuild_id); | ||
| 124 | pr_warning("%s with build id %s not found", | ||
| 125 | name, sbuild_id); | ||
| 126 | } else | ||
| 127 | pr_warning("Failed to open %s", name); | ||
| 128 | |||
| 129 | pr_warning(", continuing without symbols\n"); | ||
| 130 | return -1; | ||
| 131 | } else if (nr == 0) { | ||
| 132 | const size_t len = strlen(name); | ||
| 133 | const size_t real_len = len - sizeof(DSO__DELETED); | ||
| 134 | |||
| 135 | if (len > sizeof(DSO__DELETED) && | ||
| 136 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | ||
| 137 | pr_warning("%.*s was updated, restart the long " | ||
| 138 | "running apps that use it!\n", | ||
| 139 | (int)real_len, name); | ||
| 140 | } else { | ||
| 141 | pr_warning("no symbols found in %s, maybe install " | ||
| 142 | "a debug package?\n", name); | ||
| 143 | } | ||
| 144 | |||
| 145 | return -1; | ||
| 146 | } | ||
| 147 | |||
| 148 | return 0; | ||
| 149 | } | ||
| 150 | |||
| 151 | struct symbol *map__find_symbol(struct map *self, struct perf_session *session, | ||
| 152 | u64 addr, symbol_filter_t filter) | ||
| 153 | { | ||
| 154 | if (map__load(self, session, filter) < 0) | ||
| 155 | return NULL; | ||
| 156 | |||
| 157 | return dso__find_symbol(self->dso, self->type, addr); | ||
| 158 | } | ||
| 159 | |||
| 160 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
| 161 | struct perf_session *session, | ||
| 162 | symbol_filter_t filter) | ||
| 163 | { | ||
| 164 | if (map__load(self, session, filter) < 0) | ||
| 165 | return NULL; | ||
| 166 | |||
| 167 | if (!dso__sorted_by_name(self->dso, self->type)) | ||
| 168 | dso__sort_by_name(self->dso, self->type); | ||
| 169 | |||
| 170 | return dso__find_symbol_by_name(self->dso, self->type, name); | ||
| 171 | } | ||
| 172 | |||
| 67 | struct map *map__clone(struct map *self) | 173 | struct map *map__clone(struct map *self) |
| 68 | { | 174 | { |
| 69 | struct map *map = malloc(sizeof(*self)); | 175 | struct map *map = malloc(sizeof(*self)); |
diff --git a/tools/perf/util/module.c b/tools/perf/util/module.c deleted file mode 100644 index 0d8c85defcd2..000000000000 --- a/tools/perf/util/module.c +++ /dev/null | |||
| @@ -1,545 +0,0 @@ | |||
| 1 | #include "util.h" | ||
| 2 | #include "../perf.h" | ||
| 3 | #include "string.h" | ||
| 4 | #include "module.h" | ||
| 5 | |||
| 6 | #include <libelf.h> | ||
| 7 | #include <libgen.h> | ||
| 8 | #include <gelf.h> | ||
| 9 | #include <elf.h> | ||
| 10 | #include <dirent.h> | ||
| 11 | #include <sys/utsname.h> | ||
| 12 | |||
| 13 | static unsigned int crc32(const char *p, unsigned int len) | ||
| 14 | { | ||
| 15 | int i; | ||
| 16 | unsigned int crc = 0; | ||
| 17 | |||
| 18 | while (len--) { | ||
| 19 | crc ^= *p++; | ||
| 20 | for (i = 0; i < 8; i++) | ||
| 21 | crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0); | ||
| 22 | } | ||
| 23 | return crc; | ||
| 24 | } | ||
| 25 | |||
| 26 | /* module section methods */ | ||
| 27 | |||
| 28 | struct sec_dso *sec_dso__new_dso(const char *name) | ||
| 29 | { | ||
| 30 | struct sec_dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
| 31 | |||
| 32 | if (self != NULL) { | ||
| 33 | strcpy(self->name, name); | ||
| 34 | self->secs = RB_ROOT; | ||
| 35 | self->find_section = sec_dso__find_section; | ||
| 36 | } | ||
| 37 | |||
| 38 | return self; | ||
| 39 | } | ||
| 40 | |||
| 41 | static void sec_dso__delete_section(struct section *self) | ||
| 42 | { | ||
| 43 | free(((void *)self)); | ||
| 44 | } | ||
| 45 | |||
| 46 | void sec_dso__delete_sections(struct sec_dso *self) | ||
| 47 | { | ||
| 48 | struct section *pos; | ||
| 49 | struct rb_node *next = rb_first(&self->secs); | ||
| 50 | |||
| 51 | while (next) { | ||
| 52 | pos = rb_entry(next, struct section, rb_node); | ||
| 53 | next = rb_next(&pos->rb_node); | ||
| 54 | rb_erase(&pos->rb_node, &self->secs); | ||
| 55 | sec_dso__delete_section(pos); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | void sec_dso__delete_self(struct sec_dso *self) | ||
| 60 | { | ||
| 61 | sec_dso__delete_sections(self); | ||
| 62 | free(self); | ||
| 63 | } | ||
| 64 | |||
| 65 | static void sec_dso__insert_section(struct sec_dso *self, struct section *sec) | ||
| 66 | { | ||
| 67 | struct rb_node **p = &self->secs.rb_node; | ||
| 68 | struct rb_node *parent = NULL; | ||
| 69 | const u64 hash = sec->hash; | ||
| 70 | struct section *s; | ||
| 71 | |||
| 72 | while (*p != NULL) { | ||
| 73 | parent = *p; | ||
| 74 | s = rb_entry(parent, struct section, rb_node); | ||
| 75 | if (hash < s->hash) | ||
| 76 | p = &(*p)->rb_left; | ||
| 77 | else | ||
| 78 | p = &(*p)->rb_right; | ||
| 79 | } | ||
| 80 | rb_link_node(&sec->rb_node, parent, p); | ||
| 81 | rb_insert_color(&sec->rb_node, &self->secs); | ||
| 82 | } | ||
| 83 | |||
| 84 | struct section *sec_dso__find_section(struct sec_dso *self, const char *name) | ||
| 85 | { | ||
| 86 | struct rb_node *n; | ||
| 87 | u64 hash; | ||
| 88 | int len; | ||
| 89 | |||
| 90 | if (self == NULL) | ||
| 91 | return NULL; | ||
| 92 | |||
| 93 | len = strlen(name); | ||
| 94 | hash = crc32(name, len); | ||
| 95 | |||
| 96 | n = self->secs.rb_node; | ||
| 97 | |||
| 98 | while (n) { | ||
| 99 | struct section *s = rb_entry(n, struct section, rb_node); | ||
| 100 | |||
| 101 | if (hash < s->hash) | ||
| 102 | n = n->rb_left; | ||
| 103 | else if (hash > s->hash) | ||
| 104 | n = n->rb_right; | ||
| 105 | else { | ||
| 106 | if (!strcmp(name, s->name)) | ||
| 107 | return s; | ||
| 108 | else | ||
| 109 | n = rb_next(&s->rb_node); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | return NULL; | ||
| 114 | } | ||
| 115 | |||
| 116 | static size_t sec_dso__fprintf_section(struct section *self, FILE *fp) | ||
| 117 | { | ||
| 118 | return fprintf(fp, "name:%s vma:%llx path:%s\n", | ||
| 119 | self->name, self->vma, self->path); | ||
| 120 | } | ||
| 121 | |||
| 122 | size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp) | ||
| 123 | { | ||
| 124 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | ||
| 125 | |||
| 126 | struct rb_node *nd; | ||
| 127 | for (nd = rb_first(&self->secs); nd; nd = rb_next(nd)) { | ||
| 128 | struct section *pos = rb_entry(nd, struct section, rb_node); | ||
| 129 | ret += sec_dso__fprintf_section(pos, fp); | ||
| 130 | } | ||
| 131 | |||
| 132 | return ret; | ||
| 133 | } | ||
| 134 | |||
| 135 | static struct section *section__new(const char *name, const char *path) | ||
| 136 | { | ||
| 137 | struct section *self = calloc(1, sizeof(*self)); | ||
| 138 | |||
| 139 | if (!self) | ||
| 140 | goto out_failure; | ||
| 141 | |||
| 142 | self->name = calloc(1, strlen(name) + 1); | ||
| 143 | if (!self->name) | ||
| 144 | goto out_failure; | ||
| 145 | |||
| 146 | self->path = calloc(1, strlen(path) + 1); | ||
| 147 | if (!self->path) | ||
| 148 | goto out_failure; | ||
| 149 | |||
| 150 | strcpy(self->name, name); | ||
| 151 | strcpy(self->path, path); | ||
| 152 | self->hash = crc32(self->name, strlen(name)); | ||
| 153 | |||
| 154 | return self; | ||
| 155 | |||
| 156 | out_failure: | ||
| 157 | if (self) { | ||
| 158 | if (self->name) | ||
| 159 | free(self->name); | ||
| 160 | if (self->path) | ||
| 161 | free(self->path); | ||
| 162 | free(self); | ||
| 163 | } | ||
| 164 | |||
| 165 | return NULL; | ||
| 166 | } | ||
| 167 | |||
| 168 | /* module methods */ | ||
| 169 | |||
| 170 | struct mod_dso *mod_dso__new_dso(const char *name) | ||
| 171 | { | ||
| 172 | struct mod_dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
| 173 | |||
| 174 | if (self != NULL) { | ||
| 175 | strcpy(self->name, name); | ||
| 176 | self->mods = RB_ROOT; | ||
| 177 | self->find_module = mod_dso__find_module; | ||
| 178 | } | ||
| 179 | |||
| 180 | return self; | ||
| 181 | } | ||
| 182 | |||
| 183 | static void mod_dso__delete_module(struct module *self) | ||
| 184 | { | ||
| 185 | free(((void *)self)); | ||
| 186 | } | ||
| 187 | |||
| 188 | void mod_dso__delete_modules(struct mod_dso *self) | ||
| 189 | { | ||
| 190 | struct module *pos; | ||
| 191 | struct rb_node *next = rb_first(&self->mods); | ||
| 192 | |||
| 193 | while (next) { | ||
| 194 | pos = rb_entry(next, struct module, rb_node); | ||
| 195 | next = rb_next(&pos->rb_node); | ||
| 196 | rb_erase(&pos->rb_node, &self->mods); | ||
| 197 | mod_dso__delete_module(pos); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | void mod_dso__delete_self(struct mod_dso *self) | ||
| 202 | { | ||
| 203 | mod_dso__delete_modules(self); | ||
| 204 | free(self); | ||
| 205 | } | ||
| 206 | |||
| 207 | static void mod_dso__insert_module(struct mod_dso *self, struct module *mod) | ||
| 208 | { | ||
| 209 | struct rb_node **p = &self->mods.rb_node; | ||
| 210 | struct rb_node *parent = NULL; | ||
| 211 | const u64 hash = mod->hash; | ||
| 212 | struct module *m; | ||
| 213 | |||
| 214 | while (*p != NULL) { | ||
| 215 | parent = *p; | ||
| 216 | m = rb_entry(parent, struct module, rb_node); | ||
| 217 | if (hash < m->hash) | ||
| 218 | p = &(*p)->rb_left; | ||
| 219 | else | ||
| 220 | p = &(*p)->rb_right; | ||
| 221 | } | ||
| 222 | rb_link_node(&mod->rb_node, parent, p); | ||
| 223 | rb_insert_color(&mod->rb_node, &self->mods); | ||
| 224 | } | ||
| 225 | |||
| 226 | struct module *mod_dso__find_module(struct mod_dso *self, const char *name) | ||
| 227 | { | ||
| 228 | struct rb_node *n; | ||
| 229 | u64 hash; | ||
| 230 | int len; | ||
| 231 | |||
| 232 | if (self == NULL) | ||
| 233 | return NULL; | ||
| 234 | |||
| 235 | len = strlen(name); | ||
| 236 | hash = crc32(name, len); | ||
| 237 | |||
| 238 | n = self->mods.rb_node; | ||
| 239 | |||
| 240 | while (n) { | ||
| 241 | struct module *m = rb_entry(n, struct module, rb_node); | ||
| 242 | |||
| 243 | if (hash < m->hash) | ||
| 244 | n = n->rb_left; | ||
| 245 | else if (hash > m->hash) | ||
| 246 | n = n->rb_right; | ||
| 247 | else { | ||
| 248 | if (!strcmp(name, m->name)) | ||
| 249 | return m; | ||
| 250 | else | ||
| 251 | n = rb_next(&m->rb_node); | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | return NULL; | ||
| 256 | } | ||
| 257 | |||
| 258 | static size_t mod_dso__fprintf_module(struct module *self, FILE *fp) | ||
| 259 | { | ||
| 260 | return fprintf(fp, "name:%s path:%s\n", self->name, self->path); | ||
| 261 | } | ||
| 262 | |||
| 263 | size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp) | ||
| 264 | { | ||
| 265 | struct rb_node *nd; | ||
| 266 | size_t ret; | ||
| 267 | |||
| 268 | ret = fprintf(fp, "dso: %s\n", self->name); | ||
| 269 | |||
| 270 | for (nd = rb_first(&self->mods); nd; nd = rb_next(nd)) { | ||
| 271 | struct module *pos = rb_entry(nd, struct module, rb_node); | ||
| 272 | |||
| 273 | ret += mod_dso__fprintf_module(pos, fp); | ||
| 274 | } | ||
| 275 | |||
| 276 | return ret; | ||
| 277 | } | ||
| 278 | |||
| 279 | static struct module *module__new(const char *name, const char *path) | ||
| 280 | { | ||
| 281 | struct module *self = calloc(1, sizeof(*self)); | ||
| 282 | |||
| 283 | if (!self) | ||
| 284 | goto out_failure; | ||
| 285 | |||
| 286 | self->name = calloc(1, strlen(name) + 1); | ||
| 287 | if (!self->name) | ||
| 288 | goto out_failure; | ||
| 289 | |||
| 290 | self->path = calloc(1, strlen(path) + 1); | ||
| 291 | if (!self->path) | ||
| 292 | goto out_failure; | ||
| 293 | |||
| 294 | strcpy(self->name, name); | ||
| 295 | strcpy(self->path, path); | ||
| 296 | self->hash = crc32(self->name, strlen(name)); | ||
| 297 | |||
| 298 | return self; | ||
| 299 | |||
| 300 | out_failure: | ||
| 301 | if (self) { | ||
| 302 | if (self->name) | ||
| 303 | free(self->name); | ||
| 304 | if (self->path) | ||
| 305 | free(self->path); | ||
| 306 | free(self); | ||
| 307 | } | ||
| 308 | |||
| 309 | return NULL; | ||
| 310 | } | ||
| 311 | |||
| 312 | static int mod_dso__load_sections(struct module *mod) | ||
| 313 | { | ||
| 314 | int count = 0, path_len; | ||
| 315 | struct dirent *entry; | ||
| 316 | char *line = NULL; | ||
| 317 | char *dir_path; | ||
| 318 | DIR *dir; | ||
| 319 | size_t n; | ||
| 320 | |||
| 321 | path_len = strlen("/sys/module/"); | ||
| 322 | path_len += strlen(mod->name); | ||
| 323 | path_len += strlen("/sections/"); | ||
| 324 | |||
| 325 | dir_path = calloc(1, path_len + 1); | ||
| 326 | if (dir_path == NULL) | ||
| 327 | goto out_failure; | ||
| 328 | |||
| 329 | strcat(dir_path, "/sys/module/"); | ||
| 330 | strcat(dir_path, mod->name); | ||
| 331 | strcat(dir_path, "/sections/"); | ||
| 332 | |||
| 333 | dir = opendir(dir_path); | ||
| 334 | if (dir == NULL) | ||
| 335 | goto out_free; | ||
| 336 | |||
| 337 | while ((entry = readdir(dir))) { | ||
| 338 | struct section *section; | ||
| 339 | char *path, *vma; | ||
| 340 | int line_len; | ||
| 341 | FILE *file; | ||
| 342 | |||
| 343 | if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) | ||
| 344 | continue; | ||
| 345 | |||
| 346 | path = calloc(1, path_len + strlen(entry->d_name) + 1); | ||
| 347 | if (path == NULL) | ||
| 348 | break; | ||
| 349 | strcat(path, dir_path); | ||
| 350 | strcat(path, entry->d_name); | ||
| 351 | |||
| 352 | file = fopen(path, "r"); | ||
| 353 | if (file == NULL) { | ||
| 354 | free(path); | ||
| 355 | break; | ||
| 356 | } | ||
| 357 | |||
| 358 | line_len = getline(&line, &n, file); | ||
| 359 | if (line_len < 0) { | ||
| 360 | free(path); | ||
| 361 | fclose(file); | ||
| 362 | break; | ||
| 363 | } | ||
| 364 | |||
| 365 | if (!line) { | ||
| 366 | free(path); | ||
| 367 | fclose(file); | ||
| 368 | break; | ||
| 369 | } | ||
| 370 | |||
| 371 | line[--line_len] = '\0'; /* \n */ | ||
| 372 | |||
| 373 | vma = strstr(line, "0x"); | ||
| 374 | if (!vma) { | ||
| 375 | free(path); | ||
| 376 | fclose(file); | ||
| 377 | break; | ||
| 378 | } | ||
| 379 | vma += 2; | ||
| 380 | |||
| 381 | section = section__new(entry->d_name, path); | ||
| 382 | if (!section) { | ||
| 383 | fprintf(stderr, "load_sections: allocation error\n"); | ||
| 384 | free(path); | ||
| 385 | fclose(file); | ||
| 386 | break; | ||
| 387 | } | ||
| 388 | |||
| 389 | hex2u64(vma, §ion->vma); | ||
| 390 | sec_dso__insert_section(mod->sections, section); | ||
| 391 | |||
| 392 | free(path); | ||
| 393 | fclose(file); | ||
| 394 | count++; | ||
| 395 | } | ||
| 396 | |||
| 397 | closedir(dir); | ||
| 398 | free(line); | ||
| 399 | free(dir_path); | ||
| 400 | |||
| 401 | return count; | ||
| 402 | |||
| 403 | out_free: | ||
| 404 | free(dir_path); | ||
| 405 | |||
| 406 | out_failure: | ||
| 407 | return count; | ||
| 408 | } | ||
| 409 | |||
| 410 | static int mod_dso__load_module_paths(struct mod_dso *self) | ||
| 411 | { | ||
| 412 | struct utsname uts; | ||
| 413 | int count = 0, len, err = -1; | ||
| 414 | char *line = NULL; | ||
| 415 | FILE *file; | ||
| 416 | char *dpath, *dir; | ||
| 417 | size_t n; | ||
| 418 | |||
| 419 | if (uname(&uts) < 0) | ||
| 420 | return err; | ||
| 421 | |||
| 422 | len = strlen("/lib/modules/"); | ||
| 423 | len += strlen(uts.release); | ||
| 424 | len += strlen("/modules.dep"); | ||
| 425 | |||
| 426 | dpath = calloc(1, len + 1); | ||
| 427 | if (dpath == NULL) | ||
| 428 | return err; | ||
| 429 | |||
| 430 | strcat(dpath, "/lib/modules/"); | ||
| 431 | strcat(dpath, uts.release); | ||
| 432 | strcat(dpath, "/modules.dep"); | ||
| 433 | |||
| 434 | file = fopen(dpath, "r"); | ||
| 435 | if (file == NULL) | ||
| 436 | goto out_failure; | ||
| 437 | |||
| 438 | dir = dirname(dpath); | ||
| 439 | if (!dir) | ||
| 440 | goto out_failure; | ||
| 441 | strcat(dir, "/"); | ||
| 442 | |||
| 443 | while (!feof(file)) { | ||
| 444 | struct module *module; | ||
| 445 | char *name, *path, *tmp; | ||
| 446 | FILE *modfile; | ||
| 447 | int line_len; | ||
| 448 | |||
| 449 | line_len = getline(&line, &n, file); | ||
| 450 | if (line_len < 0) | ||
| 451 | break; | ||
| 452 | |||
| 453 | if (!line) | ||
| 454 | break; | ||
| 455 | |||
| 456 | line[--line_len] = '\0'; /* \n */ | ||
| 457 | |||
| 458 | path = strchr(line, ':'); | ||
| 459 | if (!path) | ||
| 460 | break; | ||
| 461 | *path = '\0'; | ||
| 462 | |||
| 463 | path = strdup(line); | ||
| 464 | if (!path) | ||
| 465 | break; | ||
| 466 | |||
| 467 | if (!strstr(path, dir)) { | ||
| 468 | if (strncmp(path, "kernel/", 7)) | ||
| 469 | break; | ||
| 470 | |||
| 471 | free(path); | ||
| 472 | path = calloc(1, strlen(dir) + strlen(line) + 1); | ||
| 473 | if (!path) | ||
| 474 | break; | ||
| 475 | strcat(path, dir); | ||
| 476 | strcat(path, line); | ||
| 477 | } | ||
| 478 | |||
| 479 | modfile = fopen(path, "r"); | ||
| 480 | if (modfile == NULL) | ||
| 481 | break; | ||
| 482 | fclose(modfile); | ||
| 483 | |||
| 484 | name = strdup(path); | ||
| 485 | if (!name) | ||
| 486 | break; | ||
| 487 | |||
| 488 | name = strtok(name, "/"); | ||
| 489 | tmp = name; | ||
| 490 | |||
| 491 | while (tmp) { | ||
| 492 | tmp = strtok(NULL, "/"); | ||
| 493 | if (tmp) | ||
| 494 | name = tmp; | ||
| 495 | } | ||
| 496 | |||
| 497 | name = strsep(&name, "."); | ||
| 498 | if (!name) | ||
| 499 | break; | ||
| 500 | |||
| 501 | /* Quirk: replace '-' with '_' in all modules */ | ||
| 502 | for (len = strlen(name); len; len--) { | ||
| 503 | if (*(name+len) == '-') | ||
| 504 | *(name+len) = '_'; | ||
| 505 | } | ||
| 506 | |||
| 507 | module = module__new(name, path); | ||
| 508 | if (!module) | ||
| 509 | break; | ||
| 510 | mod_dso__insert_module(self, module); | ||
| 511 | |||
| 512 | module->sections = sec_dso__new_dso("sections"); | ||
| 513 | if (!module->sections) | ||
| 514 | break; | ||
| 515 | |||
| 516 | module->active = mod_dso__load_sections(module); | ||
| 517 | |||
| 518 | if (module->active > 0) | ||
| 519 | count++; | ||
| 520 | } | ||
| 521 | |||
| 522 | if (feof(file)) | ||
| 523 | err = count; | ||
| 524 | else | ||
| 525 | fprintf(stderr, "load_module_paths: modules.dep parsing failure!\n"); | ||
| 526 | |||
| 527 | out_failure: | ||
| 528 | if (dpath) | ||
| 529 | free(dpath); | ||
| 530 | if (file) | ||
| 531 | fclose(file); | ||
| 532 | if (line) | ||
| 533 | free(line); | ||
| 534 | |||
| 535 | return err; | ||
| 536 | } | ||
| 537 | |||
| 538 | int mod_dso__load_modules(struct mod_dso *dso) | ||
| 539 | { | ||
| 540 | int err; | ||
| 541 | |||
| 542 | err = mod_dso__load_module_paths(dso); | ||
| 543 | |||
| 544 | return err; | ||
| 545 | } | ||
diff --git a/tools/perf/util/module.h b/tools/perf/util/module.h deleted file mode 100644 index 8a592ef641ca..000000000000 --- a/tools/perf/util/module.h +++ /dev/null | |||
| @@ -1,53 +0,0 @@ | |||
| 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/parse-events.c b/tools/perf/util/parse-events.c index 8cfb48cbbea0..e5bc0fb016b2 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | 1 | #include "../../../include/linux/hw_breakpoint.h" | |
| 2 | #include "util.h" | 2 | #include "util.h" |
| 3 | #include "../perf.h" | 3 | #include "../perf.h" |
| 4 | #include "parse-options.h" | 4 | #include "parse-options.h" |
| @@ -7,10 +7,12 @@ | |||
| 7 | #include "string.h" | 7 | #include "string.h" |
| 8 | #include "cache.h" | 8 | #include "cache.h" |
| 9 | #include "header.h" | 9 | #include "header.h" |
| 10 | #include "debugfs.h" | ||
| 10 | 11 | ||
| 11 | int nr_counters; | 12 | int nr_counters; |
| 12 | 13 | ||
| 13 | struct perf_event_attr attrs[MAX_COUNTERS]; | 14 | struct perf_event_attr attrs[MAX_COUNTERS]; |
| 15 | char *filters[MAX_COUNTERS]; | ||
| 14 | 16 | ||
| 15 | struct event_symbol { | 17 | struct event_symbol { |
| 16 | u8 type; | 18 | u8 type; |
| @@ -46,6 +48,8 @@ static struct event_symbol event_symbols[] = { | |||
| 46 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, | 48 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, |
| 47 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, | 49 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, |
| 48 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, | 50 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, |
| 51 | { CSW(ALIGNMENT_FAULTS), "alignment-faults", "" }, | ||
| 52 | { CSW(EMULATION_FAULTS), "emulation-faults", "" }, | ||
| 49 | }; | 53 | }; |
| 50 | 54 | ||
| 51 | #define __PERF_EVENT_FIELD(config, name) \ | 55 | #define __PERF_EVENT_FIELD(config, name) \ |
| @@ -74,6 +78,8 @@ static const char *sw_event_names[] = { | |||
| 74 | "CPU-migrations", | 78 | "CPU-migrations", |
| 75 | "minor-faults", | 79 | "minor-faults", |
| 76 | "major-faults", | 80 | "major-faults", |
| 81 | "alignment-faults", | ||
| 82 | "emulation-faults", | ||
| 77 | }; | 83 | }; |
| 78 | 84 | ||
| 79 | #define MAX_ALIASES 8 | 85 | #define MAX_ALIASES 8 |
| @@ -148,16 +154,6 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) | |||
| 148 | 154 | ||
| 149 | #define MAX_EVENT_LENGTH 512 | 155 | #define MAX_EVENT_LENGTH 512 |
| 150 | 156 | ||
| 151 | int valid_debugfs_mount(const char *debugfs) | ||
| 152 | { | ||
| 153 | struct statfs st_fs; | ||
| 154 | |||
| 155 | if (statfs(debugfs, &st_fs) < 0) | ||
| 156 | return -ENOENT; | ||
| 157 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
| 158 | return -ENOENT; | ||
| 159 | return 0; | ||
| 160 | } | ||
| 161 | 157 | ||
| 162 | struct tracepoint_path *tracepoint_id_to_path(u64 config) | 158 | struct tracepoint_path *tracepoint_id_to_path(u64 config) |
| 163 | { | 159 | { |
| @@ -170,7 +166,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
| 170 | char evt_path[MAXPATHLEN]; | 166 | char evt_path[MAXPATHLEN]; |
| 171 | char dir_path[MAXPATHLEN]; | 167 | char dir_path[MAXPATHLEN]; |
| 172 | 168 | ||
| 173 | if (valid_debugfs_mount(debugfs_path)) | 169 | if (debugfs_valid_mountpoint(debugfs_path)) |
| 174 | return NULL; | 170 | return NULL; |
| 175 | 171 | ||
| 176 | sys_dir = opendir(debugfs_path); | 172 | sys_dir = opendir(debugfs_path); |
| @@ -201,7 +197,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
| 201 | if (id == config) { | 197 | if (id == config) { |
| 202 | closedir(evt_dir); | 198 | closedir(evt_dir); |
| 203 | closedir(sys_dir); | 199 | closedir(sys_dir); |
| 204 | path = calloc(1, sizeof(path)); | 200 | path = zalloc(sizeof(*path)); |
| 205 | path->system = malloc(MAX_EVENT_LENGTH); | 201 | path->system = malloc(MAX_EVENT_LENGTH); |
| 206 | if (!path->system) { | 202 | if (!path->system) { |
| 207 | free(path); | 203 | free(path); |
| @@ -471,7 +467,6 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
| 471 | while ((evt_ent = readdir(evt_dir))) { | 467 | while ((evt_ent = readdir(evt_dir))) { |
| 472 | char event_opt[MAX_EVOPT_LEN + 1]; | 468 | char event_opt[MAX_EVOPT_LEN + 1]; |
| 473 | int len; | 469 | int len; |
| 474 | unsigned int rem = MAX_EVOPT_LEN; | ||
| 475 | 470 | ||
| 476 | if (!strcmp(evt_ent->d_name, ".") | 471 | if (!strcmp(evt_ent->d_name, ".") |
| 477 | || !strcmp(evt_ent->d_name, "..") | 472 | || !strcmp(evt_ent->d_name, "..") |
| @@ -479,20 +474,12 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
| 479 | || !strcmp(evt_ent->d_name, "filter")) | 474 | || !strcmp(evt_ent->d_name, "filter")) |
| 480 | continue; | 475 | continue; |
| 481 | 476 | ||
| 482 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name, | 477 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, |
| 483 | evt_ent->d_name); | 478 | evt_ent->d_name, flags ? ":" : "", |
| 479 | flags ?: ""); | ||
| 484 | if (len < 0) | 480 | if (len < 0) |
| 485 | return EVT_FAILED; | 481 | return EVT_FAILED; |
| 486 | 482 | ||
| 487 | rem -= len; | ||
| 488 | if (flags) { | ||
| 489 | if (rem < strlen(flags) + 1) | ||
| 490 | return EVT_FAILED; | ||
| 491 | |||
| 492 | strcat(event_opt, ":"); | ||
| 493 | strcat(event_opt, flags); | ||
| 494 | } | ||
| 495 | |||
| 496 | if (parse_events(NULL, event_opt, 0)) | 483 | if (parse_events(NULL, event_opt, 0)) |
| 497 | return EVT_FAILED; | 484 | return EVT_FAILED; |
| 498 | } | 485 | } |
| @@ -509,7 +496,7 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
| 509 | char sys_name[MAX_EVENT_LENGTH]; | 496 | char sys_name[MAX_EVENT_LENGTH]; |
| 510 | unsigned int sys_length, evt_length; | 497 | unsigned int sys_length, evt_length; |
| 511 | 498 | ||
| 512 | if (valid_debugfs_mount(debugfs_path)) | 499 | if (debugfs_valid_mountpoint(debugfs_path)) |
| 513 | return 0; | 500 | return 0; |
| 514 | 501 | ||
| 515 | evt_name = strchr(*strp, ':'); | 502 | evt_name = strchr(*strp, ':'); |
| @@ -544,6 +531,81 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
| 544 | attr, strp); | 531 | attr, strp); |
| 545 | } | 532 | } |
| 546 | 533 | ||
| 534 | static enum event_result | ||
| 535 | parse_breakpoint_type(const char *type, const char **strp, | ||
| 536 | struct perf_event_attr *attr) | ||
| 537 | { | ||
| 538 | int i; | ||
| 539 | |||
| 540 | for (i = 0; i < 3; i++) { | ||
| 541 | if (!type[i]) | ||
| 542 | break; | ||
| 543 | |||
| 544 | switch (type[i]) { | ||
| 545 | case 'r': | ||
| 546 | attr->bp_type |= HW_BREAKPOINT_R; | ||
| 547 | break; | ||
| 548 | case 'w': | ||
| 549 | attr->bp_type |= HW_BREAKPOINT_W; | ||
| 550 | break; | ||
| 551 | case 'x': | ||
| 552 | attr->bp_type |= HW_BREAKPOINT_X; | ||
| 553 | break; | ||
| 554 | default: | ||
| 555 | return EVT_FAILED; | ||
| 556 | } | ||
| 557 | } | ||
| 558 | if (!attr->bp_type) /* Default */ | ||
| 559 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
| 560 | |||
| 561 | *strp = type + i; | ||
| 562 | |||
| 563 | return EVT_HANDLED; | ||
| 564 | } | ||
| 565 | |||
| 566 | static enum event_result | ||
| 567 | parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) | ||
| 568 | { | ||
| 569 | const char *target; | ||
| 570 | const char *type; | ||
| 571 | char *endaddr; | ||
| 572 | u64 addr; | ||
| 573 | enum event_result err; | ||
| 574 | |||
| 575 | target = strchr(*strp, ':'); | ||
| 576 | if (!target) | ||
| 577 | return EVT_FAILED; | ||
| 578 | |||
| 579 | if (strncmp(*strp, "mem", target - *strp) != 0) | ||
| 580 | return EVT_FAILED; | ||
| 581 | |||
| 582 | target++; | ||
| 583 | |||
| 584 | addr = strtoull(target, &endaddr, 0); | ||
| 585 | if (target == endaddr) | ||
| 586 | return EVT_FAILED; | ||
| 587 | |||
| 588 | attr->bp_addr = addr; | ||
| 589 | *strp = endaddr; | ||
| 590 | |||
| 591 | type = strchr(target, ':'); | ||
| 592 | |||
| 593 | /* If no type is defined, just rw as default */ | ||
| 594 | if (!type) { | ||
| 595 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
| 596 | } else { | ||
| 597 | err = parse_breakpoint_type(++type, strp, attr); | ||
| 598 | if (err == EVT_FAILED) | ||
| 599 | return EVT_FAILED; | ||
| 600 | } | ||
| 601 | |||
| 602 | /* We should find a nice way to override the access type */ | ||
| 603 | attr->bp_len = HW_BREAKPOINT_LEN_4; | ||
| 604 | attr->type = PERF_TYPE_BREAKPOINT; | ||
| 605 | |||
| 606 | return EVT_HANDLED; | ||
| 607 | } | ||
| 608 | |||
| 547 | static int check_events(const char *str, unsigned int i) | 609 | static int check_events(const char *str, unsigned int i) |
| 548 | { | 610 | { |
| 549 | int n; | 611 | int n; |
| @@ -677,6 +739,12 @@ parse_event_symbols(const char **str, struct perf_event_attr *attr) | |||
| 677 | if (ret != EVT_FAILED) | 739 | if (ret != EVT_FAILED) |
| 678 | goto modifier; | 740 | goto modifier; |
| 679 | 741 | ||
| 742 | ret = parse_breakpoint_event(str, attr); | ||
| 743 | if (ret != EVT_FAILED) | ||
| 744 | goto modifier; | ||
| 745 | |||
| 746 | fprintf(stderr, "invalid or unsupported event: '%s'\n", *str); | ||
| 747 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
| 680 | return EVT_FAILED; | 748 | return EVT_FAILED; |
| 681 | 749 | ||
| 682 | modifier: | 750 | modifier: |
| @@ -708,7 +776,6 @@ static void store_event_type(const char *orgname) | |||
| 708 | perf_header__push_event(id, orgname); | 776 | perf_header__push_event(id, orgname); |
| 709 | } | 777 | } |
| 710 | 778 | ||
| 711 | |||
| 712 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 779 | int parse_events(const struct option *opt __used, const char *str, int unset __used) |
| 713 | { | 780 | { |
| 714 | struct perf_event_attr attr; | 781 | struct perf_event_attr attr; |
| @@ -745,6 +812,28 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
| 745 | return 0; | 812 | return 0; |
| 746 | } | 813 | } |
| 747 | 814 | ||
| 815 | int parse_filter(const struct option *opt __used, const char *str, | ||
| 816 | int unset __used) | ||
| 817 | { | ||
| 818 | int i = nr_counters - 1; | ||
| 819 | int len = strlen(str); | ||
| 820 | |||
| 821 | if (i < 0 || attrs[i].type != PERF_TYPE_TRACEPOINT) { | ||
| 822 | fprintf(stderr, | ||
| 823 | "-F option should follow a -e tracepoint option\n"); | ||
| 824 | return -1; | ||
| 825 | } | ||
| 826 | |||
| 827 | filters[i] = malloc(len + 1); | ||
| 828 | if (!filters[i]) { | ||
| 829 | fprintf(stderr, "not enough memory to hold filter string\n"); | ||
| 830 | return -1; | ||
| 831 | } | ||
| 832 | strcpy(filters[i], str); | ||
| 833 | |||
| 834 | return 0; | ||
| 835 | } | ||
| 836 | |||
| 748 | static const char * const event_type_descriptors[] = { | 837 | static const char * const event_type_descriptors[] = { |
| 749 | "", | 838 | "", |
| 750 | "Hardware event", | 839 | "Hardware event", |
| @@ -764,7 +853,7 @@ static void print_tracepoint_events(void) | |||
| 764 | char evt_path[MAXPATHLEN]; | 853 | char evt_path[MAXPATHLEN]; |
| 765 | char dir_path[MAXPATHLEN]; | 854 | char dir_path[MAXPATHLEN]; |
| 766 | 855 | ||
| 767 | if (valid_debugfs_mount(debugfs_path)) | 856 | if (debugfs_valid_mountpoint(debugfs_path)) |
| 768 | return; | 857 | return; |
| 769 | 858 | ||
| 770 | sys_dir = opendir(debugfs_path); | 859 | sys_dir = opendir(debugfs_path); |
| @@ -782,7 +871,7 @@ static void print_tracepoint_events(void) | |||
| 782 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | 871 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { |
| 783 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 872 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
| 784 | sys_dirent.d_name, evt_dirent.d_name); | 873 | sys_dirent.d_name, evt_dirent.d_name); |
| 785 | fprintf(stderr, " %-42s [%s]\n", evt_path, | 874 | printf(" %-42s [%s]\n", evt_path, |
| 786 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | 875 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); |
| 787 | } | 876 | } |
| 788 | closedir(evt_dir); | 877 | closedir(evt_dir); |
| @@ -799,8 +888,8 @@ void print_events(void) | |||
| 799 | unsigned int i, type, op, prev_type = -1; | 888 | unsigned int i, type, op, prev_type = -1; |
| 800 | char name[40]; | 889 | char name[40]; |
| 801 | 890 | ||
| 802 | fprintf(stderr, "\n"); | 891 | printf("\n"); |
| 803 | fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); | 892 | printf("List of pre-defined events (to be used in -e):\n"); |
| 804 | 893 | ||
| 805 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 894 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
| 806 | type = syms->type + 1; | 895 | type = syms->type + 1; |
| @@ -808,19 +897,19 @@ void print_events(void) | |||
| 808 | type = 0; | 897 | type = 0; |
| 809 | 898 | ||
| 810 | if (type != prev_type) | 899 | if (type != prev_type) |
| 811 | fprintf(stderr, "\n"); | 900 | printf("\n"); |
| 812 | 901 | ||
| 813 | if (strlen(syms->alias)) | 902 | if (strlen(syms->alias)) |
| 814 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | 903 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); |
| 815 | else | 904 | else |
| 816 | strcpy(name, syms->symbol); | 905 | strcpy(name, syms->symbol); |
| 817 | fprintf(stderr, " %-42s [%s]\n", name, | 906 | printf(" %-42s [%s]\n", name, |
| 818 | event_type_descriptors[type]); | 907 | event_type_descriptors[type]); |
| 819 | 908 | ||
| 820 | prev_type = type; | 909 | prev_type = type; |
| 821 | } | 910 | } |
| 822 | 911 | ||
| 823 | fprintf(stderr, "\n"); | 912 | printf("\n"); |
| 824 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | 913 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { |
| 825 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | 914 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { |
| 826 | /* skip invalid cache type */ | 915 | /* skip invalid cache type */ |
| @@ -828,17 +917,20 @@ void print_events(void) | |||
| 828 | continue; | 917 | continue; |
| 829 | 918 | ||
| 830 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | 919 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { |
| 831 | fprintf(stderr, " %-42s [%s]\n", | 920 | printf(" %-42s [%s]\n", |
| 832 | event_cache_name(type, op, i), | 921 | event_cache_name(type, op, i), |
| 833 | event_type_descriptors[4]); | 922 | event_type_descriptors[4]); |
| 834 | } | 923 | } |
| 835 | } | 924 | } |
| 836 | } | 925 | } |
| 837 | 926 | ||
| 838 | fprintf(stderr, "\n"); | 927 | printf("\n"); |
| 839 | fprintf(stderr, " %-42s [raw hardware event descriptor]\n", | 928 | printf(" %-42s [raw hardware event descriptor]\n", |
| 840 | "rNNN"); | 929 | "rNNN"); |
| 841 | fprintf(stderr, "\n"); | 930 | printf("\n"); |
| 931 | |||
| 932 | printf(" %-42s [hardware breakpoint]\n", "mem:<addr>[:access]"); | ||
| 933 | printf("\n"); | ||
| 842 | 934 | ||
| 843 | print_tracepoint_events(); | 935 | print_tracepoint_events(); |
| 844 | 936 | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 30c608112845..b8c1f64bc935 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef _PARSE_EVENTS_H | 1 | #ifndef __PERF_PARSE_EVENTS_H |
| 2 | #define _PARSE_EVENTS_H | 2 | #define __PERF_PARSE_EVENTS_H |
| 3 | /* | 3 | /* |
| 4 | * Parse symbolic events/counts passed in as options: | 4 | * Parse symbolic events/counts passed in as options: |
| 5 | */ | 5 | */ |
| @@ -17,11 +17,13 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config); | |||
| 17 | extern int nr_counters; | 17 | extern int nr_counters; |
| 18 | 18 | ||
| 19 | extern struct perf_event_attr attrs[MAX_COUNTERS]; | 19 | extern struct perf_event_attr attrs[MAX_COUNTERS]; |
| 20 | extern char *filters[MAX_COUNTERS]; | ||
| 20 | 21 | ||
| 21 | extern const char *event_name(int ctr); | 22 | extern const char *event_name(int ctr); |
| 22 | extern const char *__event_name(int type, u64 config); | 23 | extern const char *__event_name(int type, u64 config); |
| 23 | 24 | ||
| 24 | extern int parse_events(const struct option *opt, const char *str, int unset); | 25 | extern int parse_events(const struct option *opt, const char *str, int unset); |
| 26 | extern int parse_filter(const struct option *opt, const char *str, int unset); | ||
| 25 | 27 | ||
| 26 | #define EVENTS_HELP_MAX (128*1024) | 28 | #define EVENTS_HELP_MAX (128*1024) |
| 27 | 29 | ||
| @@ -31,4 +33,4 @@ extern char debugfs_path[]; | |||
| 31 | extern int valid_debugfs_mount(const char *debugfs); | 33 | extern int valid_debugfs_mount(const char *debugfs); |
| 32 | 34 | ||
| 33 | 35 | ||
| 34 | #endif /* _PARSE_EVENTS_H */ | 36 | #endif /* __PERF_PARSE_EVENTS_H */ |
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 6d8af48c925e..efebd5b476b3 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
| @@ -430,6 +430,9 @@ int usage_with_options_internal(const char * const *usagestr, | |||
| 430 | pos = fprintf(stderr, " "); | 430 | pos = fprintf(stderr, " "); |
| 431 | if (opts->short_name) | 431 | if (opts->short_name) |
| 432 | pos += fprintf(stderr, "-%c", opts->short_name); | 432 | pos += fprintf(stderr, "-%c", opts->short_name); |
| 433 | else | ||
| 434 | pos += fprintf(stderr, " "); | ||
| 435 | |||
| 433 | if (opts->long_name && opts->short_name) | 436 | if (opts->long_name && opts->short_name) |
| 434 | pos += fprintf(stderr, ", "); | 437 | pos += fprintf(stderr, ", "); |
| 435 | if (opts->long_name) | 438 | if (opts->long_name) |
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 2ee248ff27e5..948805af43c2 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef PARSE_OPTIONS_H | 1 | #ifndef __PERF_PARSE_OPTIONS_H |
| 2 | #define PARSE_OPTIONS_H | 2 | #define __PERF_PARSE_OPTIONS_H |
| 3 | 3 | ||
| 4 | enum parse_opt_type { | 4 | enum parse_opt_type { |
| 5 | /* special types */ | 5 | /* special types */ |
| @@ -174,4 +174,4 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int); | |||
| 174 | 174 | ||
| 175 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); | 175 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); |
| 176 | 176 | ||
| 177 | #endif | 177 | #endif /* __PERF_PARSE_OPTIONS_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c new file mode 100644 index 000000000000..29465d440043 --- /dev/null +++ b/tools/perf/util/probe-event.c | |||
| @@ -0,0 +1,677 @@ | |||
| 1 | /* | ||
| 2 | * probe-event.c : perf-probe definition to kprobe_events format converter | ||
| 3 | * | ||
| 4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #define _GNU_SOURCE | ||
| 23 | #include <sys/utsname.h> | ||
| 24 | #include <sys/types.h> | ||
| 25 | #include <sys/stat.h> | ||
| 26 | #include <fcntl.h> | ||
| 27 | #include <errno.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <unistd.h> | ||
| 30 | #include <stdlib.h> | ||
| 31 | #include <string.h> | ||
| 32 | #include <stdarg.h> | ||
| 33 | #include <limits.h> | ||
| 34 | |||
| 35 | #undef _GNU_SOURCE | ||
| 36 | #include "event.h" | ||
| 37 | #include "string.h" | ||
| 38 | #include "strlist.h" | ||
| 39 | #include "debug.h" | ||
| 40 | #include "parse-events.h" /* For debugfs_path */ | ||
| 41 | #include "probe-event.h" | ||
| 42 | |||
| 43 | #define MAX_CMDLEN 256 | ||
| 44 | #define MAX_PROBE_ARGS 128 | ||
| 45 | #define PERFPROBE_GROUP "probe" | ||
| 46 | |||
| 47 | #define semantic_error(msg ...) die("Semantic error :" msg) | ||
| 48 | |||
| 49 | /* If there is no space to write, returns -E2BIG. */ | ||
| 50 | static int e_snprintf(char *str, size_t size, const char *format, ...) | ||
| 51 | __attribute__((format(printf, 3, 4))); | ||
| 52 | |||
| 53 | static int e_snprintf(char *str, size_t size, const char *format, ...) | ||
| 54 | { | ||
| 55 | int ret; | ||
| 56 | va_list ap; | ||
| 57 | va_start(ap, format); | ||
| 58 | ret = vsnprintf(str, size, format, ap); | ||
| 59 | va_end(ap); | ||
| 60 | if (ret >= (int)size) | ||
| 61 | ret = -E2BIG; | ||
| 62 | return ret; | ||
| 63 | } | ||
| 64 | |||
| 65 | /* Check the name is good for event/group */ | ||
| 66 | static bool check_event_name(const char *name) | ||
| 67 | { | ||
| 68 | if (!isalpha(*name) && *name != '_') | ||
| 69 | return false; | ||
| 70 | while (*++name != '\0') { | ||
| 71 | if (!isalpha(*name) && !isdigit(*name) && *name != '_') | ||
| 72 | return false; | ||
| 73 | } | ||
| 74 | return true; | ||
| 75 | } | ||
| 76 | |||
| 77 | /* Parse probepoint definition. */ | ||
| 78 | static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | ||
| 79 | { | ||
| 80 | char *ptr, *tmp; | ||
| 81 | char c, nc = 0; | ||
| 82 | /* | ||
| 83 | * <Syntax> | ||
| 84 | * perf probe [EVENT=]SRC:LN | ||
| 85 | * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC] | ||
| 86 | * | ||
| 87 | * TODO:Group name support | ||
| 88 | */ | ||
| 89 | |||
| 90 | ptr = strchr(arg, '='); | ||
| 91 | if (ptr) { /* Event name */ | ||
| 92 | *ptr = '\0'; | ||
| 93 | tmp = ptr + 1; | ||
| 94 | ptr = strchr(arg, ':'); | ||
| 95 | if (ptr) /* Group name is not supported yet. */ | ||
| 96 | semantic_error("Group name is not supported yet."); | ||
| 97 | if (!check_event_name(arg)) | ||
| 98 | semantic_error("%s is bad for event name -it must " | ||
| 99 | "follow C symbol-naming rule.", arg); | ||
| 100 | pp->event = strdup(arg); | ||
| 101 | arg = tmp; | ||
| 102 | } | ||
| 103 | |||
| 104 | ptr = strpbrk(arg, ":+@%"); | ||
| 105 | if (ptr) { | ||
| 106 | nc = *ptr; | ||
| 107 | *ptr++ = '\0'; | ||
| 108 | } | ||
| 109 | |||
| 110 | /* Check arg is function or file and copy it */ | ||
| 111 | if (strchr(arg, '.')) /* File */ | ||
| 112 | pp->file = strdup(arg); | ||
| 113 | else /* Function */ | ||
| 114 | pp->function = strdup(arg); | ||
| 115 | DIE_IF(pp->file == NULL && pp->function == NULL); | ||
| 116 | |||
| 117 | /* Parse other options */ | ||
| 118 | while (ptr) { | ||
| 119 | arg = ptr; | ||
| 120 | c = nc; | ||
| 121 | ptr = strpbrk(arg, ":+@%"); | ||
| 122 | if (ptr) { | ||
| 123 | nc = *ptr; | ||
| 124 | *ptr++ = '\0'; | ||
| 125 | } | ||
| 126 | switch (c) { | ||
| 127 | case ':': /* Line number */ | ||
| 128 | pp->line = strtoul(arg, &tmp, 0); | ||
| 129 | if (*tmp != '\0') | ||
| 130 | semantic_error("There is non-digit charactor" | ||
| 131 | " in line number."); | ||
| 132 | break; | ||
| 133 | case '+': /* Byte offset from a symbol */ | ||
| 134 | pp->offset = strtoul(arg, &tmp, 0); | ||
| 135 | if (*tmp != '\0') | ||
| 136 | semantic_error("There is non-digit charactor" | ||
| 137 | " in offset."); | ||
| 138 | break; | ||
| 139 | case '@': /* File name */ | ||
| 140 | if (pp->file) | ||
| 141 | semantic_error("SRC@SRC is not allowed."); | ||
| 142 | pp->file = strdup(arg); | ||
| 143 | DIE_IF(pp->file == NULL); | ||
| 144 | if (ptr) | ||
| 145 | semantic_error("@SRC must be the last " | ||
| 146 | "option."); | ||
| 147 | break; | ||
| 148 | case '%': /* Probe places */ | ||
| 149 | if (strcmp(arg, "return") == 0) { | ||
| 150 | pp->retprobe = 1; | ||
| 151 | } else /* Others not supported yet */ | ||
| 152 | semantic_error("%%%s is not supported.", arg); | ||
| 153 | break; | ||
| 154 | default: | ||
| 155 | DIE_IF("Program has a bug."); | ||
| 156 | break; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Exclusion check */ | ||
| 161 | if (pp->line && pp->offset) | ||
| 162 | semantic_error("Offset can't be used with line number."); | ||
| 163 | |||
| 164 | if (!pp->line && pp->file && !pp->function) | ||
| 165 | semantic_error("File always requires line number."); | ||
| 166 | |||
| 167 | if (pp->offset && !pp->function) | ||
| 168 | semantic_error("Offset requires an entry function."); | ||
| 169 | |||
| 170 | if (pp->retprobe && !pp->function) | ||
| 171 | semantic_error("Return probe requires an entry function."); | ||
| 172 | |||
| 173 | if ((pp->offset || pp->line) && pp->retprobe) | ||
| 174 | semantic_error("Offset/Line can't be used with return probe."); | ||
| 175 | |||
| 176 | pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n", | ||
| 177 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe); | ||
| 178 | } | ||
| 179 | |||
| 180 | /* Parse perf-probe event definition */ | ||
| 181 | void parse_perf_probe_event(const char *str, struct probe_point *pp, | ||
| 182 | bool *need_dwarf) | ||
| 183 | { | ||
| 184 | char **argv; | ||
| 185 | int argc, i; | ||
| 186 | |||
| 187 | *need_dwarf = false; | ||
| 188 | |||
| 189 | argv = argv_split(str, &argc); | ||
| 190 | if (!argv) | ||
| 191 | die("argv_split failed."); | ||
| 192 | if (argc > MAX_PROBE_ARGS + 1) | ||
| 193 | semantic_error("Too many arguments"); | ||
| 194 | |||
| 195 | /* Parse probe point */ | ||
| 196 | parse_perf_probe_probepoint(argv[0], pp); | ||
| 197 | if (pp->file || pp->line) | ||
| 198 | *need_dwarf = true; | ||
| 199 | |||
| 200 | /* Copy arguments and ensure return probe has no C argument */ | ||
| 201 | pp->nr_args = argc - 1; | ||
| 202 | pp->args = zalloc(sizeof(char *) * pp->nr_args); | ||
| 203 | for (i = 0; i < pp->nr_args; i++) { | ||
| 204 | pp->args[i] = strdup(argv[i + 1]); | ||
| 205 | if (!pp->args[i]) | ||
| 206 | die("Failed to copy argument."); | ||
| 207 | if (is_c_varname(pp->args[i])) { | ||
| 208 | if (pp->retprobe) | ||
| 209 | semantic_error("You can't specify local" | ||
| 210 | " variable for kretprobe"); | ||
| 211 | *need_dwarf = true; | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | argv_free(argv); | ||
| 216 | } | ||
| 217 | |||
| 218 | /* Parse kprobe_events event into struct probe_point */ | ||
| 219 | void parse_trace_kprobe_event(const char *str, struct probe_point *pp) | ||
| 220 | { | ||
| 221 | char pr; | ||
| 222 | char *p; | ||
| 223 | int ret, i, argc; | ||
| 224 | char **argv; | ||
| 225 | |||
| 226 | pr_debug("Parsing kprobe_events: %s\n", str); | ||
| 227 | argv = argv_split(str, &argc); | ||
| 228 | if (!argv) | ||
| 229 | die("argv_split failed."); | ||
| 230 | if (argc < 2) | ||
| 231 | semantic_error("Too less arguments."); | ||
| 232 | |||
| 233 | /* Scan event and group name. */ | ||
| 234 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", | ||
| 235 | &pr, (float *)(void *)&pp->group, | ||
| 236 | (float *)(void *)&pp->event); | ||
| 237 | if (ret != 3) | ||
| 238 | semantic_error("Failed to parse event name: %s", argv[0]); | ||
| 239 | pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); | ||
| 240 | |||
| 241 | pp->retprobe = (pr == 'r'); | ||
| 242 | |||
| 243 | /* Scan function name and offset */ | ||
| 244 | ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, | ||
| 245 | &pp->offset); | ||
| 246 | if (ret == 1) | ||
| 247 | pp->offset = 0; | ||
| 248 | |||
| 249 | /* kprobe_events doesn't have this information */ | ||
| 250 | pp->line = 0; | ||
| 251 | pp->file = NULL; | ||
| 252 | |||
| 253 | pp->nr_args = argc - 2; | ||
| 254 | pp->args = zalloc(sizeof(char *) * pp->nr_args); | ||
| 255 | for (i = 0; i < pp->nr_args; i++) { | ||
| 256 | p = strchr(argv[i + 2], '='); | ||
| 257 | if (p) /* We don't need which register is assigned. */ | ||
| 258 | *p = '\0'; | ||
| 259 | pp->args[i] = strdup(argv[i + 2]); | ||
| 260 | if (!pp->args[i]) | ||
| 261 | die("Failed to copy argument."); | ||
| 262 | } | ||
| 263 | |||
| 264 | argv_free(argv); | ||
| 265 | } | ||
| 266 | |||
| 267 | /* Synthesize only probe point (not argument) */ | ||
| 268 | int synthesize_perf_probe_point(struct probe_point *pp) | ||
| 269 | { | ||
| 270 | char *buf; | ||
| 271 | char offs[64] = "", line[64] = ""; | ||
| 272 | int ret; | ||
| 273 | |||
| 274 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | ||
| 275 | if (!buf) | ||
| 276 | die("Failed to allocate memory by zalloc."); | ||
| 277 | if (pp->offset) { | ||
| 278 | ret = e_snprintf(offs, 64, "+%d", pp->offset); | ||
| 279 | if (ret <= 0) | ||
| 280 | goto error; | ||
| 281 | } | ||
| 282 | if (pp->line) { | ||
| 283 | ret = e_snprintf(line, 64, ":%d", pp->line); | ||
| 284 | if (ret <= 0) | ||
| 285 | goto error; | ||
| 286 | } | ||
| 287 | |||
| 288 | if (pp->function) | ||
| 289 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, | ||
| 290 | offs, pp->retprobe ? "%return" : "", line); | ||
| 291 | else | ||
| 292 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); | ||
| 293 | if (ret <= 0) { | ||
| 294 | error: | ||
| 295 | free(pp->probes[0]); | ||
| 296 | pp->probes[0] = NULL; | ||
| 297 | } | ||
| 298 | return ret; | ||
| 299 | } | ||
| 300 | |||
| 301 | int synthesize_perf_probe_event(struct probe_point *pp) | ||
| 302 | { | ||
| 303 | char *buf; | ||
| 304 | int i, len, ret; | ||
| 305 | |||
| 306 | len = synthesize_perf_probe_point(pp); | ||
| 307 | if (len < 0) | ||
| 308 | return 0; | ||
| 309 | |||
| 310 | buf = pp->probes[0]; | ||
| 311 | for (i = 0; i < pp->nr_args; i++) { | ||
| 312 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | ||
| 313 | pp->args[i]); | ||
| 314 | if (ret <= 0) | ||
| 315 | goto error; | ||
| 316 | len += ret; | ||
| 317 | } | ||
| 318 | pp->found = 1; | ||
| 319 | |||
| 320 | return pp->found; | ||
| 321 | error: | ||
| 322 | free(pp->probes[0]); | ||
| 323 | pp->probes[0] = NULL; | ||
| 324 | |||
| 325 | return ret; | ||
| 326 | } | ||
| 327 | |||
| 328 | int synthesize_trace_kprobe_event(struct probe_point *pp) | ||
| 329 | { | ||
| 330 | char *buf; | ||
| 331 | int i, len, ret; | ||
| 332 | |||
| 333 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | ||
| 334 | if (!buf) | ||
| 335 | die("Failed to allocate memory by zalloc."); | ||
| 336 | ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); | ||
| 337 | if (ret <= 0) | ||
| 338 | goto error; | ||
| 339 | len = ret; | ||
| 340 | |||
| 341 | for (i = 0; i < pp->nr_args; i++) { | ||
| 342 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | ||
| 343 | pp->args[i]); | ||
| 344 | if (ret <= 0) | ||
| 345 | goto error; | ||
| 346 | len += ret; | ||
| 347 | } | ||
| 348 | pp->found = 1; | ||
| 349 | |||
| 350 | return pp->found; | ||
| 351 | error: | ||
| 352 | free(pp->probes[0]); | ||
| 353 | pp->probes[0] = NULL; | ||
| 354 | |||
| 355 | return ret; | ||
| 356 | } | ||
| 357 | |||
| 358 | static int open_kprobe_events(int flags, int mode) | ||
| 359 | { | ||
| 360 | char buf[PATH_MAX]; | ||
| 361 | int ret; | ||
| 362 | |||
| 363 | ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); | ||
| 364 | if (ret < 0) | ||
| 365 | die("Failed to make kprobe_events path."); | ||
| 366 | |||
| 367 | ret = open(buf, flags, mode); | ||
| 368 | if (ret < 0) { | ||
| 369 | if (errno == ENOENT) | ||
| 370 | die("kprobe_events file does not exist -" | ||
| 371 | " please rebuild with CONFIG_KPROBE_TRACER."); | ||
| 372 | else | ||
| 373 | die("Could not open kprobe_events file: %s", | ||
| 374 | strerror(errno)); | ||
| 375 | } | ||
| 376 | return ret; | ||
| 377 | } | ||
| 378 | |||
| 379 | /* Get raw string list of current kprobe_events */ | ||
| 380 | static struct strlist *get_trace_kprobe_event_rawlist(int fd) | ||
| 381 | { | ||
| 382 | int ret, idx; | ||
| 383 | FILE *fp; | ||
| 384 | char buf[MAX_CMDLEN]; | ||
| 385 | char *p; | ||
| 386 | struct strlist *sl; | ||
| 387 | |||
| 388 | sl = strlist__new(true, NULL); | ||
| 389 | |||
| 390 | fp = fdopen(dup(fd), "r"); | ||
| 391 | while (!feof(fp)) { | ||
| 392 | p = fgets(buf, MAX_CMDLEN, fp); | ||
| 393 | if (!p) | ||
| 394 | break; | ||
| 395 | |||
| 396 | idx = strlen(p) - 1; | ||
| 397 | if (p[idx] == '\n') | ||
| 398 | p[idx] = '\0'; | ||
| 399 | ret = strlist__add(sl, buf); | ||
| 400 | if (ret < 0) | ||
| 401 | die("strlist__add failed: %s", strerror(-ret)); | ||
| 402 | } | ||
| 403 | fclose(fp); | ||
| 404 | |||
| 405 | return sl; | ||
| 406 | } | ||
| 407 | |||
| 408 | /* Free and zero clear probe_point */ | ||
| 409 | static void clear_probe_point(struct probe_point *pp) | ||
| 410 | { | ||
| 411 | int i; | ||
| 412 | |||
| 413 | if (pp->event) | ||
| 414 | free(pp->event); | ||
| 415 | if (pp->group) | ||
| 416 | free(pp->group); | ||
| 417 | if (pp->function) | ||
| 418 | free(pp->function); | ||
| 419 | if (pp->file) | ||
| 420 | free(pp->file); | ||
| 421 | for (i = 0; i < pp->nr_args; i++) | ||
| 422 | free(pp->args[i]); | ||
| 423 | if (pp->args) | ||
| 424 | free(pp->args); | ||
| 425 | for (i = 0; i < pp->found; i++) | ||
| 426 | free(pp->probes[i]); | ||
| 427 | memset(pp, 0, sizeof(*pp)); | ||
| 428 | } | ||
| 429 | |||
| 430 | /* Show an event */ | ||
| 431 | static void show_perf_probe_event(const char *event, const char *place, | ||
| 432 | struct probe_point *pp) | ||
| 433 | { | ||
| 434 | int i, ret; | ||
| 435 | char buf[128]; | ||
| 436 | |||
| 437 | ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); | ||
| 438 | if (ret < 0) | ||
| 439 | die("Failed to copy event: %s", strerror(-ret)); | ||
| 440 | printf(" %-40s (on %s", buf, place); | ||
| 441 | |||
| 442 | if (pp->nr_args > 0) { | ||
| 443 | printf(" with"); | ||
| 444 | for (i = 0; i < pp->nr_args; i++) | ||
| 445 | printf(" %s", pp->args[i]); | ||
| 446 | } | ||
| 447 | printf(")\n"); | ||
| 448 | } | ||
| 449 | |||
| 450 | /* List up current perf-probe events */ | ||
| 451 | void show_perf_probe_events(void) | ||
| 452 | { | ||
| 453 | int fd; | ||
| 454 | struct probe_point pp; | ||
| 455 | struct strlist *rawlist; | ||
| 456 | struct str_node *ent; | ||
| 457 | |||
| 458 | fd = open_kprobe_events(O_RDONLY, 0); | ||
| 459 | rawlist = get_trace_kprobe_event_rawlist(fd); | ||
| 460 | close(fd); | ||
| 461 | |||
| 462 | strlist__for_each(ent, rawlist) { | ||
| 463 | parse_trace_kprobe_event(ent->s, &pp); | ||
| 464 | /* Synthesize only event probe point */ | ||
| 465 | synthesize_perf_probe_point(&pp); | ||
| 466 | /* Show an event */ | ||
| 467 | show_perf_probe_event(pp.event, pp.probes[0], &pp); | ||
| 468 | clear_probe_point(&pp); | ||
| 469 | } | ||
| 470 | |||
| 471 | strlist__delete(rawlist); | ||
| 472 | } | ||
| 473 | |||
| 474 | /* Get current perf-probe event names */ | ||
| 475 | static struct strlist *get_perf_event_names(int fd, bool include_group) | ||
| 476 | { | ||
| 477 | char buf[128]; | ||
| 478 | struct strlist *sl, *rawlist; | ||
| 479 | struct str_node *ent; | ||
| 480 | struct probe_point pp; | ||
| 481 | |||
| 482 | memset(&pp, 0, sizeof(pp)); | ||
| 483 | rawlist = get_trace_kprobe_event_rawlist(fd); | ||
| 484 | |||
| 485 | sl = strlist__new(true, NULL); | ||
| 486 | strlist__for_each(ent, rawlist) { | ||
| 487 | parse_trace_kprobe_event(ent->s, &pp); | ||
| 488 | if (include_group) { | ||
| 489 | if (e_snprintf(buf, 128, "%s:%s", pp.group, | ||
| 490 | pp.event) < 0) | ||
| 491 | die("Failed to copy group:event name."); | ||
| 492 | strlist__add(sl, buf); | ||
| 493 | } else | ||
| 494 | strlist__add(sl, pp.event); | ||
| 495 | clear_probe_point(&pp); | ||
| 496 | } | ||
| 497 | |||
| 498 | strlist__delete(rawlist); | ||
| 499 | |||
| 500 | return sl; | ||
| 501 | } | ||
| 502 | |||
| 503 | static void write_trace_kprobe_event(int fd, const char *buf) | ||
| 504 | { | ||
| 505 | int ret; | ||
| 506 | |||
| 507 | pr_debug("Writing event: %s\n", buf); | ||
| 508 | ret = write(fd, buf, strlen(buf)); | ||
| 509 | if (ret <= 0) | ||
| 510 | die("Failed to write event: %s", strerror(errno)); | ||
| 511 | } | ||
| 512 | |||
| 513 | static void get_new_event_name(char *buf, size_t len, const char *base, | ||
| 514 | struct strlist *namelist, bool allow_suffix) | ||
| 515 | { | ||
| 516 | int i, ret; | ||
| 517 | |||
| 518 | /* Try no suffix */ | ||
| 519 | ret = e_snprintf(buf, len, "%s", base); | ||
| 520 | if (ret < 0) | ||
| 521 | die("snprintf() failed: %s", strerror(-ret)); | ||
| 522 | if (!strlist__has_entry(namelist, buf)) | ||
| 523 | return; | ||
| 524 | |||
| 525 | if (!allow_suffix) { | ||
| 526 | pr_warning("Error: event \"%s\" already exists. " | ||
| 527 | "(Use -f to force duplicates.)\n", base); | ||
| 528 | die("Can't add new event."); | ||
| 529 | } | ||
| 530 | |||
| 531 | /* Try to add suffix */ | ||
| 532 | for (i = 1; i < MAX_EVENT_INDEX; i++) { | ||
| 533 | ret = e_snprintf(buf, len, "%s_%d", base, i); | ||
| 534 | if (ret < 0) | ||
| 535 | die("snprintf() failed: %s", strerror(-ret)); | ||
| 536 | if (!strlist__has_entry(namelist, buf)) | ||
| 537 | break; | ||
| 538 | } | ||
| 539 | if (i == MAX_EVENT_INDEX) | ||
| 540 | die("Too many events are on the same function."); | ||
| 541 | } | ||
| 542 | |||
| 543 | void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | ||
| 544 | bool force_add) | ||
| 545 | { | ||
| 546 | int i, j, fd; | ||
| 547 | struct probe_point *pp; | ||
| 548 | char buf[MAX_CMDLEN]; | ||
| 549 | char event[64]; | ||
| 550 | struct strlist *namelist; | ||
| 551 | bool allow_suffix; | ||
| 552 | |||
| 553 | fd = open_kprobe_events(O_RDWR, O_APPEND); | ||
| 554 | /* Get current event names */ | ||
| 555 | namelist = get_perf_event_names(fd, false); | ||
| 556 | |||
| 557 | for (j = 0; j < nr_probes; j++) { | ||
| 558 | pp = probes + j; | ||
| 559 | if (!pp->event) | ||
| 560 | pp->event = strdup(pp->function); | ||
| 561 | if (!pp->group) | ||
| 562 | pp->group = strdup(PERFPROBE_GROUP); | ||
| 563 | DIE_IF(!pp->event || !pp->group); | ||
| 564 | /* If force_add is true, suffix search is allowed */ | ||
| 565 | allow_suffix = force_add; | ||
| 566 | for (i = 0; i < pp->found; i++) { | ||
| 567 | /* Get an unused new event name */ | ||
| 568 | get_new_event_name(event, 64, pp->event, namelist, | ||
| 569 | allow_suffix); | ||
| 570 | snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", | ||
| 571 | pp->retprobe ? 'r' : 'p', | ||
| 572 | pp->group, event, | ||
| 573 | pp->probes[i]); | ||
| 574 | write_trace_kprobe_event(fd, buf); | ||
| 575 | printf("Added new event:\n"); | ||
| 576 | /* Get the first parameter (probe-point) */ | ||
| 577 | sscanf(pp->probes[i], "%s", buf); | ||
| 578 | show_perf_probe_event(event, buf, pp); | ||
| 579 | /* Add added event name to namelist */ | ||
| 580 | strlist__add(namelist, event); | ||
| 581 | /* | ||
| 582 | * Probes after the first probe which comes from same | ||
| 583 | * user input are always allowed to add suffix, because | ||
| 584 | * there might be several addresses corresponding to | ||
| 585 | * one code line. | ||
| 586 | */ | ||
| 587 | allow_suffix = true; | ||
| 588 | } | ||
| 589 | } | ||
| 590 | /* Show how to use the event. */ | ||
| 591 | printf("\nYou can now use it on all perf tools, such as:\n\n"); | ||
| 592 | printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event); | ||
| 593 | |||
| 594 | strlist__delete(namelist); | ||
| 595 | close(fd); | ||
| 596 | } | ||
| 597 | |||
| 598 | static void __del_trace_kprobe_event(int fd, struct str_node *ent) | ||
| 599 | { | ||
| 600 | char *p; | ||
| 601 | char buf[128]; | ||
| 602 | |||
| 603 | /* Convert from perf-probe event to trace-kprobe event */ | ||
| 604 | if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) | ||
| 605 | die("Failed to copy event."); | ||
| 606 | p = strchr(buf + 2, ':'); | ||
| 607 | if (!p) | ||
| 608 | die("Internal error: %s should have ':' but not.", ent->s); | ||
| 609 | *p = '/'; | ||
| 610 | |||
| 611 | write_trace_kprobe_event(fd, buf); | ||
| 612 | printf("Remove event: %s\n", ent->s); | ||
| 613 | } | ||
| 614 | |||
| 615 | static void del_trace_kprobe_event(int fd, const char *group, | ||
| 616 | const char *event, struct strlist *namelist) | ||
| 617 | { | ||
| 618 | char buf[128]; | ||
| 619 | struct str_node *ent, *n; | ||
| 620 | int found = 0; | ||
| 621 | |||
| 622 | if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) | ||
| 623 | die("Failed to copy event."); | ||
| 624 | |||
| 625 | if (strpbrk(buf, "*?")) { /* Glob-exp */ | ||
| 626 | strlist__for_each_safe(ent, n, namelist) | ||
| 627 | if (strglobmatch(ent->s, buf)) { | ||
| 628 | found++; | ||
| 629 | __del_trace_kprobe_event(fd, ent); | ||
| 630 | strlist__remove(namelist, ent); | ||
| 631 | } | ||
| 632 | } else { | ||
| 633 | ent = strlist__find(namelist, buf); | ||
| 634 | if (ent) { | ||
| 635 | found++; | ||
| 636 | __del_trace_kprobe_event(fd, ent); | ||
| 637 | strlist__remove(namelist, ent); | ||
| 638 | } | ||
| 639 | } | ||
| 640 | if (found == 0) | ||
| 641 | pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); | ||
| 642 | } | ||
| 643 | |||
| 644 | void del_trace_kprobe_events(struct strlist *dellist) | ||
| 645 | { | ||
| 646 | int fd; | ||
| 647 | const char *group, *event; | ||
| 648 | char *p, *str; | ||
| 649 | struct str_node *ent; | ||
| 650 | struct strlist *namelist; | ||
| 651 | |||
| 652 | fd = open_kprobe_events(O_RDWR, O_APPEND); | ||
| 653 | /* Get current event names */ | ||
| 654 | namelist = get_perf_event_names(fd, true); | ||
| 655 | |||
| 656 | strlist__for_each(ent, dellist) { | ||
| 657 | str = strdup(ent->s); | ||
| 658 | if (!str) | ||
| 659 | die("Failed to copy event."); | ||
| 660 | pr_debug("Parsing: %s\n", str); | ||
| 661 | p = strchr(str, ':'); | ||
| 662 | if (p) { | ||
| 663 | group = str; | ||
| 664 | *p = '\0'; | ||
| 665 | event = p + 1; | ||
| 666 | } else { | ||
| 667 | group = "*"; | ||
| 668 | event = str; | ||
| 669 | } | ||
| 670 | pr_debug("Group: %s, Event: %s\n", group, event); | ||
| 671 | del_trace_kprobe_event(fd, group, event, namelist); | ||
| 672 | free(str); | ||
| 673 | } | ||
| 674 | strlist__delete(namelist); | ||
| 675 | close(fd); | ||
| 676 | } | ||
| 677 | |||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h new file mode 100644 index 000000000000..7f1d499118c0 --- /dev/null +++ b/tools/perf/util/probe-event.h | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | #ifndef _PROBE_EVENT_H | ||
| 2 | #define _PROBE_EVENT_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include "probe-finder.h" | ||
| 6 | #include "strlist.h" | ||
| 7 | |||
| 8 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, | ||
| 9 | bool *need_dwarf); | ||
| 10 | extern int synthesize_perf_probe_point(struct probe_point *pp); | ||
| 11 | extern int synthesize_perf_probe_event(struct probe_point *pp); | ||
| 12 | extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); | ||
| 13 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); | ||
| 14 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | ||
| 15 | bool force_add); | ||
| 16 | extern void del_trace_kprobe_events(struct strlist *dellist); | ||
| 17 | extern void show_perf_probe_events(void); | ||
| 18 | |||
| 19 | /* Maximum index number of event-name postfix */ | ||
| 20 | #define MAX_EVENT_INDEX 1024 | ||
| 21 | |||
| 22 | #endif /*_PROBE_EVENT_H */ | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c new file mode 100644 index 000000000000..4b852c0d16a5 --- /dev/null +++ b/tools/perf/util/probe-finder.c | |||
| @@ -0,0 +1,730 @@ | |||
| 1 | /* | ||
| 2 | * probe-finder.c : C expression to kprobe event converter | ||
| 3 | * | ||
| 4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <sys/utsname.h> | ||
| 23 | #include <sys/types.h> | ||
| 24 | #include <sys/stat.h> | ||
| 25 | #include <fcntl.h> | ||
| 26 | #include <errno.h> | ||
| 27 | #include <stdio.h> | ||
| 28 | #include <unistd.h> | ||
| 29 | #include <getopt.h> | ||
| 30 | #include <stdlib.h> | ||
| 31 | #include <string.h> | ||
| 32 | #include <stdarg.h> | ||
| 33 | #include <ctype.h> | ||
| 34 | |||
| 35 | #include "event.h" | ||
| 36 | #include "debug.h" | ||
| 37 | #include "util.h" | ||
| 38 | #include "probe-finder.h" | ||
| 39 | |||
| 40 | |||
| 41 | /* Dwarf_Die Linkage to parent Die */ | ||
| 42 | struct die_link { | ||
| 43 | struct die_link *parent; /* Parent die */ | ||
| 44 | Dwarf_Die die; /* Current die */ | ||
| 45 | }; | ||
| 46 | |||
| 47 | static Dwarf_Debug __dw_debug; | ||
| 48 | static Dwarf_Error __dw_error; | ||
| 49 | |||
| 50 | /* | ||
| 51 | * Generic dwarf analysis helpers | ||
| 52 | */ | ||
| 53 | |||
| 54 | #define X86_32_MAX_REGS 8 | ||
| 55 | const char *x86_32_regs_table[X86_32_MAX_REGS] = { | ||
| 56 | "%ax", | ||
| 57 | "%cx", | ||
| 58 | "%dx", | ||
| 59 | "%bx", | ||
| 60 | "$stack", /* Stack address instead of %sp */ | ||
| 61 | "%bp", | ||
| 62 | "%si", | ||
| 63 | "%di", | ||
| 64 | }; | ||
| 65 | |||
| 66 | #define X86_64_MAX_REGS 16 | ||
| 67 | const char *x86_64_regs_table[X86_64_MAX_REGS] = { | ||
| 68 | "%ax", | ||
| 69 | "%dx", | ||
| 70 | "%cx", | ||
| 71 | "%bx", | ||
| 72 | "%si", | ||
| 73 | "%di", | ||
| 74 | "%bp", | ||
| 75 | "%sp", | ||
| 76 | "%r8", | ||
| 77 | "%r9", | ||
| 78 | "%r10", | ||
| 79 | "%r11", | ||
| 80 | "%r12", | ||
| 81 | "%r13", | ||
| 82 | "%r14", | ||
| 83 | "%r15", | ||
| 84 | }; | ||
| 85 | |||
| 86 | /* TODO: switching by dwarf address size */ | ||
| 87 | #ifdef __x86_64__ | ||
| 88 | #define ARCH_MAX_REGS X86_64_MAX_REGS | ||
| 89 | #define arch_regs_table x86_64_regs_table | ||
| 90 | #else | ||
| 91 | #define ARCH_MAX_REGS X86_32_MAX_REGS | ||
| 92 | #define arch_regs_table x86_32_regs_table | ||
| 93 | #endif | ||
| 94 | |||
| 95 | /* Return architecture dependent register string (for kprobe-tracer) */ | ||
| 96 | static const char *get_arch_regstr(unsigned int n) | ||
| 97 | { | ||
| 98 | return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; | ||
| 99 | } | ||
| 100 | |||
| 101 | /* | ||
| 102 | * Compare the tail of two strings. | ||
| 103 | * Return 0 if whole of either string is same as another's tail part. | ||
| 104 | */ | ||
| 105 | static int strtailcmp(const char *s1, const char *s2) | ||
| 106 | { | ||
| 107 | int i1 = strlen(s1); | ||
| 108 | int i2 = strlen(s2); | ||
| 109 | while (--i1 >= 0 && --i2 >= 0) { | ||
| 110 | if (s1[i1] != s2[i2]) | ||
| 111 | return s1[i1] - s2[i2]; | ||
| 112 | } | ||
| 113 | return 0; | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Find the fileno of the target file. */ | ||
| 117 | static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname) | ||
| 118 | { | ||
| 119 | Dwarf_Signed cnt, i; | ||
| 120 | Dwarf_Unsigned found = 0; | ||
| 121 | char **srcs; | ||
| 122 | int ret; | ||
| 123 | |||
| 124 | if (!fname) | ||
| 125 | return 0; | ||
| 126 | |||
| 127 | ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); | ||
| 128 | if (ret == DW_DLV_OK) { | ||
| 129 | for (i = 0; i < cnt && !found; i++) { | ||
| 130 | if (strtailcmp(srcs[i], fname) == 0) | ||
| 131 | found = i + 1; | ||
| 132 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | ||
| 133 | } | ||
| 134 | for (; i < cnt; i++) | ||
| 135 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | ||
| 136 | dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); | ||
| 137 | } | ||
| 138 | if (found) | ||
| 139 | pr_debug("found fno: %d\n", (int)found); | ||
| 140 | return found; | ||
| 141 | } | ||
| 142 | |||
| 143 | /* Compare diename and tname */ | ||
| 144 | static int die_compare_name(Dwarf_Die dw_die, const char *tname) | ||
| 145 | { | ||
| 146 | char *name; | ||
| 147 | int ret; | ||
| 148 | ret = dwarf_diename(dw_die, &name, &__dw_error); | ||
| 149 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 150 | if (ret == DW_DLV_OK) { | ||
| 151 | ret = strcmp(tname, name); | ||
| 152 | dwarf_dealloc(__dw_debug, name, DW_DLA_STRING); | ||
| 153 | } else | ||
| 154 | ret = -1; | ||
| 155 | return ret; | ||
| 156 | } | ||
| 157 | |||
| 158 | /* Check the address is in the subprogram(function). */ | ||
| 159 | static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr, | ||
| 160 | Dwarf_Signed *offs) | ||
| 161 | { | ||
| 162 | Dwarf_Addr lopc, hipc; | ||
| 163 | int ret; | ||
| 164 | |||
| 165 | /* TODO: check ranges */ | ||
| 166 | ret = dwarf_lowpc(sp_die, &lopc, &__dw_error); | ||
| 167 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 168 | if (ret == DW_DLV_NO_ENTRY) | ||
| 169 | return 0; | ||
| 170 | ret = dwarf_highpc(sp_die, &hipc, &__dw_error); | ||
| 171 | DIE_IF(ret != DW_DLV_OK); | ||
| 172 | if (lopc <= addr && addr < hipc) { | ||
| 173 | *offs = addr - lopc; | ||
| 174 | return 1; | ||
| 175 | } else | ||
| 176 | return 0; | ||
| 177 | } | ||
| 178 | |||
| 179 | /* Check the die is inlined function */ | ||
| 180 | static Dwarf_Bool die_inlined_subprogram(Dwarf_Die dw_die) | ||
| 181 | { | ||
| 182 | /* TODO: check strictly */ | ||
| 183 | Dwarf_Bool inl; | ||
| 184 | int ret; | ||
| 185 | |||
| 186 | ret = dwarf_hasattr(dw_die, DW_AT_inline, &inl, &__dw_error); | ||
| 187 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 188 | return inl; | ||
| 189 | } | ||
| 190 | |||
| 191 | /* Get the offset of abstruct_origin */ | ||
| 192 | static Dwarf_Off die_get_abstract_origin(Dwarf_Die dw_die) | ||
| 193 | { | ||
| 194 | Dwarf_Attribute attr; | ||
| 195 | Dwarf_Off cu_offs; | ||
| 196 | int ret; | ||
| 197 | |||
| 198 | ret = dwarf_attr(dw_die, DW_AT_abstract_origin, &attr, &__dw_error); | ||
| 199 | DIE_IF(ret != DW_DLV_OK); | ||
| 200 | ret = dwarf_formref(attr, &cu_offs, &__dw_error); | ||
| 201 | DIE_IF(ret != DW_DLV_OK); | ||
| 202 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 203 | return cu_offs; | ||
| 204 | } | ||
| 205 | |||
| 206 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | ||
| 207 | static Dwarf_Addr die_get_entrypc(Dwarf_Die dw_die) | ||
| 208 | { | ||
| 209 | Dwarf_Attribute attr; | ||
| 210 | Dwarf_Addr addr; | ||
| 211 | Dwarf_Off offs; | ||
| 212 | Dwarf_Ranges *ranges; | ||
| 213 | Dwarf_Signed cnt; | ||
| 214 | int ret; | ||
| 215 | |||
| 216 | /* Try to get entry pc */ | ||
| 217 | ret = dwarf_attr(dw_die, DW_AT_entry_pc, &attr, &__dw_error); | ||
| 218 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 219 | if (ret == DW_DLV_OK) { | ||
| 220 | ret = dwarf_formaddr(attr, &addr, &__dw_error); | ||
| 221 | DIE_IF(ret != DW_DLV_OK); | ||
| 222 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 223 | return addr; | ||
| 224 | } | ||
| 225 | |||
| 226 | /* Try to get low pc */ | ||
| 227 | ret = dwarf_lowpc(dw_die, &addr, &__dw_error); | ||
| 228 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 229 | if (ret == DW_DLV_OK) | ||
| 230 | return addr; | ||
| 231 | |||
| 232 | /* Try to get ranges */ | ||
| 233 | ret = dwarf_attr(dw_die, DW_AT_ranges, &attr, &__dw_error); | ||
| 234 | DIE_IF(ret != DW_DLV_OK); | ||
| 235 | ret = dwarf_formref(attr, &offs, &__dw_error); | ||
| 236 | DIE_IF(ret != DW_DLV_OK); | ||
| 237 | ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL, | ||
| 238 | &__dw_error); | ||
| 239 | DIE_IF(ret != DW_DLV_OK); | ||
| 240 | addr = ranges[0].dwr_addr1; | ||
| 241 | dwarf_ranges_dealloc(__dw_debug, ranges, cnt); | ||
| 242 | return addr; | ||
| 243 | } | ||
| 244 | |||
| 245 | /* | ||
| 246 | * Search a Die from Die tree. | ||
| 247 | * Note: cur_link->die should be deallocated in this function. | ||
| 248 | */ | ||
| 249 | static int __search_die_tree(struct die_link *cur_link, | ||
| 250 | int (*die_cb)(struct die_link *, void *), | ||
| 251 | void *data) | ||
| 252 | { | ||
| 253 | Dwarf_Die new_die; | ||
| 254 | struct die_link new_link; | ||
| 255 | int ret; | ||
| 256 | |||
| 257 | if (!die_cb) | ||
| 258 | return 0; | ||
| 259 | |||
| 260 | /* Check current die */ | ||
| 261 | while (!(ret = die_cb(cur_link, data))) { | ||
| 262 | /* Check child die */ | ||
| 263 | ret = dwarf_child(cur_link->die, &new_die, &__dw_error); | ||
| 264 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 265 | if (ret == DW_DLV_OK) { | ||
| 266 | new_link.parent = cur_link; | ||
| 267 | new_link.die = new_die; | ||
| 268 | ret = __search_die_tree(&new_link, die_cb, data); | ||
| 269 | if (ret) | ||
| 270 | break; | ||
| 271 | } | ||
| 272 | |||
| 273 | /* Move to next sibling */ | ||
| 274 | ret = dwarf_siblingof(__dw_debug, cur_link->die, &new_die, | ||
| 275 | &__dw_error); | ||
| 276 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 277 | dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE); | ||
| 278 | cur_link->die = new_die; | ||
| 279 | if (ret == DW_DLV_NO_ENTRY) | ||
| 280 | return 0; | ||
| 281 | } | ||
| 282 | dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE); | ||
| 283 | return ret; | ||
| 284 | } | ||
| 285 | |||
| 286 | /* Search a die in its children's die tree */ | ||
| 287 | static int search_die_from_children(Dwarf_Die parent_die, | ||
| 288 | int (*die_cb)(struct die_link *, void *), | ||
| 289 | void *data) | ||
| 290 | { | ||
| 291 | struct die_link new_link; | ||
| 292 | int ret; | ||
| 293 | |||
| 294 | new_link.parent = NULL; | ||
| 295 | ret = dwarf_child(parent_die, &new_link.die, &__dw_error); | ||
| 296 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 297 | if (ret == DW_DLV_OK) | ||
| 298 | return __search_die_tree(&new_link, die_cb, data); | ||
| 299 | else | ||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | |||
| 303 | /* Find a locdesc corresponding to the address */ | ||
| 304 | static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc, | ||
| 305 | Dwarf_Addr addr) | ||
| 306 | { | ||
| 307 | Dwarf_Signed lcnt; | ||
| 308 | Dwarf_Locdesc **llbuf; | ||
| 309 | int ret, i; | ||
| 310 | |||
| 311 | ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &__dw_error); | ||
| 312 | DIE_IF(ret != DW_DLV_OK); | ||
| 313 | ret = DW_DLV_NO_ENTRY; | ||
| 314 | for (i = 0; i < lcnt; ++i) { | ||
| 315 | if (llbuf[i]->ld_lopc <= addr && | ||
| 316 | llbuf[i]->ld_hipc > addr) { | ||
| 317 | memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc)); | ||
| 318 | desc->ld_s = | ||
| 319 | malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents); | ||
| 320 | DIE_IF(desc->ld_s == NULL); | ||
| 321 | memcpy(desc->ld_s, llbuf[i]->ld_s, | ||
| 322 | sizeof(Dwarf_Loc) * llbuf[i]->ld_cents); | ||
| 323 | ret = DW_DLV_OK; | ||
| 324 | break; | ||
| 325 | } | ||
| 326 | dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK); | ||
| 327 | dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC); | ||
| 328 | } | ||
| 329 | /* Releasing loop */ | ||
| 330 | for (; i < lcnt; ++i) { | ||
| 331 | dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK); | ||
| 332 | dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC); | ||
| 333 | } | ||
| 334 | dwarf_dealloc(__dw_debug, llbuf, DW_DLA_LIST); | ||
| 335 | return ret; | ||
| 336 | } | ||
| 337 | |||
| 338 | /* Get decl_file attribute value (file number) */ | ||
| 339 | static Dwarf_Unsigned die_get_decl_file(Dwarf_Die sp_die) | ||
| 340 | { | ||
| 341 | Dwarf_Attribute attr; | ||
| 342 | Dwarf_Unsigned fno; | ||
| 343 | int ret; | ||
| 344 | |||
| 345 | ret = dwarf_attr(sp_die, DW_AT_decl_file, &attr, &__dw_error); | ||
| 346 | DIE_IF(ret != DW_DLV_OK); | ||
| 347 | dwarf_formudata(attr, &fno, &__dw_error); | ||
| 348 | DIE_IF(ret != DW_DLV_OK); | ||
| 349 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 350 | return fno; | ||
| 351 | } | ||
| 352 | |||
| 353 | /* Get decl_line attribute value (line number) */ | ||
| 354 | static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die) | ||
| 355 | { | ||
| 356 | Dwarf_Attribute attr; | ||
| 357 | Dwarf_Unsigned lno; | ||
| 358 | int ret; | ||
| 359 | |||
| 360 | ret = dwarf_attr(sp_die, DW_AT_decl_line, &attr, &__dw_error); | ||
| 361 | DIE_IF(ret != DW_DLV_OK); | ||
| 362 | dwarf_formudata(attr, &lno, &__dw_error); | ||
| 363 | DIE_IF(ret != DW_DLV_OK); | ||
| 364 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 365 | return lno; | ||
| 366 | } | ||
| 367 | |||
| 368 | /* | ||
| 369 | * Probe finder related functions | ||
| 370 | */ | ||
| 371 | |||
| 372 | /* Show a location */ | ||
| 373 | static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) | ||
| 374 | { | ||
| 375 | Dwarf_Small op; | ||
| 376 | Dwarf_Unsigned regn; | ||
| 377 | Dwarf_Signed offs; | ||
| 378 | int deref = 0, ret; | ||
| 379 | const char *regs; | ||
| 380 | |||
| 381 | op = loc->lr_atom; | ||
| 382 | |||
| 383 | /* If this is based on frame buffer, set the offset */ | ||
| 384 | if (op == DW_OP_fbreg) { | ||
| 385 | deref = 1; | ||
| 386 | offs = (Dwarf_Signed)loc->lr_number; | ||
| 387 | op = pf->fbloc.ld_s[0].lr_atom; | ||
| 388 | loc = &pf->fbloc.ld_s[0]; | ||
| 389 | } else | ||
| 390 | offs = 0; | ||
| 391 | |||
| 392 | if (op >= DW_OP_breg0 && op <= DW_OP_breg31) { | ||
| 393 | regn = op - DW_OP_breg0; | ||
| 394 | offs += (Dwarf_Signed)loc->lr_number; | ||
| 395 | deref = 1; | ||
| 396 | } else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) { | ||
| 397 | regn = op - DW_OP_reg0; | ||
| 398 | } else if (op == DW_OP_bregx) { | ||
| 399 | regn = loc->lr_number; | ||
| 400 | offs += (Dwarf_Signed)loc->lr_number2; | ||
| 401 | deref = 1; | ||
| 402 | } else if (op == DW_OP_regx) { | ||
| 403 | regn = loc->lr_number; | ||
| 404 | } else | ||
| 405 | die("Dwarf_OP %d is not supported.\n", op); | ||
| 406 | |||
| 407 | regs = get_arch_regstr(regn); | ||
| 408 | if (!regs) | ||
| 409 | die("%lld exceeds max register number.\n", regn); | ||
| 410 | |||
| 411 | if (deref) | ||
| 412 | ret = snprintf(pf->buf, pf->len, | ||
| 413 | " %s=%+lld(%s)", pf->var, offs, regs); | ||
| 414 | else | ||
| 415 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); | ||
| 416 | DIE_IF(ret < 0); | ||
| 417 | DIE_IF(ret >= pf->len); | ||
| 418 | } | ||
| 419 | |||
| 420 | /* Show a variables in kprobe event format */ | ||
| 421 | static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf) | ||
| 422 | { | ||
| 423 | Dwarf_Attribute attr; | ||
| 424 | Dwarf_Locdesc ld; | ||
| 425 | int ret; | ||
| 426 | |||
| 427 | ret = dwarf_attr(vr_die, DW_AT_location, &attr, &__dw_error); | ||
| 428 | if (ret != DW_DLV_OK) | ||
| 429 | goto error; | ||
| 430 | ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base)); | ||
| 431 | if (ret != DW_DLV_OK) | ||
| 432 | goto error; | ||
| 433 | /* TODO? */ | ||
| 434 | DIE_IF(ld.ld_cents != 1); | ||
| 435 | show_location(&ld.ld_s[0], pf); | ||
| 436 | free(ld.ld_s); | ||
| 437 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 438 | return ; | ||
| 439 | error: | ||
| 440 | die("Failed to find the location of %s at this address.\n" | ||
| 441 | " Perhaps, it has been optimized out.\n", pf->var); | ||
| 442 | } | ||
| 443 | |||
| 444 | static int variable_callback(struct die_link *dlink, void *data) | ||
| 445 | { | ||
| 446 | struct probe_finder *pf = (struct probe_finder *)data; | ||
| 447 | Dwarf_Half tag; | ||
| 448 | int ret; | ||
| 449 | |||
| 450 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | ||
| 451 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 452 | if ((tag == DW_TAG_formal_parameter || | ||
| 453 | tag == DW_TAG_variable) && | ||
| 454 | (die_compare_name(dlink->die, pf->var) == 0)) { | ||
| 455 | show_variable(dlink->die, pf); | ||
| 456 | return 1; | ||
| 457 | } | ||
| 458 | /* TODO: Support struct members and arrays */ | ||
| 459 | return 0; | ||
| 460 | } | ||
| 461 | |||
| 462 | /* Find a variable in a subprogram die */ | ||
| 463 | static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) | ||
| 464 | { | ||
| 465 | int ret; | ||
| 466 | |||
| 467 | if (!is_c_varname(pf->var)) { | ||
| 468 | /* Output raw parameters */ | ||
| 469 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); | ||
| 470 | DIE_IF(ret < 0); | ||
| 471 | DIE_IF(ret >= pf->len); | ||
| 472 | return ; | ||
| 473 | } | ||
| 474 | |||
| 475 | pr_debug("Searching '%s' variable in context.\n", pf->var); | ||
| 476 | /* Search child die for local variables and parameters. */ | ||
| 477 | ret = search_die_from_children(sp_die, variable_callback, pf); | ||
| 478 | if (!ret) | ||
| 479 | die("Failed to find '%s' in this function.\n", pf->var); | ||
| 480 | } | ||
| 481 | |||
| 482 | /* Get a frame base on the address */ | ||
| 483 | static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf) | ||
| 484 | { | ||
| 485 | Dwarf_Attribute attr; | ||
| 486 | int ret; | ||
| 487 | |||
| 488 | ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &__dw_error); | ||
| 489 | DIE_IF(ret != DW_DLV_OK); | ||
| 490 | ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base)); | ||
| 491 | DIE_IF(ret != DW_DLV_OK); | ||
| 492 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 493 | } | ||
| 494 | |||
| 495 | static void free_current_frame_base(struct probe_finder *pf) | ||
| 496 | { | ||
| 497 | free(pf->fbloc.ld_s); | ||
| 498 | memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc)); | ||
| 499 | } | ||
| 500 | |||
| 501 | /* Show a probe point to output buffer */ | ||
| 502 | static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, | ||
| 503 | struct probe_finder *pf) | ||
| 504 | { | ||
| 505 | struct probe_point *pp = pf->pp; | ||
| 506 | char *name; | ||
| 507 | char tmp[MAX_PROBE_BUFFER]; | ||
| 508 | int ret, i, len; | ||
| 509 | |||
| 510 | /* Output name of probe point */ | ||
| 511 | ret = dwarf_diename(sp_die, &name, &__dw_error); | ||
| 512 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 513 | if (ret == DW_DLV_OK) { | ||
| 514 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name, | ||
| 515 | (unsigned int)offs); | ||
| 516 | /* Copy the function name if possible */ | ||
| 517 | if (!pp->function) { | ||
| 518 | pp->function = strdup(name); | ||
| 519 | pp->offset = offs; | ||
| 520 | } | ||
| 521 | dwarf_dealloc(__dw_debug, name, DW_DLA_STRING); | ||
| 522 | } else { | ||
| 523 | /* This function has no name. */ | ||
| 524 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%llx", pf->addr); | ||
| 525 | if (!pp->function) { | ||
| 526 | /* TODO: Use _stext */ | ||
| 527 | pp->function = strdup(""); | ||
| 528 | pp->offset = (int)pf->addr; | ||
| 529 | } | ||
| 530 | } | ||
| 531 | DIE_IF(ret < 0); | ||
| 532 | DIE_IF(ret >= MAX_PROBE_BUFFER); | ||
| 533 | len = ret; | ||
| 534 | pr_debug("Probe point found: %s\n", tmp); | ||
| 535 | |||
| 536 | /* Find each argument */ | ||
| 537 | get_current_frame_base(sp_die, pf); | ||
| 538 | for (i = 0; i < pp->nr_args; i++) { | ||
| 539 | pf->var = pp->args[i]; | ||
| 540 | pf->buf = &tmp[len]; | ||
| 541 | pf->len = MAX_PROBE_BUFFER - len; | ||
| 542 | find_variable(sp_die, pf); | ||
| 543 | len += strlen(pf->buf); | ||
| 544 | } | ||
| 545 | free_current_frame_base(pf); | ||
| 546 | |||
| 547 | pp->probes[pp->found] = strdup(tmp); | ||
| 548 | pp->found++; | ||
| 549 | } | ||
| 550 | |||
| 551 | static int probeaddr_callback(struct die_link *dlink, void *data) | ||
| 552 | { | ||
| 553 | struct probe_finder *pf = (struct probe_finder *)data; | ||
| 554 | Dwarf_Half tag; | ||
| 555 | Dwarf_Signed offs; | ||
| 556 | int ret; | ||
| 557 | |||
| 558 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | ||
| 559 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 560 | /* Check the address is in this subprogram */ | ||
| 561 | if (tag == DW_TAG_subprogram && | ||
| 562 | die_within_subprogram(dlink->die, pf->addr, &offs)) { | ||
| 563 | show_probepoint(dlink->die, offs, pf); | ||
| 564 | return 1; | ||
| 565 | } | ||
| 566 | return 0; | ||
| 567 | } | ||
| 568 | |||
| 569 | /* Find probe point from its line number */ | ||
| 570 | static void find_by_line(struct probe_finder *pf) | ||
| 571 | { | ||
| 572 | Dwarf_Signed cnt, i, clm; | ||
| 573 | Dwarf_Line *lines; | ||
| 574 | Dwarf_Unsigned lineno = 0; | ||
| 575 | Dwarf_Addr addr; | ||
| 576 | Dwarf_Unsigned fno; | ||
| 577 | int ret; | ||
| 578 | |||
| 579 | ret = dwarf_srclines(pf->cu_die, &lines, &cnt, &__dw_error); | ||
| 580 | DIE_IF(ret != DW_DLV_OK); | ||
| 581 | |||
| 582 | for (i = 0; i < cnt; i++) { | ||
| 583 | ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error); | ||
| 584 | DIE_IF(ret != DW_DLV_OK); | ||
| 585 | if (fno != pf->fno) | ||
| 586 | continue; | ||
| 587 | |||
| 588 | ret = dwarf_lineno(lines[i], &lineno, &__dw_error); | ||
| 589 | DIE_IF(ret != DW_DLV_OK); | ||
| 590 | if (lineno != pf->lno) | ||
| 591 | continue; | ||
| 592 | |||
| 593 | ret = dwarf_lineoff(lines[i], &clm, &__dw_error); | ||
| 594 | DIE_IF(ret != DW_DLV_OK); | ||
| 595 | |||
| 596 | ret = dwarf_lineaddr(lines[i], &addr, &__dw_error); | ||
| 597 | DIE_IF(ret != DW_DLV_OK); | ||
| 598 | pr_debug("Probe line found: line[%d]:%u,%d addr:0x%llx\n", | ||
| 599 | (int)i, (unsigned)lineno, (int)clm, addr); | ||
| 600 | pf->addr = addr; | ||
| 601 | /* Search a real subprogram including this line, */ | ||
| 602 | ret = search_die_from_children(pf->cu_die, | ||
| 603 | probeaddr_callback, pf); | ||
| 604 | if (ret == 0) | ||
| 605 | die("Probe point is not found in subprograms.\n"); | ||
| 606 | /* Continuing, because target line might be inlined. */ | ||
| 607 | } | ||
| 608 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); | ||
| 609 | } | ||
| 610 | |||
| 611 | /* Search function from function name */ | ||
| 612 | static int probefunc_callback(struct die_link *dlink, void *data) | ||
| 613 | { | ||
| 614 | struct probe_finder *pf = (struct probe_finder *)data; | ||
| 615 | struct probe_point *pp = pf->pp; | ||
| 616 | struct die_link *lk; | ||
| 617 | Dwarf_Signed offs; | ||
| 618 | Dwarf_Half tag; | ||
| 619 | int ret; | ||
| 620 | |||
| 621 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | ||
| 622 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 623 | if (tag == DW_TAG_subprogram) { | ||
| 624 | if (die_compare_name(dlink->die, pp->function) == 0) { | ||
| 625 | if (pp->line) { /* Function relative line */ | ||
| 626 | pf->fno = die_get_decl_file(dlink->die); | ||
| 627 | pf->lno = die_get_decl_line(dlink->die) | ||
| 628 | + pp->line; | ||
| 629 | find_by_line(pf); | ||
| 630 | return 1; | ||
| 631 | } | ||
| 632 | if (die_inlined_subprogram(dlink->die)) { | ||
| 633 | /* Inlined function, save it. */ | ||
| 634 | ret = dwarf_die_CU_offset(dlink->die, | ||
| 635 | &pf->inl_offs, | ||
| 636 | &__dw_error); | ||
| 637 | DIE_IF(ret != DW_DLV_OK); | ||
| 638 | pr_debug("inline definition offset %lld\n", | ||
| 639 | pf->inl_offs); | ||
| 640 | return 0; /* Continue to search */ | ||
| 641 | } | ||
| 642 | /* Get probe address */ | ||
| 643 | pf->addr = die_get_entrypc(dlink->die); | ||
| 644 | pf->addr += pp->offset; | ||
| 645 | /* TODO: Check the address in this function */ | ||
| 646 | show_probepoint(dlink->die, pp->offset, pf); | ||
| 647 | return 1; /* Exit; no same symbol in this CU. */ | ||
| 648 | } | ||
| 649 | } else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) { | ||
| 650 | if (die_get_abstract_origin(dlink->die) == pf->inl_offs) { | ||
| 651 | /* Get probe address */ | ||
| 652 | pf->addr = die_get_entrypc(dlink->die); | ||
| 653 | pf->addr += pp->offset; | ||
| 654 | pr_debug("found inline addr: 0x%llx\n", pf->addr); | ||
| 655 | /* Inlined function. Get a real subprogram */ | ||
| 656 | for (lk = dlink->parent; lk != NULL; lk = lk->parent) { | ||
| 657 | tag = 0; | ||
| 658 | dwarf_tag(lk->die, &tag, &__dw_error); | ||
| 659 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 660 | if (tag == DW_TAG_subprogram && | ||
| 661 | !die_inlined_subprogram(lk->die)) | ||
| 662 | goto found; | ||
| 663 | } | ||
| 664 | die("Failed to find real subprogram.\n"); | ||
| 665 | found: | ||
| 666 | /* Get offset from subprogram */ | ||
| 667 | ret = die_within_subprogram(lk->die, pf->addr, &offs); | ||
| 668 | DIE_IF(!ret); | ||
| 669 | show_probepoint(lk->die, offs, pf); | ||
| 670 | /* Continue to search */ | ||
| 671 | } | ||
| 672 | } | ||
| 673 | return 0; | ||
| 674 | } | ||
| 675 | |||
| 676 | static void find_by_func(struct probe_finder *pf) | ||
| 677 | { | ||
| 678 | search_die_from_children(pf->cu_die, probefunc_callback, pf); | ||
| 679 | } | ||
| 680 | |||
| 681 | /* Find a probe point */ | ||
| 682 | int find_probepoint(int fd, struct probe_point *pp) | ||
| 683 | { | ||
| 684 | Dwarf_Half addr_size = 0; | ||
| 685 | Dwarf_Unsigned next_cuh = 0; | ||
| 686 | int cu_number = 0, ret; | ||
| 687 | struct probe_finder pf = {.pp = pp}; | ||
| 688 | |||
| 689 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); | ||
| 690 | if (ret != DW_DLV_OK) | ||
| 691 | return -ENOENT; | ||
| 692 | |||
| 693 | pp->found = 0; | ||
| 694 | while (++cu_number) { | ||
| 695 | /* Search CU (Compilation Unit) */ | ||
| 696 | ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL, | ||
| 697 | &addr_size, &next_cuh, &__dw_error); | ||
| 698 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 699 | if (ret == DW_DLV_NO_ENTRY) | ||
| 700 | break; | ||
| 701 | |||
| 702 | /* Get the DIE(Debugging Information Entry) of this CU */ | ||
| 703 | ret = dwarf_siblingof(__dw_debug, 0, &pf.cu_die, &__dw_error); | ||
| 704 | DIE_IF(ret != DW_DLV_OK); | ||
| 705 | |||
| 706 | /* Check if target file is included. */ | ||
| 707 | if (pp->file) | ||
| 708 | pf.fno = cu_find_fileno(pf.cu_die, pp->file); | ||
| 709 | |||
| 710 | if (!pp->file || pf.fno) { | ||
| 711 | /* Save CU base address (for frame_base) */ | ||
| 712 | ret = dwarf_lowpc(pf.cu_die, &pf.cu_base, &__dw_error); | ||
| 713 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 714 | if (ret == DW_DLV_NO_ENTRY) | ||
| 715 | pf.cu_base = 0; | ||
| 716 | if (pp->function) | ||
| 717 | find_by_func(&pf); | ||
| 718 | else { | ||
| 719 | pf.lno = pp->line; | ||
| 720 | find_by_line(&pf); | ||
| 721 | } | ||
| 722 | } | ||
| 723 | dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE); | ||
| 724 | } | ||
| 725 | ret = dwarf_finish(__dw_debug, &__dw_error); | ||
| 726 | DIE_IF(ret != DW_DLV_OK); | ||
| 727 | |||
| 728 | return pp->found; | ||
| 729 | } | ||
| 730 | |||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h new file mode 100644 index 000000000000..a4086aaddb73 --- /dev/null +++ b/tools/perf/util/probe-finder.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | #ifndef _PROBE_FINDER_H | ||
| 2 | #define _PROBE_FINDER_H | ||
| 3 | |||
| 4 | #define MAX_PATH_LEN 256 | ||
| 5 | #define MAX_PROBE_BUFFER 1024 | ||
| 6 | #define MAX_PROBES 128 | ||
| 7 | |||
| 8 | static inline int is_c_varname(const char *name) | ||
| 9 | { | ||
| 10 | /* TODO */ | ||
| 11 | return isalpha(name[0]) || name[0] == '_'; | ||
| 12 | } | ||
| 13 | |||
| 14 | struct probe_point { | ||
| 15 | char *event; /* Event name */ | ||
| 16 | char *group; /* Event group */ | ||
| 17 | |||
| 18 | /* Inputs */ | ||
| 19 | char *file; /* File name */ | ||
| 20 | int line; /* Line number */ | ||
| 21 | |||
| 22 | char *function; /* Function name */ | ||
| 23 | int offset; /* Offset bytes */ | ||
| 24 | |||
| 25 | int nr_args; /* Number of arguments */ | ||
| 26 | char **args; /* Arguments */ | ||
| 27 | |||
| 28 | int retprobe; /* Return probe */ | ||
| 29 | |||
| 30 | /* Output */ | ||
| 31 | int found; /* Number of found probe points */ | ||
| 32 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ | ||
| 33 | }; | ||
| 34 | |||
| 35 | #ifndef NO_LIBDWARF | ||
| 36 | extern int find_probepoint(int fd, struct probe_point *pp); | ||
| 37 | |||
| 38 | /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ | ||
| 39 | #ifndef _MIPS_SZLONG | ||
| 40 | # define _MIPS_SZLONG 0 | ||
| 41 | #endif | ||
| 42 | |||
| 43 | #include <dwarf.h> | ||
| 44 | #include <libdwarf.h> | ||
| 45 | |||
| 46 | struct probe_finder { | ||
| 47 | struct probe_point *pp; /* Target probe point */ | ||
| 48 | |||
| 49 | /* For function searching */ | ||
| 50 | Dwarf_Addr addr; /* Address */ | ||
| 51 | Dwarf_Unsigned fno; /* File number */ | ||
| 52 | Dwarf_Unsigned lno; /* Line number */ | ||
| 53 | Dwarf_Off inl_offs; /* Inline offset */ | ||
| 54 | Dwarf_Die cu_die; /* Current CU */ | ||
| 55 | |||
| 56 | /* For variable searching */ | ||
| 57 | Dwarf_Addr cu_base; /* Current CU base address */ | ||
| 58 | Dwarf_Locdesc fbloc; /* Location of Current Frame Base */ | ||
| 59 | const char *var; /* Current variable name */ | ||
| 60 | char *buf; /* Current output buffer */ | ||
| 61 | int len; /* Length of output buffer */ | ||
| 62 | }; | ||
| 63 | #endif /* NO_LIBDWARF */ | ||
| 64 | |||
| 65 | #endif /*_PROBE_FINDER_H */ | ||
diff --git a/tools/perf/util/quote.h b/tools/perf/util/quote.h index a5454a1d1c13..b6a019733919 100644 --- a/tools/perf/util/quote.h +++ b/tools/perf/util/quote.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef QUOTE_H | 1 | #ifndef __PERF_QUOTE_H |
| 2 | #define QUOTE_H | 2 | #define __PERF_QUOTE_H |
| 3 | 3 | ||
| 4 | #include <stddef.h> | 4 | #include <stddef.h> |
| 5 | #include <stdio.h> | 5 | #include <stdio.h> |
| @@ -65,4 +65,4 @@ extern void perl_quote_print(FILE *stream, const char *src); | |||
| 65 | extern void python_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); | 66 | extern void tcl_quote_print(FILE *stream, const char *src); |
| 67 | 67 | ||
| 68 | #endif | 68 | #endif /* __PERF_QUOTE_H */ |
diff --git a/tools/perf/util/run-command.h b/tools/perf/util/run-command.h index cc1837deba88..d79028727ce2 100644 --- a/tools/perf/util/run-command.h +++ b/tools/perf/util/run-command.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef RUN_COMMAND_H | 1 | #ifndef __PERF_RUN_COMMAND_H |
| 2 | #define RUN_COMMAND_H | 2 | #define __PERF_RUN_COMMAND_H |
| 3 | 3 | ||
| 4 | enum { | 4 | enum { |
| 5 | ERR_RUN_COMMAND_FORK = 10000, | 5 | ERR_RUN_COMMAND_FORK = 10000, |
| @@ -85,4 +85,4 @@ struct async { | |||
| 85 | int start_async(struct async *async); | 85 | int start_async(struct async *async); |
| 86 | int finish_async(struct async *async); | 86 | int finish_async(struct async *async); |
| 87 | 87 | ||
| 88 | #endif | 88 | #endif /* __PERF_RUN_COMMAND_H */ |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c new file mode 100644 index 000000000000..ce3a6c8abe76 --- /dev/null +++ b/tools/perf/util/session.c | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | #include <linux/kernel.h> | ||
| 2 | |||
| 3 | #include <unistd.h> | ||
| 4 | #include <sys/types.h> | ||
| 5 | |||
| 6 | #include "session.h" | ||
| 7 | #include "sort.h" | ||
| 8 | #include "util.h" | ||
| 9 | |||
| 10 | static int perf_session__open(struct perf_session *self, bool force) | ||
| 11 | { | ||
| 12 | struct stat input_stat; | ||
| 13 | |||
| 14 | self->fd = open(self->filename, O_RDONLY); | ||
| 15 | if (self->fd < 0) { | ||
| 16 | pr_err("failed to open file: %s", self->filename); | ||
| 17 | if (!strcmp(self->filename, "perf.data")) | ||
| 18 | pr_err(" (try 'perf record' first)"); | ||
| 19 | pr_err("\n"); | ||
| 20 | return -errno; | ||
| 21 | } | ||
| 22 | |||
| 23 | if (fstat(self->fd, &input_stat) < 0) | ||
| 24 | goto out_close; | ||
| 25 | |||
| 26 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
| 27 | pr_err("file %s not owned by current user or root\n", | ||
| 28 | self->filename); | ||
| 29 | goto out_close; | ||
| 30 | } | ||
| 31 | |||
| 32 | if (!input_stat.st_size) { | ||
| 33 | pr_info("zero-sized file (%s), nothing to do!\n", | ||
| 34 | self->filename); | ||
| 35 | goto out_close; | ||
| 36 | } | ||
| 37 | |||
| 38 | if (perf_header__read(&self->header, self->fd) < 0) { | ||
| 39 | pr_err("incompatible file format"); | ||
| 40 | goto out_close; | ||
| 41 | } | ||
| 42 | |||
| 43 | self->size = input_stat.st_size; | ||
| 44 | return 0; | ||
| 45 | |||
| 46 | out_close: | ||
| 47 | close(self->fd); | ||
| 48 | self->fd = -1; | ||
| 49 | return -1; | ||
| 50 | } | ||
| 51 | |||
| 52 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) | ||
| 53 | { | ||
| 54 | size_t len = filename ? strlen(filename) + 1 : 0; | ||
| 55 | struct perf_session *self = zalloc(sizeof(*self) + len); | ||
| 56 | |||
| 57 | if (self == NULL) | ||
| 58 | goto out; | ||
| 59 | |||
| 60 | if (perf_header__init(&self->header) < 0) | ||
| 61 | goto out_free; | ||
| 62 | |||
| 63 | memcpy(self->filename, filename, len); | ||
| 64 | self->threads = RB_ROOT; | ||
| 65 | self->last_match = NULL; | ||
| 66 | self->mmap_window = 32; | ||
| 67 | self->cwd = NULL; | ||
| 68 | self->cwdlen = 0; | ||
| 69 | map_groups__init(&self->kmaps); | ||
| 70 | |||
| 71 | if (perf_session__create_kernel_maps(self) < 0) | ||
| 72 | goto out_delete; | ||
| 73 | |||
| 74 | if (mode == O_RDONLY && perf_session__open(self, force) < 0) | ||
| 75 | goto out_delete; | ||
| 76 | out: | ||
| 77 | return self; | ||
| 78 | out_free: | ||
| 79 | free(self); | ||
| 80 | return NULL; | ||
| 81 | out_delete: | ||
| 82 | perf_session__delete(self); | ||
| 83 | return NULL; | ||
| 84 | } | ||
| 85 | |||
| 86 | void perf_session__delete(struct perf_session *self) | ||
| 87 | { | ||
| 88 | perf_header__exit(&self->header); | ||
| 89 | close(self->fd); | ||
| 90 | free(self->cwd); | ||
| 91 | free(self); | ||
| 92 | } | ||
| 93 | |||
| 94 | static bool symbol__match_parent_regex(struct symbol *sym) | ||
| 95 | { | ||
| 96 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
| 97 | return 1; | ||
| 98 | |||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | ||
| 103 | struct thread *thread, | ||
| 104 | struct ip_callchain *chain, | ||
| 105 | struct symbol **parent) | ||
| 106 | { | ||
| 107 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
| 108 | struct symbol **syms = NULL; | ||
| 109 | unsigned int i; | ||
| 110 | |||
| 111 | if (symbol_conf.use_callchain) { | ||
| 112 | syms = calloc(chain->nr, sizeof(*syms)); | ||
| 113 | if (!syms) { | ||
| 114 | fprintf(stderr, "Can't allocate memory for symbols\n"); | ||
| 115 | exit(-1); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | for (i = 0; i < chain->nr; i++) { | ||
| 120 | u64 ip = chain->ips[i]; | ||
| 121 | struct addr_location al; | ||
| 122 | |||
| 123 | if (ip >= PERF_CONTEXT_MAX) { | ||
| 124 | switch (ip) { | ||
| 125 | case PERF_CONTEXT_HV: | ||
| 126 | cpumode = PERF_RECORD_MISC_HYPERVISOR; break; | ||
| 127 | case PERF_CONTEXT_KERNEL: | ||
| 128 | cpumode = PERF_RECORD_MISC_KERNEL; break; | ||
| 129 | case PERF_CONTEXT_USER: | ||
| 130 | cpumode = PERF_RECORD_MISC_USER; break; | ||
| 131 | default: | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | continue; | ||
| 135 | } | ||
| 136 | |||
| 137 | thread__find_addr_location(thread, self, cpumode, | ||
| 138 | MAP__FUNCTION, ip, &al, NULL); | ||
| 139 | if (al.sym != NULL) { | ||
| 140 | if (sort__has_parent && !*parent && | ||
| 141 | symbol__match_parent_regex(al.sym)) | ||
| 142 | *parent = al.sym; | ||
| 143 | if (!symbol_conf.use_callchain) | ||
| 144 | break; | ||
| 145 | syms[i] = al.sym; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | return syms; | ||
| 150 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h new file mode 100644 index 000000000000..32eaa1bada06 --- /dev/null +++ b/tools/perf/util/session.h | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | #ifndef __PERF_SESSION_H | ||
| 2 | #define __PERF_SESSION_H | ||
| 3 | |||
| 4 | #include "event.h" | ||
| 5 | #include "header.h" | ||
| 6 | #include "thread.h" | ||
| 7 | #include <linux/rbtree.h> | ||
| 8 | #include "../../../include/linux/perf_event.h" | ||
| 9 | |||
| 10 | struct ip_callchain; | ||
| 11 | struct thread; | ||
| 12 | struct symbol; | ||
| 13 | |||
| 14 | struct perf_session { | ||
| 15 | struct perf_header header; | ||
| 16 | unsigned long size; | ||
| 17 | unsigned long mmap_window; | ||
| 18 | struct map_groups kmaps; | ||
| 19 | struct rb_root threads; | ||
| 20 | struct thread *last_match; | ||
| 21 | struct events_stats events_stats; | ||
| 22 | unsigned long event_total[PERF_RECORD_MAX]; | ||
| 23 | struct rb_root hists; | ||
| 24 | u64 sample_type; | ||
| 25 | int fd; | ||
| 26 | int cwdlen; | ||
| 27 | char *cwd; | ||
| 28 | char filename[0]; | ||
| 29 | }; | ||
| 30 | |||
| 31 | typedef int (*event_op)(event_t *self, struct perf_session *session); | ||
| 32 | |||
| 33 | struct perf_event_ops { | ||
| 34 | event_op process_sample_event; | ||
| 35 | event_op process_mmap_event; | ||
| 36 | event_op process_comm_event; | ||
| 37 | event_op process_fork_event; | ||
| 38 | event_op process_exit_event; | ||
| 39 | event_op process_lost_event; | ||
| 40 | event_op process_read_event; | ||
| 41 | event_op process_throttle_event; | ||
| 42 | event_op process_unthrottle_event; | ||
| 43 | int (*sample_type_check)(struct perf_session *session); | ||
| 44 | unsigned long total_unknown; | ||
| 45 | bool full_paths; | ||
| 46 | }; | ||
| 47 | |||
| 48 | struct perf_session *perf_session__new(const char *filename, int mode, bool force); | ||
| 49 | void perf_session__delete(struct perf_session *self); | ||
| 50 | |||
| 51 | int perf_session__process_events(struct perf_session *self, | ||
| 52 | struct perf_event_ops *event_ops); | ||
| 53 | |||
| 54 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | ||
| 55 | struct thread *thread, | ||
| 56 | struct ip_callchain *chain, | ||
| 57 | struct symbol **parent); | ||
| 58 | |||
| 59 | int perf_header__read_build_ids(int input, u64 offset, u64 file_size); | ||
| 60 | |||
| 61 | #endif /* __PERF_SESSION_H */ | ||
diff --git a/tools/perf/util/sigchain.h b/tools/perf/util/sigchain.h index 618083bce0c6..1a53c11265fd 100644 --- a/tools/perf/util/sigchain.h +++ b/tools/perf/util/sigchain.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef SIGCHAIN_H | 1 | #ifndef __PERF_SIGCHAIN_H |
| 2 | #define SIGCHAIN_H | 2 | #define __PERF_SIGCHAIN_H |
| 3 | 3 | ||
| 4 | typedef void (*sigchain_fun)(int); | 4 | typedef void (*sigchain_fun)(int); |
| 5 | 5 | ||
| @@ -8,4 +8,4 @@ int sigchain_pop(int sig); | |||
| 8 | 8 | ||
| 9 | void sigchain_push_common(sigchain_fun f); | 9 | void sigchain_push_common(sigchain_fun f); |
| 10 | 10 | ||
| 11 | #endif /* SIGCHAIN_H */ | 11 | #endif /* __PERF_SIGCHAIN_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c new file mode 100644 index 000000000000..cb0f327de9e8 --- /dev/null +++ b/tools/perf/util/sort.c | |||
| @@ -0,0 +1,316 @@ | |||
| 1 | #include "sort.h" | ||
| 2 | |||
| 3 | regex_t parent_regex; | ||
| 4 | char default_parent_pattern[] = "^sys_|^do_page_fault"; | ||
| 5 | char *parent_pattern = default_parent_pattern; | ||
| 6 | char default_sort_order[] = "comm,dso,symbol"; | ||
| 7 | char *sort_order = default_sort_order; | ||
| 8 | int sort__need_collapse = 0; | ||
| 9 | int sort__has_parent = 0; | ||
| 10 | |||
| 11 | enum sort_type sort__first_dimension; | ||
| 12 | |||
| 13 | unsigned int dsos__col_width; | ||
| 14 | unsigned int comms__col_width; | ||
| 15 | unsigned int threads__col_width; | ||
| 16 | static unsigned int parent_symbol__col_width; | ||
| 17 | char * field_sep; | ||
| 18 | |||
| 19 | LIST_HEAD(hist_entry__sort_list); | ||
| 20 | |||
| 21 | struct sort_entry sort_thread = { | ||
| 22 | .header = "Command: Pid", | ||
| 23 | .cmp = sort__thread_cmp, | ||
| 24 | .print = sort__thread_print, | ||
| 25 | .width = &threads__col_width, | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct sort_entry sort_comm = { | ||
| 29 | .header = "Command", | ||
| 30 | .cmp = sort__comm_cmp, | ||
| 31 | .collapse = sort__comm_collapse, | ||
| 32 | .print = sort__comm_print, | ||
| 33 | .width = &comms__col_width, | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct sort_entry sort_dso = { | ||
| 37 | .header = "Shared Object", | ||
| 38 | .cmp = sort__dso_cmp, | ||
| 39 | .print = sort__dso_print, | ||
| 40 | .width = &dsos__col_width, | ||
| 41 | }; | ||
| 42 | |||
| 43 | struct sort_entry sort_sym = { | ||
| 44 | .header = "Symbol", | ||
| 45 | .cmp = sort__sym_cmp, | ||
| 46 | .print = sort__sym_print, | ||
| 47 | }; | ||
| 48 | |||
| 49 | struct sort_entry sort_parent = { | ||
| 50 | .header = "Parent symbol", | ||
| 51 | .cmp = sort__parent_cmp, | ||
| 52 | .print = sort__parent_print, | ||
| 53 | .width = &parent_symbol__col_width, | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct sort_dimension { | ||
| 57 | const char *name; | ||
| 58 | struct sort_entry *entry; | ||
| 59 | int taken; | ||
| 60 | }; | ||
| 61 | |||
| 62 | static struct sort_dimension sort_dimensions[] = { | ||
| 63 | { .name = "pid", .entry = &sort_thread, }, | ||
| 64 | { .name = "comm", .entry = &sort_comm, }, | ||
| 65 | { .name = "dso", .entry = &sort_dso, }, | ||
| 66 | { .name = "symbol", .entry = &sort_sym, }, | ||
| 67 | { .name = "parent", .entry = &sort_parent, }, | ||
| 68 | }; | ||
| 69 | |||
| 70 | int64_t cmp_null(void *l, void *r) | ||
| 71 | { | ||
| 72 | if (!l && !r) | ||
| 73 | return 0; | ||
| 74 | else if (!l) | ||
| 75 | return -1; | ||
| 76 | else | ||
| 77 | return 1; | ||
| 78 | } | ||
| 79 | |||
| 80 | /* --sort pid */ | ||
| 81 | |||
| 82 | int64_t | ||
| 83 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 84 | { | ||
| 85 | return right->thread->pid - left->thread->pid; | ||
| 86 | } | ||
| 87 | |||
| 88 | int repsep_fprintf(FILE *fp, const char *fmt, ...) | ||
| 89 | { | ||
| 90 | int n; | ||
| 91 | va_list ap; | ||
| 92 | |||
| 93 | va_start(ap, fmt); | ||
| 94 | if (!field_sep) | ||
| 95 | n = vfprintf(fp, fmt, ap); | ||
| 96 | else { | ||
| 97 | char *bf = NULL; | ||
| 98 | n = vasprintf(&bf, fmt, ap); | ||
| 99 | if (n > 0) { | ||
| 100 | char *sep = bf; | ||
| 101 | |||
| 102 | while (1) { | ||
| 103 | sep = strchr(sep, *field_sep); | ||
| 104 | if (sep == NULL) | ||
| 105 | break; | ||
| 106 | *sep = '.'; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | fputs(bf, fp); | ||
| 110 | free(bf); | ||
| 111 | } | ||
| 112 | va_end(ap); | ||
| 113 | return n; | ||
| 114 | } | ||
| 115 | |||
| 116 | size_t | ||
| 117 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
| 118 | { | ||
| 119 | return repsep_fprintf(fp, "%*s:%5d", width - 6, | ||
| 120 | self->thread->comm ?: "", self->thread->pid); | ||
| 121 | } | ||
| 122 | |||
| 123 | size_t | ||
| 124 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
| 125 | { | ||
| 126 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); | ||
| 127 | } | ||
| 128 | |||
| 129 | /* --sort dso */ | ||
| 130 | |||
| 131 | int64_t | ||
| 132 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 133 | { | ||
| 134 | struct dso *dso_l = left->map ? left->map->dso : NULL; | ||
| 135 | struct dso *dso_r = right->map ? right->map->dso : NULL; | ||
| 136 | const char *dso_name_l, *dso_name_r; | ||
| 137 | |||
| 138 | if (!dso_l || !dso_r) | ||
| 139 | return cmp_null(dso_l, dso_r); | ||
| 140 | |||
| 141 | if (verbose) { | ||
| 142 | dso_name_l = dso_l->long_name; | ||
| 143 | dso_name_r = dso_r->long_name; | ||
| 144 | } else { | ||
| 145 | dso_name_l = dso_l->short_name; | ||
| 146 | dso_name_r = dso_r->short_name; | ||
| 147 | } | ||
| 148 | |||
| 149 | return strcmp(dso_name_l, dso_name_r); | ||
| 150 | } | ||
| 151 | |||
| 152 | size_t | ||
| 153 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
| 154 | { | ||
| 155 | if (self->map && self->map->dso) { | ||
| 156 | const char *dso_name = !verbose ? self->map->dso->short_name : | ||
| 157 | self->map->dso->long_name; | ||
| 158 | return repsep_fprintf(fp, "%-*s", width, dso_name); | ||
| 159 | } | ||
| 160 | |||
| 161 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); | ||
| 162 | } | ||
| 163 | |||
| 164 | /* --sort symbol */ | ||
| 165 | |||
| 166 | int64_t | ||
| 167 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 168 | { | ||
| 169 | u64 ip_l, ip_r; | ||
| 170 | |||
| 171 | if (left->sym == right->sym) | ||
| 172 | return 0; | ||
| 173 | |||
| 174 | ip_l = left->sym ? left->sym->start : left->ip; | ||
| 175 | ip_r = right->sym ? right->sym->start : right->ip; | ||
| 176 | |||
| 177 | return (int64_t)(ip_r - ip_l); | ||
| 178 | } | ||
| 179 | |||
| 180 | |||
| 181 | size_t | ||
| 182 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) | ||
| 183 | { | ||
| 184 | size_t ret = 0; | ||
| 185 | |||
| 186 | if (verbose) { | ||
| 187 | char o = self->map ? dso__symtab_origin(self->map->dso) : '!'; | ||
| 188 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); | ||
| 189 | } | ||
| 190 | |||
| 191 | ret += repsep_fprintf(fp, "[%c] ", self->level); | ||
| 192 | if (self->sym) | ||
| 193 | ret += repsep_fprintf(fp, "%s", self->sym->name); | ||
| 194 | else | ||
| 195 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); | ||
| 196 | |||
| 197 | return ret; | ||
| 198 | } | ||
| 199 | |||
| 200 | /* --sort comm */ | ||
| 201 | |||
| 202 | int64_t | ||
| 203 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 204 | { | ||
| 205 | return right->thread->pid - left->thread->pid; | ||
| 206 | } | ||
| 207 | |||
| 208 | int64_t | ||
| 209 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
| 210 | { | ||
| 211 | char *comm_l = left->thread->comm; | ||
| 212 | char *comm_r = right->thread->comm; | ||
| 213 | |||
| 214 | if (!comm_l || !comm_r) | ||
| 215 | return cmp_null(comm_l, comm_r); | ||
| 216 | |||
| 217 | return strcmp(comm_l, comm_r); | ||
| 218 | } | ||
| 219 | |||
| 220 | /* --sort parent */ | ||
| 221 | |||
| 222 | int64_t | ||
| 223 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 224 | { | ||
| 225 | struct symbol *sym_l = left->parent; | ||
| 226 | struct symbol *sym_r = right->parent; | ||
| 227 | |||
| 228 | if (!sym_l || !sym_r) | ||
| 229 | return cmp_null(sym_l, sym_r); | ||
| 230 | |||
| 231 | return strcmp(sym_l->name, sym_r->name); | ||
| 232 | } | ||
| 233 | |||
| 234 | size_t | ||
| 235 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
| 236 | { | ||
| 237 | return repsep_fprintf(fp, "%-*s", width, | ||
| 238 | self->parent ? self->parent->name : "[other]"); | ||
| 239 | } | ||
| 240 | |||
| 241 | int sort_dimension__add(const char *tok) | ||
| 242 | { | ||
| 243 | unsigned int i; | ||
| 244 | |||
| 245 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | ||
| 246 | struct sort_dimension *sd = &sort_dimensions[i]; | ||
| 247 | |||
| 248 | if (sd->taken) | ||
| 249 | continue; | ||
| 250 | |||
| 251 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
| 252 | continue; | ||
| 253 | |||
| 254 | if (sd->entry->collapse) | ||
| 255 | sort__need_collapse = 1; | ||
| 256 | |||
| 257 | if (sd->entry == &sort_parent) { | ||
| 258 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | ||
| 259 | if (ret) { | ||
| 260 | char err[BUFSIZ]; | ||
| 261 | |||
| 262 | regerror(ret, &parent_regex, err, sizeof(err)); | ||
| 263 | fprintf(stderr, "Invalid regex: %s\n%s", | ||
| 264 | parent_pattern, err); | ||
| 265 | exit(-1); | ||
| 266 | } | ||
| 267 | sort__has_parent = 1; | ||
| 268 | } | ||
| 269 | |||
| 270 | if (list_empty(&hist_entry__sort_list)) { | ||
| 271 | if (!strcmp(sd->name, "pid")) | ||
| 272 | sort__first_dimension = SORT_PID; | ||
| 273 | else if (!strcmp(sd->name, "comm")) | ||
| 274 | sort__first_dimension = SORT_COMM; | ||
| 275 | else if (!strcmp(sd->name, "dso")) | ||
| 276 | sort__first_dimension = SORT_DSO; | ||
| 277 | else if (!strcmp(sd->name, "symbol")) | ||
| 278 | sort__first_dimension = SORT_SYM; | ||
| 279 | else if (!strcmp(sd->name, "parent")) | ||
| 280 | sort__first_dimension = SORT_PARENT; | ||
| 281 | } | ||
| 282 | |||
| 283 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | ||
| 284 | sd->taken = 1; | ||
| 285 | |||
| 286 | return 0; | ||
| 287 | } | ||
| 288 | |||
| 289 | return -ESRCH; | ||
| 290 | } | ||
| 291 | |||
| 292 | void setup_sorting(const char * const usagestr[], const struct option *opts) | ||
| 293 | { | ||
| 294 | char *tmp, *tok, *str = strdup(sort_order); | ||
| 295 | |||
| 296 | for (tok = strtok_r(str, ", ", &tmp); | ||
| 297 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
| 298 | if (sort_dimension__add(tok) < 0) { | ||
| 299 | error("Unknown --sort key: `%s'", tok); | ||
| 300 | usage_with_options(usagestr, opts); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | free(str); | ||
| 305 | } | ||
| 306 | |||
| 307 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | ||
| 308 | const char *list_name, FILE *fp) | ||
| 309 | { | ||
| 310 | if (list && strlist__nr_entries(list) == 1) { | ||
| 311 | if (fp != NULL) | ||
| 312 | fprintf(fp, "# %s: %s\n", list_name, | ||
| 313 | strlist__entry(list, 0)->s); | ||
| 314 | self->elide = true; | ||
| 315 | } | ||
| 316 | } | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h new file mode 100644 index 000000000000..753f9ea99fb0 --- /dev/null +++ b/tools/perf/util/sort.h | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | #ifndef __PERF_SORT_H | ||
| 2 | #define __PERF_SORT_H | ||
| 3 | #include "../builtin.h" | ||
| 4 | |||
| 5 | #include "util.h" | ||
| 6 | |||
| 7 | #include "color.h" | ||
| 8 | #include <linux/list.h> | ||
| 9 | #include "cache.h" | ||
| 10 | #include <linux/rbtree.h> | ||
| 11 | #include "symbol.h" | ||
| 12 | #include "string.h" | ||
| 13 | #include "callchain.h" | ||
| 14 | #include "strlist.h" | ||
| 15 | #include "values.h" | ||
| 16 | |||
| 17 | #include "../perf.h" | ||
| 18 | #include "debug.h" | ||
| 19 | #include "header.h" | ||
| 20 | |||
| 21 | #include "parse-options.h" | ||
| 22 | #include "parse-events.h" | ||
| 23 | |||
| 24 | #include "thread.h" | ||
| 25 | #include "sort.h" | ||
| 26 | |||
| 27 | extern regex_t parent_regex; | ||
| 28 | extern char *sort_order; | ||
| 29 | extern char default_parent_pattern[]; | ||
| 30 | extern char *parent_pattern; | ||
| 31 | extern char default_sort_order[]; | ||
| 32 | extern int sort__need_collapse; | ||
| 33 | extern int sort__has_parent; | ||
| 34 | extern char *field_sep; | ||
| 35 | extern struct sort_entry sort_comm; | ||
| 36 | extern struct sort_entry sort_dso; | ||
| 37 | extern struct sort_entry sort_sym; | ||
| 38 | extern struct sort_entry sort_parent; | ||
| 39 | extern unsigned int dsos__col_width; | ||
| 40 | extern unsigned int comms__col_width; | ||
| 41 | extern unsigned int threads__col_width; | ||
| 42 | extern enum sort_type sort__first_dimension; | ||
| 43 | |||
| 44 | struct hist_entry { | ||
| 45 | struct rb_node rb_node; | ||
| 46 | u64 count; | ||
| 47 | struct thread *thread; | ||
| 48 | struct map *map; | ||
| 49 | struct symbol *sym; | ||
| 50 | u64 ip; | ||
| 51 | char level; | ||
| 52 | struct symbol *parent; | ||
| 53 | struct callchain_node callchain; | ||
| 54 | union { | ||
| 55 | unsigned long position; | ||
| 56 | struct hist_entry *pair; | ||
| 57 | struct rb_root sorted_chain; | ||
| 58 | }; | ||
| 59 | }; | ||
| 60 | |||
| 61 | enum sort_type { | ||
| 62 | SORT_PID, | ||
| 63 | SORT_COMM, | ||
| 64 | SORT_DSO, | ||
| 65 | SORT_SYM, | ||
| 66 | SORT_PARENT | ||
| 67 | }; | ||
| 68 | |||
| 69 | /* | ||
| 70 | * configurable sorting bits | ||
| 71 | */ | ||
| 72 | |||
| 73 | struct sort_entry { | ||
| 74 | struct list_head list; | ||
| 75 | |||
| 76 | const char *header; | ||
| 77 | |||
| 78 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | ||
| 79 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | ||
| 80 | size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); | ||
| 81 | unsigned int *width; | ||
| 82 | bool elide; | ||
| 83 | }; | ||
| 84 | |||
| 85 | extern struct sort_entry sort_thread; | ||
| 86 | extern struct list_head hist_entry__sort_list; | ||
| 87 | |||
| 88 | void setup_sorting(const char * const usagestr[], const struct option *opts); | ||
| 89 | |||
| 90 | extern int repsep_fprintf(FILE *fp, const char *fmt, ...); | ||
| 91 | extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); | ||
| 92 | extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); | ||
| 93 | extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); | ||
| 94 | extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used); | ||
| 95 | extern int64_t cmp_null(void *, void *); | ||
| 96 | extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *); | ||
| 97 | extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *); | ||
| 98 | extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); | ||
| 99 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); | ||
| 100 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | ||
| 101 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | ||
| 102 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | ||
| 103 | extern int sort_dimension__add(const char *); | ||
| 104 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | ||
| 105 | const char *list_name, FILE *fp); | ||
| 106 | |||
| 107 | #endif /* __PERF_SORT_H */ | ||
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h index d2aa86c014c1..a3d121d6c83e 100644 --- a/tools/perf/util/strbuf.h +++ b/tools/perf/util/strbuf.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef STRBUF_H | 1 | #ifndef __PERF_STRBUF_H |
| 2 | #define STRBUF_H | 2 | #define __PERF_STRBUF_H |
| 3 | 3 | ||
| 4 | /* | 4 | /* |
| 5 | * Strbuf's can be use in many ways: as a byte array, or to store arbitrary | 5 | * Strbuf's can be use in many ways: as a byte array, or to store arbitrary |
| @@ -134,4 +134,4 @@ extern int launch_editor(const char *path, struct strbuf *buffer, const char *co | |||
| 134 | extern int strbuf_branchname(struct strbuf *sb, const char *name); | 134 | extern int strbuf_branchname(struct strbuf *sb, const char *name); |
| 135 | extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); | 135 | extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); |
| 136 | 136 | ||
| 137 | #endif /* STRBUF_H */ | 137 | #endif /* __PERF_STRBUF_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index c93eca9a7be3..5352d7dccc61 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #include "string.h" | 1 | #include "string.h" |
| 2 | #include "util.h" | ||
| 2 | 3 | ||
| 3 | static int hex(char ch) | 4 | static int hex(char ch) |
| 4 | { | 5 | { |
| @@ -32,3 +33,221 @@ int hex2u64(const char *ptr, u64 *long_val) | |||
| 32 | 33 | ||
| 33 | return p - ptr; | 34 | return p - ptr; |
| 34 | } | 35 | } |
| 36 | |||
| 37 | char *strxfrchar(char *s, char from, char to) | ||
| 38 | { | ||
| 39 | char *p = s; | ||
| 40 | |||
| 41 | while ((p = strchr(p, from)) != NULL) | ||
| 42 | *p++ = to; | ||
| 43 | |||
| 44 | return s; | ||
| 45 | } | ||
| 46 | |||
| 47 | #define K 1024LL | ||
| 48 | /* | ||
| 49 | * perf_atoll() | ||
| 50 | * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB") | ||
| 51 | * and return its numeric value | ||
| 52 | */ | ||
| 53 | s64 perf_atoll(const char *str) | ||
| 54 | { | ||
| 55 | unsigned int i; | ||
| 56 | s64 length = -1, unit = 1; | ||
| 57 | |||
| 58 | if (!isdigit(str[0])) | ||
| 59 | goto out_err; | ||
| 60 | |||
| 61 | for (i = 1; i < strlen(str); i++) { | ||
| 62 | switch (str[i]) { | ||
| 63 | case 'B': | ||
| 64 | case 'b': | ||
| 65 | break; | ||
| 66 | case 'K': | ||
| 67 | if (str[i + 1] != 'B') | ||
| 68 | goto out_err; | ||
| 69 | else | ||
| 70 | goto kilo; | ||
| 71 | case 'k': | ||
| 72 | if (str[i + 1] != 'b') | ||
| 73 | goto out_err; | ||
| 74 | kilo: | ||
| 75 | unit = K; | ||
| 76 | break; | ||
| 77 | case 'M': | ||
| 78 | if (str[i + 1] != 'B') | ||
| 79 | goto out_err; | ||
| 80 | else | ||
| 81 | goto mega; | ||
| 82 | case 'm': | ||
| 83 | if (str[i + 1] != 'b') | ||
| 84 | goto out_err; | ||
| 85 | mega: | ||
| 86 | unit = K * K; | ||
| 87 | break; | ||
| 88 | case 'G': | ||
| 89 | if (str[i + 1] != 'B') | ||
| 90 | goto out_err; | ||
| 91 | else | ||
| 92 | goto giga; | ||
| 93 | case 'g': | ||
| 94 | if (str[i + 1] != 'b') | ||
| 95 | goto out_err; | ||
| 96 | giga: | ||
| 97 | unit = K * K * K; | ||
| 98 | break; | ||
| 99 | case 'T': | ||
| 100 | if (str[i + 1] != 'B') | ||
| 101 | goto out_err; | ||
| 102 | else | ||
| 103 | goto tera; | ||
| 104 | case 't': | ||
| 105 | if (str[i + 1] != 'b') | ||
| 106 | goto out_err; | ||
| 107 | tera: | ||
| 108 | unit = K * K * K * K; | ||
| 109 | break; | ||
| 110 | case '\0': /* only specified figures */ | ||
| 111 | unit = 1; | ||
| 112 | break; | ||
| 113 | default: | ||
| 114 | if (!isdigit(str[i])) | ||
| 115 | goto out_err; | ||
| 116 | break; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | length = atoll(str) * unit; | ||
| 121 | goto out; | ||
| 122 | |||
| 123 | out_err: | ||
| 124 | length = -1; | ||
| 125 | out: | ||
| 126 | return length; | ||
| 127 | } | ||
| 128 | |||
| 129 | /* | ||
| 130 | * Helper function for splitting a string into an argv-like array. | ||
| 131 | * originaly copied from lib/argv_split.c | ||
| 132 | */ | ||
| 133 | static const char *skip_sep(const char *cp) | ||
| 134 | { | ||
| 135 | while (*cp && isspace(*cp)) | ||
| 136 | cp++; | ||
| 137 | |||
| 138 | return cp; | ||
| 139 | } | ||
| 140 | |||
| 141 | static const char *skip_arg(const char *cp) | ||
| 142 | { | ||
| 143 | while (*cp && !isspace(*cp)) | ||
| 144 | cp++; | ||
| 145 | |||
| 146 | return cp; | ||
| 147 | } | ||
| 148 | |||
| 149 | static int count_argc(const char *str) | ||
| 150 | { | ||
| 151 | int count = 0; | ||
| 152 | |||
| 153 | while (*str) { | ||
| 154 | str = skip_sep(str); | ||
| 155 | if (*str) { | ||
| 156 | count++; | ||
| 157 | str = skip_arg(str); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | return count; | ||
| 162 | } | ||
| 163 | |||
| 164 | /** | ||
| 165 | * argv_free - free an argv | ||
| 166 | * @argv - the argument vector to be freed | ||
| 167 | * | ||
| 168 | * Frees an argv and the strings it points to. | ||
| 169 | */ | ||
| 170 | void argv_free(char **argv) | ||
| 171 | { | ||
| 172 | char **p; | ||
| 173 | for (p = argv; *p; p++) | ||
| 174 | free(*p); | ||
| 175 | |||
| 176 | free(argv); | ||
| 177 | } | ||
| 178 | |||
| 179 | /** | ||
| 180 | * argv_split - split a string at whitespace, returning an argv | ||
| 181 | * @str: the string to be split | ||
| 182 | * @argcp: returned argument count | ||
| 183 | * | ||
| 184 | * Returns an array of pointers to strings which are split out from | ||
| 185 | * @str. This is performed by strictly splitting on white-space; no | ||
| 186 | * quote processing is performed. Multiple whitespace characters are | ||
| 187 | * considered to be a single argument separator. The returned array | ||
| 188 | * is always NULL-terminated. Returns NULL on memory allocation | ||
| 189 | * failure. | ||
| 190 | */ | ||
| 191 | char **argv_split(const char *str, int *argcp) | ||
| 192 | { | ||
| 193 | int argc = count_argc(str); | ||
| 194 | char **argv = zalloc(sizeof(*argv) * (argc+1)); | ||
| 195 | char **argvp; | ||
| 196 | |||
| 197 | if (argv == NULL) | ||
| 198 | goto out; | ||
| 199 | |||
| 200 | if (argcp) | ||
| 201 | *argcp = argc; | ||
| 202 | |||
| 203 | argvp = argv; | ||
| 204 | |||
| 205 | while (*str) { | ||
| 206 | str = skip_sep(str); | ||
| 207 | |||
| 208 | if (*str) { | ||
| 209 | const char *p = str; | ||
| 210 | char *t; | ||
| 211 | |||
| 212 | str = skip_arg(str); | ||
| 213 | |||
| 214 | t = strndup(p, str-p); | ||
| 215 | if (t == NULL) | ||
| 216 | goto fail; | ||
| 217 | *argvp++ = t; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | *argvp = NULL; | ||
| 221 | |||
| 222 | out: | ||
| 223 | return argv; | ||
| 224 | |||
| 225 | fail: | ||
| 226 | argv_free(argv); | ||
| 227 | return NULL; | ||
| 228 | } | ||
| 229 | |||
| 230 | /* Glob expression pattern matching */ | ||
| 231 | bool strglobmatch(const char *str, const char *pat) | ||
| 232 | { | ||
| 233 | while (*str && *pat && *pat != '*') { | ||
| 234 | if (*pat == '?') { | ||
| 235 | str++; | ||
| 236 | pat++; | ||
| 237 | } else | ||
| 238 | if (*str++ != *pat++) | ||
| 239 | return false; | ||
| 240 | } | ||
| 241 | /* Check wild card */ | ||
| 242 | if (*pat == '*') { | ||
| 243 | while (*pat == '*') | ||
| 244 | pat++; | ||
| 245 | if (!*pat) /* Tail wild card matches all */ | ||
| 246 | return true; | ||
| 247 | while (*str) | ||
| 248 | if (strglobmatch(str++, pat)) | ||
| 249 | return true; | ||
| 250 | } | ||
| 251 | return !*str && !*pat; | ||
| 252 | } | ||
| 253 | |||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index bf39dfadfd24..02ede58c54b4 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
| @@ -1,11 +1,17 @@ | |||
| 1 | #ifndef _PERF_STRING_H_ | 1 | #ifndef __PERF_STRING_H_ |
| 2 | #define _PERF_STRING_H_ | 2 | #define __PERF_STRING_H_ |
| 3 | 3 | ||
| 4 | #include <stdbool.h> | ||
| 4 | #include "types.h" | 5 | #include "types.h" |
| 5 | 6 | ||
| 6 | int hex2u64(const char *ptr, u64 *val); | 7 | int hex2u64(const char *ptr, u64 *val); |
| 8 | char *strxfrchar(char *s, char from, char to); | ||
| 9 | s64 perf_atoll(const char *str); | ||
| 10 | char **argv_split(const char *str, int *argcp); | ||
| 11 | void argv_free(char **argv); | ||
| 12 | bool strglobmatch(const char *str, const char *pat); | ||
| 7 | 13 | ||
| 8 | #define _STR(x) #x | 14 | #define _STR(x) #x |
| 9 | #define STR(x) _STR(x) | 15 | #define STR(x) _STR(x) |
| 10 | 16 | ||
| 11 | #endif | 17 | #endif /* __PERF_STRING_H */ |
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 7ad38171dc2b..6783a2043555 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c | |||
| @@ -102,7 +102,7 @@ void strlist__remove(struct strlist *self, struct str_node *sn) | |||
| 102 | str_node__delete(sn, self->dupstr); | 102 | str_node__delete(sn, self->dupstr); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | bool strlist__has_entry(struct strlist *self, const char *entry) | 105 | struct str_node *strlist__find(struct strlist *self, const char *entry) |
| 106 | { | 106 | { |
| 107 | struct rb_node **p = &self->entries.rb_node; | 107 | struct rb_node **p = &self->entries.rb_node; |
| 108 | struct rb_node *parent = NULL; | 108 | struct rb_node *parent = NULL; |
| @@ -120,10 +120,10 @@ bool strlist__has_entry(struct strlist *self, const char *entry) | |||
| 120 | else if (rc < 0) | 120 | else if (rc < 0) |
| 121 | p = &(*p)->rb_right; | 121 | p = &(*p)->rb_right; |
| 122 | else | 122 | else |
| 123 | return true; | 123 | return sn; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | return false; | 126 | return NULL; |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | static int strlist__parse_list_entry(struct strlist *self, const char *s) | 129 | static int strlist__parse_list_entry(struct strlist *self, const char *s) |
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index 921818e44a54..3ba839007d2c 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef STRLIST_H_ | 1 | #ifndef __PERF_STRLIST_H |
| 2 | #define STRLIST_H_ | 2 | #define __PERF_STRLIST_H |
| 3 | 3 | ||
| 4 | #include <linux/rbtree.h> | 4 | #include <linux/rbtree.h> |
| 5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
| @@ -23,7 +23,12 @@ int strlist__load(struct strlist *self, const char *filename); | |||
| 23 | int strlist__add(struct strlist *self, const char *str); | 23 | int strlist__add(struct strlist *self, const char *str); |
| 24 | 24 | ||
| 25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); | 25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); |
| 26 | bool strlist__has_entry(struct strlist *self, const char *entry); | 26 | struct str_node *strlist__find(struct strlist *self, const char *entry); |
| 27 | |||
| 28 | static inline bool strlist__has_entry(struct strlist *self, const char *entry) | ||
| 29 | { | ||
| 30 | return strlist__find(self, entry) != NULL; | ||
| 31 | } | ||
| 27 | 32 | ||
| 28 | static inline bool strlist__empty(const struct strlist *self) | 33 | static inline bool strlist__empty(const struct strlist *self) |
| 29 | { | 34 | { |
| @@ -35,5 +40,39 @@ static inline unsigned int strlist__nr_entries(const struct strlist *self) | |||
| 35 | return self->nr_entries; | 40 | return self->nr_entries; |
| 36 | } | 41 | } |
| 37 | 42 | ||
| 43 | /* For strlist iteration */ | ||
| 44 | static inline struct str_node *strlist__first(struct strlist *self) | ||
| 45 | { | ||
| 46 | struct rb_node *rn = rb_first(&self->entries); | ||
| 47 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; | ||
| 48 | } | ||
| 49 | static inline struct str_node *strlist__next(struct str_node *sn) | ||
| 50 | { | ||
| 51 | struct rb_node *rn; | ||
| 52 | if (!sn) | ||
| 53 | return NULL; | ||
| 54 | rn = rb_next(&sn->rb_node); | ||
| 55 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; | ||
| 56 | } | ||
| 57 | |||
| 58 | /** | ||
| 59 | * strlist_for_each - iterate over a strlist | ||
| 60 | * @pos: the &struct str_node to use as a loop cursor. | ||
| 61 | * @self: the &struct strlist for loop. | ||
| 62 | */ | ||
| 63 | #define strlist__for_each(pos, self) \ | ||
| 64 | for (pos = strlist__first(self); pos; pos = strlist__next(pos)) | ||
| 65 | |||
| 66 | /** | ||
| 67 | * strlist_for_each_safe - iterate over a strlist safe against removal of | ||
| 68 | * str_node | ||
| 69 | * @pos: the &struct str_node to use as a loop cursor. | ||
| 70 | * @n: another &struct str_node to use as temporary storage. | ||
| 71 | * @self: the &struct strlist for loop. | ||
| 72 | */ | ||
| 73 | #define strlist__for_each_safe(pos, n, self) \ | ||
| 74 | for (pos = strlist__first(self), n = strlist__next(pos); pos;\ | ||
| 75 | pos = n, n = strlist__next(n)) | ||
| 76 | |||
| 38 | int strlist__parse_list(struct strlist *self, const char *s); | 77 | int strlist__parse_list(struct strlist *self, const char *s); |
| 39 | #endif /* STRLIST_H_ */ | 78 | #endif /* __PERF_STRLIST_H */ |
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index cd93195aedb3..e0781989cc31 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef _INCLUDE_GUARD_SVG_HELPER_ | 1 | #ifndef __PERF_SVGHELPER_H |
| 2 | #define _INCLUDE_GUARD_SVG_HELPER_ | 2 | #define __PERF_SVGHELPER_H |
| 3 | 3 | ||
| 4 | #include "types.h" | 4 | #include "types.h" |
| 5 | 5 | ||
| @@ -25,4 +25,4 @@ extern void svg_close(void); | |||
| 25 | 25 | ||
| 26 | extern int svg_page_width; | 26 | extern int svg_page_width; |
| 27 | 27 | ||
| 28 | #endif | 28 | #endif /* __PERF_SVGHELPER_H */ |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 226f44a2357d..ab92763edb03 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -1,15 +1,23 @@ | |||
| 1 | #include "util.h" | 1 | #include "util.h" |
| 2 | #include "../perf.h" | 2 | #include "../perf.h" |
| 3 | #include "session.h" | ||
| 4 | #include "sort.h" | ||
| 3 | #include "string.h" | 5 | #include "string.h" |
| 4 | #include "symbol.h" | 6 | #include "symbol.h" |
| 7 | #include "thread.h" | ||
| 5 | 8 | ||
| 6 | #include "debug.h" | 9 | #include "debug.h" |
| 7 | 10 | ||
| 11 | #include <asm/bug.h> | ||
| 8 | #include <libelf.h> | 12 | #include <libelf.h> |
| 9 | #include <gelf.h> | 13 | #include <gelf.h> |
| 10 | #include <elf.h> | 14 | #include <elf.h> |
| 15 | #include <limits.h> | ||
| 16 | #include <sys/utsname.h> | ||
| 11 | 17 | ||
| 12 | const char *sym_hist_filter; | 18 | #ifndef NT_GNU_BUILD_ID |
| 19 | #define NT_GNU_BUILD_ID 3 | ||
| 20 | #endif | ||
| 13 | 21 | ||
| 14 | enum dso_origin { | 22 | enum dso_origin { |
| 15 | DSO__ORIG_KERNEL = 0, | 23 | DSO__ORIG_KERNEL = 0, |
| @@ -18,94 +26,206 @@ enum dso_origin { | |||
| 18 | DSO__ORIG_UBUNTU, | 26 | DSO__ORIG_UBUNTU, |
| 19 | DSO__ORIG_BUILDID, | 27 | DSO__ORIG_BUILDID, |
| 20 | DSO__ORIG_DSO, | 28 | DSO__ORIG_DSO, |
| 29 | DSO__ORIG_KMODULE, | ||
| 21 | DSO__ORIG_NOT_FOUND, | 30 | DSO__ORIG_NOT_FOUND, |
| 22 | }; | 31 | }; |
| 23 | 32 | ||
| 24 | static struct symbol *symbol__new(u64 start, u64 len, | 33 | static void dsos__add(struct list_head *head, struct dso *dso); |
| 25 | const char *name, unsigned int priv_size, | 34 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
| 26 | u64 obj_start, int v) | 35 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
| 36 | struct perf_session *session, symbol_filter_t filter); | ||
| 37 | static int vmlinux_path__nr_entries; | ||
| 38 | static char **vmlinux_path; | ||
| 39 | |||
| 40 | struct symbol_conf symbol_conf = { | ||
| 41 | .exclude_other = true, | ||
| 42 | .use_modules = true, | ||
| 43 | .try_vmlinux_path = true, | ||
| 44 | }; | ||
| 45 | |||
| 46 | bool dso__loaded(const struct dso *self, enum map_type type) | ||
| 27 | { | 47 | { |
| 28 | size_t namelen = strlen(name) + 1; | 48 | return self->loaded & (1 << type); |
| 29 | struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); | 49 | } |
| 30 | 50 | ||
| 31 | if (!self) | 51 | bool dso__sorted_by_name(const struct dso *self, enum map_type type) |
| 32 | return NULL; | 52 | { |
| 53 | return self->sorted_by_name & (1 << type); | ||
| 54 | } | ||
| 55 | |||
| 56 | static void dso__set_loaded(struct dso *self, enum map_type type) | ||
| 57 | { | ||
| 58 | self->loaded |= (1 << type); | ||
| 59 | } | ||
| 60 | |||
| 61 | static void dso__set_sorted_by_name(struct dso *self, enum map_type type) | ||
| 62 | { | ||
| 63 | self->sorted_by_name |= (1 << type); | ||
| 64 | } | ||
| 65 | |||
| 66 | static bool symbol_type__is_a(char symbol_type, enum map_type map_type) | ||
| 67 | { | ||
| 68 | switch (map_type) { | ||
| 69 | case MAP__FUNCTION: | ||
| 70 | return symbol_type == 'T' || symbol_type == 'W'; | ||
| 71 | case MAP__VARIABLE: | ||
| 72 | return symbol_type == 'D' || symbol_type == 'd'; | ||
| 73 | default: | ||
| 74 | return false; | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | static void symbols__fixup_end(struct rb_root *self) | ||
| 79 | { | ||
| 80 | struct rb_node *nd, *prevnd = rb_first(self); | ||
| 81 | struct symbol *curr, *prev; | ||
| 82 | |||
| 83 | if (prevnd == NULL) | ||
| 84 | return; | ||
| 85 | |||
| 86 | curr = rb_entry(prevnd, struct symbol, rb_node); | ||
| 87 | |||
| 88 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | ||
| 89 | prev = curr; | ||
| 90 | curr = rb_entry(nd, struct symbol, rb_node); | ||
| 91 | |||
| 92 | if (prev->end == prev->start) | ||
| 93 | prev->end = curr->start - 1; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* Last entry */ | ||
| 97 | if (curr->end == curr->start) | ||
| 98 | curr->end = roundup(curr->start, 4096); | ||
| 99 | } | ||
| 33 | 100 | ||
| 34 | if (v >= 2) | 101 | static void __map_groups__fixup_end(struct map_groups *self, enum map_type type) |
| 35 | printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n", | 102 | { |
| 36 | (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start); | 103 | struct map *prev, *curr; |
| 104 | struct rb_node *nd, *prevnd = rb_first(&self->maps[type]); | ||
| 37 | 105 | ||
| 38 | self->obj_start= obj_start; | 106 | if (prevnd == NULL) |
| 39 | self->hist = NULL; | 107 | return; |
| 40 | self->hist_sum = 0; | ||
| 41 | 108 | ||
| 42 | if (sym_hist_filter && !strcmp(name, sym_hist_filter)) | 109 | curr = rb_entry(prevnd, struct map, rb_node); |
| 43 | self->hist = calloc(sizeof(u64), len); | ||
| 44 | 110 | ||
| 45 | if (priv_size) { | 111 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { |
| 46 | memset(self, 0, priv_size); | 112 | prev = curr; |
| 47 | self = ((void *)self) + priv_size; | 113 | curr = rb_entry(nd, struct map, rb_node); |
| 114 | prev->end = curr->start - 1; | ||
| 48 | } | 115 | } |
| 116 | |||
| 117 | /* | ||
| 118 | * We still haven't the actual symbols, so guess the | ||
| 119 | * last map final address. | ||
| 120 | */ | ||
| 121 | curr->end = ~0UL; | ||
| 122 | } | ||
| 123 | |||
| 124 | static void map_groups__fixup_end(struct map_groups *self) | ||
| 125 | { | ||
| 126 | int i; | ||
| 127 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
| 128 | __map_groups__fixup_end(self, i); | ||
| 129 | } | ||
| 130 | |||
| 131 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) | ||
| 132 | { | ||
| 133 | size_t namelen = strlen(name) + 1; | ||
| 134 | struct symbol *self = zalloc(symbol_conf.priv_size + | ||
| 135 | sizeof(*self) + namelen); | ||
| 136 | if (self == NULL) | ||
| 137 | return NULL; | ||
| 138 | |||
| 139 | if (symbol_conf.priv_size) | ||
| 140 | self = ((void *)self) + symbol_conf.priv_size; | ||
| 141 | |||
| 49 | self->start = start; | 142 | self->start = start; |
| 50 | self->end = len ? start + len - 1 : start; | 143 | self->end = len ? start + len - 1 : start; |
| 144 | |||
| 145 | pr_debug3("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); | ||
| 146 | |||
| 51 | memcpy(self->name, name, namelen); | 147 | memcpy(self->name, name, namelen); |
| 52 | 148 | ||
| 53 | return self; | 149 | return self; |
| 54 | } | 150 | } |
| 55 | 151 | ||
| 56 | static void symbol__delete(struct symbol *self, unsigned int priv_size) | 152 | static void symbol__delete(struct symbol *self) |
| 57 | { | 153 | { |
| 58 | free(((void *)self) - priv_size); | 154 | free(((void *)self) - symbol_conf.priv_size); |
| 59 | } | 155 | } |
| 60 | 156 | ||
| 61 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | 157 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) |
| 62 | { | 158 | { |
| 63 | if (!self->module) | 159 | return fprintf(fp, " %llx-%llx %s\n", |
| 64 | return fprintf(fp, " %llx-%llx %s\n", | ||
| 65 | self->start, self->end, self->name); | 160 | self->start, self->end, self->name); |
| 66 | else | ||
| 67 | return fprintf(fp, " %llx-%llx %s \t[%s]\n", | ||
| 68 | self->start, self->end, self->name, self->module->name); | ||
| 69 | } | 161 | } |
| 70 | 162 | ||
| 71 | struct dso *dso__new(const char *name, unsigned int sym_priv_size) | 163 | static void dso__set_long_name(struct dso *self, char *name) |
| 164 | { | ||
| 165 | if (name == NULL) | ||
| 166 | return; | ||
| 167 | self->long_name = name; | ||
| 168 | self->long_name_len = strlen(name); | ||
| 169 | } | ||
| 170 | |||
| 171 | static void dso__set_basename(struct dso *self) | ||
| 172 | { | ||
| 173 | self->short_name = basename(self->long_name); | ||
| 174 | } | ||
| 175 | |||
| 176 | struct dso *dso__new(const char *name) | ||
| 72 | { | 177 | { |
| 73 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | 178 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); |
| 74 | 179 | ||
| 75 | if (self != NULL) { | 180 | if (self != NULL) { |
| 181 | int i; | ||
| 76 | strcpy(self->name, name); | 182 | strcpy(self->name, name); |
| 77 | self->syms = RB_ROOT; | 183 | dso__set_long_name(self, self->name); |
| 78 | self->sym_priv_size = sym_priv_size; | 184 | self->short_name = self->name; |
| 79 | self->find_symbol = dso__find_symbol; | 185 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 186 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; | ||
| 80 | self->slen_calculated = 0; | 187 | self->slen_calculated = 0; |
| 81 | self->origin = DSO__ORIG_NOT_FOUND; | 188 | self->origin = DSO__ORIG_NOT_FOUND; |
| 189 | self->loaded = 0; | ||
| 190 | self->sorted_by_name = 0; | ||
| 191 | self->has_build_id = 0; | ||
| 82 | } | 192 | } |
| 83 | 193 | ||
| 84 | return self; | 194 | return self; |
| 85 | } | 195 | } |
| 86 | 196 | ||
| 87 | static void dso__delete_symbols(struct dso *self) | 197 | static void symbols__delete(struct rb_root *self) |
| 88 | { | 198 | { |
| 89 | struct symbol *pos; | 199 | struct symbol *pos; |
| 90 | struct rb_node *next = rb_first(&self->syms); | 200 | struct rb_node *next = rb_first(self); |
| 91 | 201 | ||
| 92 | while (next) { | 202 | while (next) { |
| 93 | pos = rb_entry(next, struct symbol, rb_node); | 203 | pos = rb_entry(next, struct symbol, rb_node); |
| 94 | next = rb_next(&pos->rb_node); | 204 | next = rb_next(&pos->rb_node); |
| 95 | rb_erase(&pos->rb_node, &self->syms); | 205 | rb_erase(&pos->rb_node, self); |
| 96 | symbol__delete(pos, self->sym_priv_size); | 206 | symbol__delete(pos); |
| 97 | } | 207 | } |
| 98 | } | 208 | } |
| 99 | 209 | ||
| 100 | void dso__delete(struct dso *self) | 210 | void dso__delete(struct dso *self) |
| 101 | { | 211 | { |
| 102 | dso__delete_symbols(self); | 212 | int i; |
| 213 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
| 214 | symbols__delete(&self->symbols[i]); | ||
| 215 | if (self->long_name != self->name) | ||
| 216 | free(self->long_name); | ||
| 103 | free(self); | 217 | free(self); |
| 104 | } | 218 | } |
| 105 | 219 | ||
| 106 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) | 220 | void dso__set_build_id(struct dso *self, void *build_id) |
| 107 | { | 221 | { |
| 108 | struct rb_node **p = &self->syms.rb_node; | 222 | memcpy(self->build_id, build_id, sizeof(self->build_id)); |
| 223 | self->has_build_id = 1; | ||
| 224 | } | ||
| 225 | |||
| 226 | static void symbols__insert(struct rb_root *self, struct symbol *sym) | ||
| 227 | { | ||
| 228 | struct rb_node **p = &self->rb_node; | ||
| 109 | struct rb_node *parent = NULL; | 229 | struct rb_node *parent = NULL; |
| 110 | const u64 ip = sym->start; | 230 | const u64 ip = sym->start; |
| 111 | struct symbol *s; | 231 | struct symbol *s; |
| @@ -119,17 +239,17 @@ static void dso__insert_symbol(struct dso *self, struct symbol *sym) | |||
| 119 | p = &(*p)->rb_right; | 239 | p = &(*p)->rb_right; |
| 120 | } | 240 | } |
| 121 | rb_link_node(&sym->rb_node, parent, p); | 241 | rb_link_node(&sym->rb_node, parent, p); |
| 122 | rb_insert_color(&sym->rb_node, &self->syms); | 242 | rb_insert_color(&sym->rb_node, self); |
| 123 | } | 243 | } |
| 124 | 244 | ||
| 125 | struct symbol *dso__find_symbol(struct dso *self, u64 ip) | 245 | static struct symbol *symbols__find(struct rb_root *self, u64 ip) |
| 126 | { | 246 | { |
| 127 | struct rb_node *n; | 247 | struct rb_node *n; |
| 128 | 248 | ||
| 129 | if (self == NULL) | 249 | if (self == NULL) |
| 130 | return NULL; | 250 | return NULL; |
| 131 | 251 | ||
| 132 | n = self->syms.rb_node; | 252 | n = self->rb_node; |
| 133 | 253 | ||
| 134 | while (n) { | 254 | while (n) { |
| 135 | struct symbol *s = rb_entry(n, struct symbol, rb_node); | 255 | struct symbol *s = rb_entry(n, struct symbol, rb_node); |
| @@ -145,12 +265,116 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip) | |||
| 145 | return NULL; | 265 | return NULL; |
| 146 | } | 266 | } |
| 147 | 267 | ||
| 148 | size_t dso__fprintf(struct dso *self, FILE *fp) | 268 | struct symbol_name_rb_node { |
| 269 | struct rb_node rb_node; | ||
| 270 | struct symbol sym; | ||
| 271 | }; | ||
| 272 | |||
| 273 | static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym) | ||
| 149 | { | 274 | { |
| 150 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | 275 | struct rb_node **p = &self->rb_node; |
| 276 | struct rb_node *parent = NULL; | ||
| 277 | struct symbol_name_rb_node *symn = ((void *)sym) - sizeof(*parent), *s; | ||
| 151 | 278 | ||
| 279 | while (*p != NULL) { | ||
| 280 | parent = *p; | ||
| 281 | s = rb_entry(parent, struct symbol_name_rb_node, rb_node); | ||
| 282 | if (strcmp(sym->name, s->sym.name) < 0) | ||
| 283 | p = &(*p)->rb_left; | ||
| 284 | else | ||
| 285 | p = &(*p)->rb_right; | ||
| 286 | } | ||
| 287 | rb_link_node(&symn->rb_node, parent, p); | ||
| 288 | rb_insert_color(&symn->rb_node, self); | ||
| 289 | } | ||
| 290 | |||
| 291 | static void symbols__sort_by_name(struct rb_root *self, struct rb_root *source) | ||
| 292 | { | ||
| 152 | struct rb_node *nd; | 293 | struct rb_node *nd; |
| 153 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | 294 | |
| 295 | for (nd = rb_first(source); nd; nd = rb_next(nd)) { | ||
| 296 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
| 297 | symbols__insert_by_name(self, pos); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | static struct symbol *symbols__find_by_name(struct rb_root *self, const char *name) | ||
| 302 | { | ||
| 303 | struct rb_node *n; | ||
| 304 | |||
| 305 | if (self == NULL) | ||
| 306 | return NULL; | ||
| 307 | |||
| 308 | n = self->rb_node; | ||
| 309 | |||
| 310 | while (n) { | ||
| 311 | struct symbol_name_rb_node *s; | ||
| 312 | int cmp; | ||
| 313 | |||
| 314 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); | ||
| 315 | cmp = strcmp(name, s->sym.name); | ||
| 316 | |||
| 317 | if (cmp < 0) | ||
| 318 | n = n->rb_left; | ||
| 319 | else if (cmp > 0) | ||
| 320 | n = n->rb_right; | ||
| 321 | else | ||
| 322 | return &s->sym; | ||
| 323 | } | ||
| 324 | |||
| 325 | return NULL; | ||
| 326 | } | ||
| 327 | |||
| 328 | struct symbol *dso__find_symbol(struct dso *self, | ||
| 329 | enum map_type type, u64 addr) | ||
| 330 | { | ||
| 331 | return symbols__find(&self->symbols[type], addr); | ||
| 332 | } | ||
| 333 | |||
| 334 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | ||
| 335 | const char *name) | ||
| 336 | { | ||
| 337 | return symbols__find_by_name(&self->symbol_names[type], name); | ||
| 338 | } | ||
| 339 | |||
| 340 | void dso__sort_by_name(struct dso *self, enum map_type type) | ||
| 341 | { | ||
| 342 | dso__set_sorted_by_name(self, type); | ||
| 343 | return symbols__sort_by_name(&self->symbol_names[type], | ||
| 344 | &self->symbols[type]); | ||
| 345 | } | ||
| 346 | |||
| 347 | int build_id__sprintf(u8 *self, int len, char *bf) | ||
| 348 | { | ||
| 349 | char *bid = bf; | ||
| 350 | u8 *raw = self; | ||
| 351 | int i; | ||
| 352 | |||
| 353 | for (i = 0; i < len; ++i) { | ||
| 354 | sprintf(bid, "%02x", *raw); | ||
| 355 | ++raw; | ||
| 356 | bid += 2; | ||
| 357 | } | ||
| 358 | |||
| 359 | return raw - self; | ||
| 360 | } | ||
| 361 | |||
| 362 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp) | ||
| 363 | { | ||
| 364 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
| 365 | |||
| 366 | build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id); | ||
| 367 | return fprintf(fp, "%s", sbuild_id); | ||
| 368 | } | ||
| 369 | |||
| 370 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) | ||
| 371 | { | ||
| 372 | struct rb_node *nd; | ||
| 373 | size_t ret = fprintf(fp, "dso: %s (", self->short_name); | ||
| 374 | |||
| 375 | ret += dso__fprintf_buildid(self, fp); | ||
| 376 | ret += fprintf(fp, ")\n"); | ||
| 377 | for (nd = rb_first(&self->symbols[type]); nd; nd = rb_next(nd)) { | ||
| 154 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 378 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); |
| 155 | ret += symbol__fprintf(pos, fp); | 379 | ret += symbol__fprintf(pos, fp); |
| 156 | } | 380 | } |
| @@ -158,13 +382,17 @@ size_t dso__fprintf(struct dso *self, FILE *fp) | |||
| 158 | return ret; | 382 | return ret; |
| 159 | } | 383 | } |
| 160 | 384 | ||
| 161 | static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v) | 385 | /* |
| 386 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, | ||
| 387 | * so that we can in the next step set the symbol ->end address and then | ||
| 388 | * call kernel_maps__split_kallsyms. | ||
| 389 | */ | ||
| 390 | static int dso__load_all_kallsyms(struct dso *self, struct map *map) | ||
| 162 | { | 391 | { |
| 163 | struct rb_node *nd, *prevnd; | ||
| 164 | char *line = NULL; | 392 | char *line = NULL; |
| 165 | size_t n; | 393 | size_t n; |
| 394 | struct rb_root *root = &self->symbols[map->type]; | ||
| 166 | FILE *file = fopen("/proc/kallsyms", "r"); | 395 | FILE *file = fopen("/proc/kallsyms", "r"); |
| 167 | int count = 0; | ||
| 168 | 396 | ||
| 169 | if (file == NULL) | 397 | if (file == NULL) |
| 170 | goto out_failure; | 398 | goto out_failure; |
| @@ -174,6 +402,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v) | |||
| 174 | struct symbol *sym; | 402 | struct symbol *sym; |
| 175 | int line_len, len; | 403 | int line_len, len; |
| 176 | char symbol_type; | 404 | char symbol_type; |
| 405 | char *symbol_name; | ||
| 177 | 406 | ||
| 178 | line_len = getline(&line, &n, file); | 407 | line_len = getline(&line, &n, file); |
| 179 | if (line_len < 0) | 408 | if (line_len < 0) |
| @@ -191,49 +420,28 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v) | |||
| 191 | continue; | 420 | continue; |
| 192 | 421 | ||
| 193 | symbol_type = toupper(line[len]); | 422 | symbol_type = toupper(line[len]); |
| 194 | /* | 423 | if (!symbol_type__is_a(symbol_type, map->type)) |
| 195 | * We're interested only in code ('T'ext) | ||
| 196 | */ | ||
| 197 | if (symbol_type != 'T' && symbol_type != 'W') | ||
| 198 | continue; | 424 | continue; |
| 425 | |||
| 426 | symbol_name = line + len + 2; | ||
| 199 | /* | 427 | /* |
| 200 | * Well fix up the end later, when we have all sorted. | 428 | * Will fix up the end later, when we have all symbols sorted. |
| 201 | */ | 429 | */ |
| 202 | sym = symbol__new(start, 0xdead, line + len + 2, | 430 | sym = symbol__new(start, 0, symbol_name); |
| 203 | self->sym_priv_size, 0, v); | ||
| 204 | 431 | ||
| 205 | if (sym == NULL) | 432 | if (sym == NULL) |
| 206 | goto out_delete_line; | 433 | goto out_delete_line; |
| 207 | 434 | /* | |
| 208 | if (filter && filter(self, sym)) | 435 | * We will pass the symbols to the filter later, in |
| 209 | symbol__delete(sym, self->sym_priv_size); | 436 | * map__split_kallsyms, when we have split the maps per module |
| 210 | else { | 437 | */ |
| 211 | dso__insert_symbol(self, sym); | 438 | symbols__insert(root, sym); |
| 212 | count++; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | /* | ||
| 217 | * Now that we have all sorted out, just set the ->end of all | ||
| 218 | * symbols | ||
| 219 | */ | ||
| 220 | prevnd = rb_first(&self->syms); | ||
| 221 | |||
| 222 | if (prevnd == NULL) | ||
| 223 | goto out_delete_line; | ||
| 224 | |||
| 225 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | ||
| 226 | struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node), | ||
| 227 | *curr = rb_entry(nd, struct symbol, rb_node); | ||
| 228 | |||
| 229 | prev->end = curr->start - 1; | ||
| 230 | prevnd = nd; | ||
| 231 | } | 439 | } |
| 232 | 440 | ||
| 233 | free(line); | 441 | free(line); |
| 234 | fclose(file); | 442 | fclose(file); |
| 235 | 443 | ||
| 236 | return count; | 444 | return 0; |
| 237 | 445 | ||
| 238 | out_delete_line: | 446 | out_delete_line: |
| 239 | free(line); | 447 | free(line); |
| @@ -241,14 +449,107 @@ out_failure: | |||
| 241 | return -1; | 449 | return -1; |
| 242 | } | 450 | } |
| 243 | 451 | ||
| 244 | static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v) | 452 | /* |
| 453 | * Split the symbols into maps, making sure there are no overlaps, i.e. the | ||
| 454 | * kernel range is broken in several maps, named [kernel].N, as we don't have | ||
| 455 | * the original ELF section names vmlinux have. | ||
| 456 | */ | ||
| 457 | static int dso__split_kallsyms(struct dso *self, struct map *map, | ||
| 458 | struct perf_session *session, symbol_filter_t filter) | ||
| 459 | { | ||
| 460 | struct map *curr_map = map; | ||
| 461 | struct symbol *pos; | ||
| 462 | int count = 0; | ||
| 463 | struct rb_root *root = &self->symbols[map->type]; | ||
| 464 | struct rb_node *next = rb_first(root); | ||
| 465 | int kernel_range = 0; | ||
| 466 | |||
| 467 | while (next) { | ||
| 468 | char *module; | ||
| 469 | |||
| 470 | pos = rb_entry(next, struct symbol, rb_node); | ||
| 471 | next = rb_next(&pos->rb_node); | ||
| 472 | |||
| 473 | module = strchr(pos->name, '\t'); | ||
| 474 | if (module) { | ||
| 475 | if (!symbol_conf.use_modules) | ||
| 476 | goto discard_symbol; | ||
| 477 | |||
| 478 | *module++ = '\0'; | ||
| 479 | |||
| 480 | if (strcmp(self->name, module)) { | ||
| 481 | curr_map = map_groups__find_by_name(&session->kmaps, map->type, module); | ||
| 482 | if (curr_map == NULL) { | ||
| 483 | pr_debug("/proc/{kallsyms,modules} " | ||
| 484 | "inconsistency!\n"); | ||
| 485 | return -1; | ||
| 486 | } | ||
| 487 | } | ||
| 488 | /* | ||
| 489 | * So that we look just like we get from .ko files, | ||
| 490 | * i.e. not prelinked, relative to map->start. | ||
| 491 | */ | ||
| 492 | pos->start = curr_map->map_ip(curr_map, pos->start); | ||
| 493 | pos->end = curr_map->map_ip(curr_map, pos->end); | ||
| 494 | } else if (curr_map != map) { | ||
| 495 | char dso_name[PATH_MAX]; | ||
| 496 | struct dso *dso; | ||
| 497 | |||
| 498 | snprintf(dso_name, sizeof(dso_name), "[kernel].%d", | ||
| 499 | kernel_range++); | ||
| 500 | |||
| 501 | dso = dso__new(dso_name); | ||
| 502 | if (dso == NULL) | ||
| 503 | return -1; | ||
| 504 | |||
| 505 | curr_map = map__new2(pos->start, dso, map->type); | ||
| 506 | if (map == NULL) { | ||
| 507 | dso__delete(dso); | ||
| 508 | return -1; | ||
| 509 | } | ||
| 510 | |||
| 511 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; | ||
| 512 | map_groups__insert(&session->kmaps, curr_map); | ||
| 513 | ++kernel_range; | ||
| 514 | } | ||
| 515 | |||
| 516 | if (filter && filter(curr_map, pos)) { | ||
| 517 | discard_symbol: rb_erase(&pos->rb_node, root); | ||
| 518 | symbol__delete(pos); | ||
| 519 | } else { | ||
| 520 | if (curr_map != map) { | ||
| 521 | rb_erase(&pos->rb_node, root); | ||
| 522 | symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); | ||
| 523 | } | ||
| 524 | count++; | ||
| 525 | } | ||
| 526 | } | ||
| 527 | |||
| 528 | return count; | ||
| 529 | } | ||
| 530 | |||
| 531 | |||
| 532 | static int dso__load_kallsyms(struct dso *self, struct map *map, | ||
| 533 | struct perf_session *session, symbol_filter_t filter) | ||
| 534 | { | ||
| 535 | if (dso__load_all_kallsyms(self, map) < 0) | ||
| 536 | return -1; | ||
| 537 | |||
| 538 | symbols__fixup_end(&self->symbols[map->type]); | ||
| 539 | self->origin = DSO__ORIG_KERNEL; | ||
| 540 | |||
| 541 | return dso__split_kallsyms(self, map, session, filter); | ||
| 542 | } | ||
| 543 | |||
| 544 | static int dso__load_perf_map(struct dso *self, struct map *map, | ||
| 545 | symbol_filter_t filter) | ||
| 245 | { | 546 | { |
| 246 | char *line = NULL; | 547 | char *line = NULL; |
| 247 | size_t n; | 548 | size_t n; |
| 248 | FILE *file; | 549 | FILE *file; |
| 249 | int nr_syms = 0; | 550 | int nr_syms = 0; |
| 250 | 551 | ||
| 251 | file = fopen(self->name, "r"); | 552 | file = fopen(self->long_name, "r"); |
| 252 | if (file == NULL) | 553 | if (file == NULL) |
| 253 | goto out_failure; | 554 | goto out_failure; |
| 254 | 555 | ||
| @@ -278,16 +579,15 @@ static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v) | |||
| 278 | if (len + 2 >= line_len) | 579 | if (len + 2 >= line_len) |
| 279 | continue; | 580 | continue; |
| 280 | 581 | ||
| 281 | sym = symbol__new(start, size, line + len, | 582 | sym = symbol__new(start, size, line + len); |
| 282 | self->sym_priv_size, start, v); | ||
| 283 | 583 | ||
| 284 | if (sym == NULL) | 584 | if (sym == NULL) |
| 285 | goto out_delete_line; | 585 | goto out_delete_line; |
| 286 | 586 | ||
| 287 | if (filter && filter(self, sym)) | 587 | if (filter && filter(map, sym)) |
| 288 | symbol__delete(sym, self->sym_priv_size); | 588 | symbol__delete(sym); |
| 289 | else { | 589 | else { |
| 290 | dso__insert_symbol(self, sym); | 590 | symbols__insert(&self->symbols[map->type], sym); |
| 291 | nr_syms++; | 591 | nr_syms++; |
| 292 | } | 592 | } |
| 293 | } | 593 | } |
| @@ -327,6 +627,13 @@ static inline int elf_sym__is_function(const GElf_Sym *sym) | |||
| 327 | sym->st_shndx != SHN_UNDEF; | 627 | sym->st_shndx != SHN_UNDEF; |
| 328 | } | 628 | } |
| 329 | 629 | ||
| 630 | static inline bool elf_sym__is_object(const GElf_Sym *sym) | ||
| 631 | { | ||
| 632 | return elf_sym__type(sym) == STT_OBJECT && | ||
| 633 | sym->st_name != 0 && | ||
| 634 | sym->st_shndx != SHN_UNDEF; | ||
| 635 | } | ||
| 636 | |||
| 330 | static inline int elf_sym__is_label(const GElf_Sym *sym) | 637 | static inline int elf_sym__is_label(const GElf_Sym *sym) |
| 331 | { | 638 | { |
| 332 | return elf_sym__type(sym) == STT_NOTYPE && | 639 | return elf_sym__type(sym) == STT_NOTYPE && |
| @@ -347,6 +654,12 @@ static inline int elf_sec__is_text(const GElf_Shdr *shdr, | |||
| 347 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | 654 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; |
| 348 | } | 655 | } |
| 349 | 656 | ||
| 657 | static inline bool elf_sec__is_data(const GElf_Shdr *shdr, | ||
| 658 | const Elf_Data *secstrs) | ||
| 659 | { | ||
| 660 | return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; | ||
| 661 | } | ||
| 662 | |||
| 350 | static inline const char *elf_sym__name(const GElf_Sym *sym, | 663 | static inline const char *elf_sym__name(const GElf_Sym *sym, |
| 351 | const Elf_Data *symstrs) | 664 | const Elf_Data *symstrs) |
| 352 | { | 665 | { |
| @@ -393,7 +706,8 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
| 393 | * And always look at the original dso, not at debuginfo packages, that | 706 | * And always look at the original dso, not at debuginfo packages, that |
| 394 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | 707 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). |
| 395 | */ | 708 | */ |
| 396 | static int dso__synthesize_plt_symbols(struct dso *self, int v) | 709 | static int dso__synthesize_plt_symbols(struct dso *self, struct map *map, |
| 710 | symbol_filter_t filter) | ||
| 397 | { | 711 | { |
| 398 | uint32_t nr_rel_entries, idx; | 712 | uint32_t nr_rel_entries, idx; |
| 399 | GElf_Sym sym; | 713 | GElf_Sym sym; |
| @@ -409,7 +723,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
| 409 | Elf *elf; | 723 | Elf *elf; |
| 410 | int nr = 0, symidx, fd, err = 0; | 724 | int nr = 0, symidx, fd, err = 0; |
| 411 | 725 | ||
| 412 | fd = open(self->name, O_RDONLY); | 726 | fd = open(self->long_name, O_RDONLY); |
| 413 | if (fd < 0) | 727 | if (fd < 0) |
| 414 | goto out; | 728 | goto out; |
| 415 | 729 | ||
| @@ -477,12 +791,16 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
| 477 | "%s@plt", elf_sym__name(&sym, symstrs)); | 791 | "%s@plt", elf_sym__name(&sym, symstrs)); |
| 478 | 792 | ||
| 479 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 793 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
| 480 | sympltname, self->sym_priv_size, 0, v); | 794 | sympltname); |
| 481 | if (!f) | 795 | if (!f) |
| 482 | goto out_elf_end; | 796 | goto out_elf_end; |
| 483 | 797 | ||
| 484 | dso__insert_symbol(self, f); | 798 | if (filter && filter(map, f)) |
| 485 | ++nr; | 799 | symbol__delete(f); |
| 800 | else { | ||
| 801 | symbols__insert(&self->symbols[map->type], f); | ||
| 802 | ++nr; | ||
| 803 | } | ||
| 486 | } | 804 | } |
| 487 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | 805 | } else if (shdr_rel_plt.sh_type == SHT_REL) { |
| 488 | GElf_Rel pos_mem, *pos; | 806 | GElf_Rel pos_mem, *pos; |
| @@ -495,12 +813,16 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
| 495 | "%s@plt", elf_sym__name(&sym, symstrs)); | 813 | "%s@plt", elf_sym__name(&sym, symstrs)); |
| 496 | 814 | ||
| 497 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 815 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
| 498 | sympltname, self->sym_priv_size, 0, v); | 816 | sympltname); |
| 499 | if (!f) | 817 | if (!f) |
| 500 | goto out_elf_end; | 818 | goto out_elf_end; |
| 501 | 819 | ||
| 502 | dso__insert_symbol(self, f); | 820 | if (filter && filter(map, f)) |
| 503 | ++nr; | 821 | symbol__delete(f); |
| 822 | else { | ||
| 823 | symbols__insert(&self->symbols[map->type], f); | ||
| 824 | ++nr; | ||
| 825 | } | ||
| 504 | } | 826 | } |
| 505 | } | 827 | } |
| 506 | 828 | ||
| @@ -513,14 +835,42 @@ out_close: | |||
| 513 | if (err == 0) | 835 | if (err == 0) |
| 514 | return nr; | 836 | return nr; |
| 515 | out: | 837 | out: |
| 516 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | 838 | pr_warning("%s: problems reading %s PLT info.\n", |
| 517 | __func__, self->name); | 839 | __func__, self->long_name); |
| 518 | return 0; | 840 | return 0; |
| 519 | } | 841 | } |
| 520 | 842 | ||
| 521 | static int dso__load_sym(struct dso *self, int fd, const char *name, | 843 | static bool elf_sym__is_a(GElf_Sym *self, enum map_type type) |
| 522 | symbol_filter_t filter, int v, struct module *mod) | ||
| 523 | { | 844 | { |
| 845 | switch (type) { | ||
| 846 | case MAP__FUNCTION: | ||
| 847 | return elf_sym__is_function(self); | ||
| 848 | case MAP__VARIABLE: | ||
| 849 | return elf_sym__is_object(self); | ||
| 850 | default: | ||
| 851 | return false; | ||
| 852 | } | ||
| 853 | } | ||
| 854 | |||
| 855 | static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type) | ||
| 856 | { | ||
| 857 | switch (type) { | ||
| 858 | case MAP__FUNCTION: | ||
| 859 | return elf_sec__is_text(self, secstrs); | ||
| 860 | case MAP__VARIABLE: | ||
| 861 | return elf_sec__is_data(self, secstrs); | ||
| 862 | default: | ||
| 863 | return false; | ||
| 864 | } | ||
| 865 | } | ||
| 866 | |||
| 867 | static int dso__load_sym(struct dso *self, struct map *map, | ||
| 868 | struct perf_session *session, const char *name, int fd, | ||
| 869 | symbol_filter_t filter, int kernel, int kmodule) | ||
| 870 | { | ||
| 871 | struct map *curr_map = map; | ||
| 872 | struct dso *curr_dso = self; | ||
| 873 | size_t dso_name_len = strlen(self->short_name); | ||
| 524 | Elf_Data *symstrs, *secstrs; | 874 | Elf_Data *symstrs, *secstrs; |
| 525 | uint32_t nr_syms; | 875 | uint32_t nr_syms; |
| 526 | int err = -1; | 876 | int err = -1; |
| @@ -531,19 +881,16 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 531 | GElf_Sym sym; | 881 | GElf_Sym sym; |
| 532 | Elf_Scn *sec, *sec_strndx; | 882 | Elf_Scn *sec, *sec_strndx; |
| 533 | Elf *elf; | 883 | Elf *elf; |
| 534 | int nr = 0, kernel = !strcmp("[kernel]", self->name); | 884 | int nr = 0; |
| 535 | 885 | ||
| 536 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 886 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
| 537 | if (elf == NULL) { | 887 | if (elf == NULL) { |
| 538 | if (v) | 888 | pr_err("%s: cannot read %s ELF file.\n", __func__, name); |
| 539 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
| 540 | __func__, name); | ||
| 541 | goto out_close; | 889 | goto out_close; |
| 542 | } | 890 | } |
| 543 | 891 | ||
| 544 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 892 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
| 545 | if (v) | 893 | pr_err("%s: cannot get elf header.\n", __func__); |
| 546 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
| 547 | goto out_elf_end; | 894 | goto out_elf_end; |
| 548 | } | 895 | } |
| 549 | 896 | ||
| @@ -587,13 +934,11 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 587 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | 934 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { |
| 588 | struct symbol *f; | 935 | struct symbol *f; |
| 589 | const char *elf_name; | 936 | const char *elf_name; |
| 590 | char *demangled; | 937 | char *demangled = NULL; |
| 591 | u64 obj_start; | ||
| 592 | struct section *section = NULL; | ||
| 593 | int is_label = elf_sym__is_label(&sym); | 938 | int is_label = elf_sym__is_label(&sym); |
| 594 | const char *section_name; | 939 | const char *section_name; |
| 595 | 940 | ||
| 596 | if (!is_label && !elf_sym__is_function(&sym)) | 941 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
| 597 | continue; | 942 | continue; |
| 598 | 943 | ||
| 599 | sec = elf_getscn(elf, sym.st_shndx); | 944 | sec = elf_getscn(elf, sym.st_shndx); |
| @@ -602,55 +947,88 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 602 | 947 | ||
| 603 | gelf_getshdr(sec, &shdr); | 948 | gelf_getshdr(sec, &shdr); |
| 604 | 949 | ||
| 605 | if (is_label && !elf_sec__is_text(&shdr, secstrs)) | 950 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) |
| 606 | continue; | 951 | continue; |
| 607 | 952 | ||
| 953 | elf_name = elf_sym__name(&sym, symstrs); | ||
| 608 | section_name = elf_sec__name(&shdr, secstrs); | 954 | section_name = elf_sec__name(&shdr, secstrs); |
| 609 | obj_start = sym.st_value; | ||
| 610 | 955 | ||
| 611 | if (self->adjust_symbols) { | 956 | if (kernel || kmodule) { |
| 612 | if (v >= 2) | 957 | char dso_name[PATH_MAX]; |
| 613 | printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n", | ||
| 614 | (u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset); | ||
| 615 | 958 | ||
| 616 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | 959 | if (strcmp(section_name, |
| 617 | } | 960 | curr_dso->short_name + dso_name_len) == 0) |
| 961 | goto new_symbol; | ||
| 618 | 962 | ||
| 619 | if (mod) { | 963 | if (strcmp(section_name, ".text") == 0) { |
| 620 | section = mod->sections->find_section(mod->sections, section_name); | 964 | curr_map = map; |
| 621 | if (section) | 965 | curr_dso = self; |
| 622 | sym.st_value += section->vma; | 966 | goto new_symbol; |
| 623 | else { | ||
| 624 | fprintf(stderr, "dso__load_sym() module %s lookup of %s failed\n", | ||
| 625 | mod->name, section_name); | ||
| 626 | goto out_elf_end; | ||
| 627 | } | 967 | } |
| 968 | |||
| 969 | snprintf(dso_name, sizeof(dso_name), | ||
| 970 | "%s%s", self->short_name, section_name); | ||
| 971 | |||
| 972 | curr_map = map_groups__find_by_name(&session->kmaps, map->type, dso_name); | ||
| 973 | if (curr_map == NULL) { | ||
| 974 | u64 start = sym.st_value; | ||
| 975 | |||
| 976 | if (kmodule) | ||
| 977 | start += map->start + shdr.sh_offset; | ||
| 978 | |||
| 979 | curr_dso = dso__new(dso_name); | ||
| 980 | if (curr_dso == NULL) | ||
| 981 | goto out_elf_end; | ||
| 982 | curr_map = map__new2(start, curr_dso, | ||
| 983 | MAP__FUNCTION); | ||
| 984 | if (curr_map == NULL) { | ||
| 985 | dso__delete(curr_dso); | ||
| 986 | goto out_elf_end; | ||
| 987 | } | ||
| 988 | curr_map->map_ip = identity__map_ip; | ||
| 989 | curr_map->unmap_ip = identity__map_ip; | ||
| 990 | curr_dso->origin = DSO__ORIG_KERNEL; | ||
| 991 | map_groups__insert(&session->kmaps, curr_map); | ||
| 992 | dsos__add(&dsos__kernel, curr_dso); | ||
| 993 | } else | ||
| 994 | curr_dso = curr_map->dso; | ||
| 995 | |||
| 996 | goto new_symbol; | ||
| 997 | } | ||
| 998 | |||
| 999 | if (curr_dso->adjust_symbols) { | ||
| 1000 | pr_debug2("adjusting symbol: st_value: %Lx sh_addr: " | ||
| 1001 | "%Lx sh_offset: %Lx\n", (u64)sym.st_value, | ||
| 1002 | (u64)shdr.sh_addr, (u64)shdr.sh_offset); | ||
| 1003 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
| 628 | } | 1004 | } |
| 629 | /* | 1005 | /* |
| 630 | * We need to figure out if the object was created from C++ sources | 1006 | * We need to figure out if the object was created from C++ sources |
| 631 | * DWARF DW_compile_unit has this, but we don't always have access | 1007 | * DWARF DW_compile_unit has this, but we don't always have access |
| 632 | * to it... | 1008 | * to it... |
| 633 | */ | 1009 | */ |
| 634 | elf_name = elf_sym__name(&sym, symstrs); | ||
| 635 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); | 1010 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); |
| 636 | if (demangled != NULL) | 1011 | if (demangled != NULL) |
| 637 | elf_name = demangled; | 1012 | elf_name = demangled; |
| 638 | 1013 | new_symbol: | |
| 639 | f = symbol__new(sym.st_value, sym.st_size, elf_name, | 1014 | f = symbol__new(sym.st_value, sym.st_size, elf_name); |
| 640 | self->sym_priv_size, obj_start, v); | ||
| 641 | free(demangled); | 1015 | free(demangled); |
| 642 | if (!f) | 1016 | if (!f) |
| 643 | goto out_elf_end; | 1017 | goto out_elf_end; |
| 644 | 1018 | ||
| 645 | if (filter && filter(self, f)) | 1019 | if (filter && filter(curr_map, f)) |
| 646 | symbol__delete(f, self->sym_priv_size); | 1020 | symbol__delete(f); |
| 647 | else { | 1021 | else { |
| 648 | f->module = mod; | 1022 | symbols__insert(&curr_dso->symbols[curr_map->type], f); |
| 649 | dso__insert_symbol(self, f); | ||
| 650 | nr++; | 1023 | nr++; |
| 651 | } | 1024 | } |
| 652 | } | 1025 | } |
| 653 | 1026 | ||
| 1027 | /* | ||
| 1028 | * For misannotated, zeroed, ASM function sizes. | ||
| 1029 | */ | ||
| 1030 | if (nr > 0) | ||
| 1031 | symbols__fixup_end(&self->symbols[map->type]); | ||
| 654 | err = nr; | 1032 | err = nr; |
| 655 | out_elf_end: | 1033 | out_elf_end: |
| 656 | elf_end(elf); | 1034 | elf_end(elf); |
| @@ -658,63 +1036,154 @@ out_close: | |||
| 658 | return err; | 1036 | return err; |
| 659 | } | 1037 | } |
| 660 | 1038 | ||
| 661 | #define BUILD_ID_SIZE 128 | 1039 | static bool dso__build_id_equal(const struct dso *self, u8 *build_id) |
| 1040 | { | ||
| 1041 | return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; | ||
| 1042 | } | ||
| 662 | 1043 | ||
| 663 | static char *dso__read_build_id(struct dso *self, int v) | 1044 | static bool __dsos__read_build_ids(struct list_head *head) |
| 664 | { | 1045 | { |
| 665 | int i; | 1046 | bool have_build_id = false; |
| 1047 | struct dso *pos; | ||
| 1048 | |||
| 1049 | list_for_each_entry(pos, head, node) | ||
| 1050 | if (filename__read_build_id(pos->long_name, pos->build_id, | ||
| 1051 | sizeof(pos->build_id)) > 0) { | ||
| 1052 | have_build_id = true; | ||
| 1053 | pos->has_build_id = true; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | return have_build_id; | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | bool dsos__read_build_ids(void) | ||
| 1060 | { | ||
| 1061 | bool kbuildids = __dsos__read_build_ids(&dsos__kernel), | ||
| 1062 | ubuildids = __dsos__read_build_ids(&dsos__user); | ||
| 1063 | return kbuildids || ubuildids; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | /* | ||
| 1067 | * Align offset to 4 bytes as needed for note name and descriptor data. | ||
| 1068 | */ | ||
| 1069 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | ||
| 1070 | |||
| 1071 | int filename__read_build_id(const char *filename, void *bf, size_t size) | ||
| 1072 | { | ||
| 1073 | int fd, err = -1; | ||
| 666 | GElf_Ehdr ehdr; | 1074 | GElf_Ehdr ehdr; |
| 667 | GElf_Shdr shdr; | 1075 | GElf_Shdr shdr; |
| 668 | Elf_Data *build_id_data; | 1076 | Elf_Data *data; |
| 669 | Elf_Scn *sec; | 1077 | Elf_Scn *sec; |
| 670 | char *build_id = NULL, *bid; | 1078 | Elf_Kind ek; |
| 671 | unsigned char *raw; | 1079 | void *ptr; |
| 672 | Elf *elf; | 1080 | Elf *elf; |
| 673 | int fd = open(self->name, O_RDONLY); | ||
| 674 | 1081 | ||
| 1082 | if (size < BUILD_ID_SIZE) | ||
| 1083 | goto out; | ||
| 1084 | |||
| 1085 | fd = open(filename, O_RDONLY); | ||
| 675 | if (fd < 0) | 1086 | if (fd < 0) |
| 676 | goto out; | 1087 | goto out; |
| 677 | 1088 | ||
| 678 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 1089 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
| 679 | if (elf == NULL) { | 1090 | if (elf == NULL) { |
| 680 | if (v) | 1091 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); |
| 681 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
| 682 | __func__, self->name); | ||
| 683 | goto out_close; | 1092 | goto out_close; |
| 684 | } | 1093 | } |
| 685 | 1094 | ||
| 1095 | ek = elf_kind(elf); | ||
| 1096 | if (ek != ELF_K_ELF) | ||
| 1097 | goto out_elf_end; | ||
| 1098 | |||
| 686 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 1099 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
| 687 | if (v) | 1100 | pr_err("%s: cannot get elf header.\n", __func__); |
| 688 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
| 689 | goto out_elf_end; | 1101 | goto out_elf_end; |
| 690 | } | 1102 | } |
| 691 | 1103 | ||
| 692 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); | 1104 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
| 693 | if (sec == NULL) | 1105 | ".note.gnu.build-id", NULL); |
| 694 | goto out_elf_end; | 1106 | if (sec == NULL) { |
| 1107 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
| 1108 | ".notes", NULL); | ||
| 1109 | if (sec == NULL) | ||
| 1110 | goto out_elf_end; | ||
| 1111 | } | ||
| 695 | 1112 | ||
| 696 | build_id_data = elf_getdata(sec, NULL); | 1113 | data = elf_getdata(sec, NULL); |
| 697 | if (build_id_data == NULL) | 1114 | if (data == NULL) |
| 698 | goto out_elf_end; | ||
| 699 | build_id = malloc(BUILD_ID_SIZE); | ||
| 700 | if (build_id == NULL) | ||
| 701 | goto out_elf_end; | 1115 | goto out_elf_end; |
| 702 | raw = build_id_data->d_buf + 16; | ||
| 703 | bid = build_id; | ||
| 704 | 1116 | ||
| 705 | for (i = 0; i < 20; ++i) { | 1117 | ptr = data->d_buf; |
| 706 | sprintf(bid, "%02x", *raw); | 1118 | while (ptr < (data->d_buf + data->d_size)) { |
| 707 | ++raw; | 1119 | GElf_Nhdr *nhdr = ptr; |
| 708 | bid += 2; | 1120 | int namesz = NOTE_ALIGN(nhdr->n_namesz), |
| 1121 | descsz = NOTE_ALIGN(nhdr->n_descsz); | ||
| 1122 | const char *name; | ||
| 1123 | |||
| 1124 | ptr += sizeof(*nhdr); | ||
| 1125 | name = ptr; | ||
| 1126 | ptr += namesz; | ||
| 1127 | if (nhdr->n_type == NT_GNU_BUILD_ID && | ||
| 1128 | nhdr->n_namesz == sizeof("GNU")) { | ||
| 1129 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | ||
| 1130 | memcpy(bf, ptr, BUILD_ID_SIZE); | ||
| 1131 | err = BUILD_ID_SIZE; | ||
| 1132 | break; | ||
| 1133 | } | ||
| 1134 | } | ||
| 1135 | ptr += descsz; | ||
| 709 | } | 1136 | } |
| 710 | if (v >= 2) | ||
| 711 | printf("%s(%s): %s\n", __func__, self->name, build_id); | ||
| 712 | out_elf_end: | 1137 | out_elf_end: |
| 713 | elf_end(elf); | 1138 | elf_end(elf); |
| 714 | out_close: | 1139 | out_close: |
| 715 | close(fd); | 1140 | close(fd); |
| 716 | out: | 1141 | out: |
| 717 | return build_id; | 1142 | return err; |
| 1143 | } | ||
| 1144 | |||
| 1145 | int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | ||
| 1146 | { | ||
| 1147 | int fd, err = -1; | ||
| 1148 | |||
| 1149 | if (size < BUILD_ID_SIZE) | ||
| 1150 | goto out; | ||
| 1151 | |||
| 1152 | fd = open(filename, O_RDONLY); | ||
| 1153 | if (fd < 0) | ||
| 1154 | goto out; | ||
| 1155 | |||
| 1156 | while (1) { | ||
| 1157 | char bf[BUFSIZ]; | ||
| 1158 | GElf_Nhdr nhdr; | ||
| 1159 | int namesz, descsz; | ||
| 1160 | |||
| 1161 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) | ||
| 1162 | break; | ||
| 1163 | |||
| 1164 | namesz = NOTE_ALIGN(nhdr.n_namesz); | ||
| 1165 | descsz = NOTE_ALIGN(nhdr.n_descsz); | ||
| 1166 | if (nhdr.n_type == NT_GNU_BUILD_ID && | ||
| 1167 | nhdr.n_namesz == sizeof("GNU")) { | ||
| 1168 | if (read(fd, bf, namesz) != namesz) | ||
| 1169 | break; | ||
| 1170 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { | ||
| 1171 | if (read(fd, build_id, | ||
| 1172 | BUILD_ID_SIZE) == BUILD_ID_SIZE) { | ||
| 1173 | err = 0; | ||
| 1174 | break; | ||
| 1175 | } | ||
| 1176 | } else if (read(fd, bf, descsz) != descsz) | ||
| 1177 | break; | ||
| 1178 | } else { | ||
| 1179 | int n = namesz + descsz; | ||
| 1180 | if (read(fd, bf, n) != n) | ||
| 1181 | break; | ||
| 1182 | } | ||
| 1183 | } | ||
| 1184 | close(fd); | ||
| 1185 | out: | ||
| 1186 | return err; | ||
| 718 | } | 1187 | } |
| 719 | 1188 | ||
| 720 | char dso__symtab_origin(const struct dso *self) | 1189 | char dso__symtab_origin(const struct dso *self) |
| @@ -726,6 +1195,7 @@ char dso__symtab_origin(const struct dso *self) | |||
| 726 | [DSO__ORIG_UBUNTU] = 'u', | 1195 | [DSO__ORIG_UBUNTU] = 'u', |
| 727 | [DSO__ORIG_BUILDID] = 'b', | 1196 | [DSO__ORIG_BUILDID] = 'b', |
| 728 | [DSO__ORIG_DSO] = 'd', | 1197 | [DSO__ORIG_DSO] = 'd', |
| 1198 | [DSO__ORIG_KMODULE] = 'K', | ||
| 729 | }; | 1199 | }; |
| 730 | 1200 | ||
| 731 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | 1201 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) |
| @@ -733,20 +1203,28 @@ char dso__symtab_origin(const struct dso *self) | |||
| 733 | return origin[self->origin]; | 1203 | return origin[self->origin]; |
| 734 | } | 1204 | } |
| 735 | 1205 | ||
| 736 | int dso__load(struct dso *self, symbol_filter_t filter, int v) | 1206 | int dso__load(struct dso *self, struct map *map, struct perf_session *session, |
| 1207 | symbol_filter_t filter) | ||
| 737 | { | 1208 | { |
| 738 | int size = PATH_MAX; | 1209 | int size = PATH_MAX; |
| 739 | char *name = malloc(size), *build_id = NULL; | 1210 | char *name; |
| 1211 | u8 build_id[BUILD_ID_SIZE]; | ||
| 740 | int ret = -1; | 1212 | int ret = -1; |
| 741 | int fd; | 1213 | int fd; |
| 742 | 1214 | ||
| 1215 | dso__set_loaded(self, map->type); | ||
| 1216 | |||
| 1217 | if (self->kernel) | ||
| 1218 | return dso__load_kernel_sym(self, map, session, filter); | ||
| 1219 | |||
| 1220 | name = malloc(size); | ||
| 743 | if (!name) | 1221 | if (!name) |
| 744 | return -1; | 1222 | return -1; |
| 745 | 1223 | ||
| 746 | self->adjust_symbols = 0; | 1224 | self->adjust_symbols = 0; |
| 747 | 1225 | ||
| 748 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { | 1226 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
| 749 | ret = dso__load_perf_map(self, filter, v); | 1227 | ret = dso__load_perf_map(self, map, filter); |
| 750 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | 1228 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : |
| 751 | DSO__ORIG_NOT_FOUND; | 1229 | DSO__ORIG_NOT_FOUND; |
| 752 | return ret; | 1230 | return ret; |
| @@ -759,34 +1237,50 @@ more: | |||
| 759 | self->origin++; | 1237 | self->origin++; |
| 760 | switch (self->origin) { | 1238 | switch (self->origin) { |
| 761 | case DSO__ORIG_FEDORA: | 1239 | case DSO__ORIG_FEDORA: |
| 762 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | 1240 | snprintf(name, size, "/usr/lib/debug%s.debug", |
| 1241 | self->long_name); | ||
| 763 | break; | 1242 | break; |
| 764 | case DSO__ORIG_UBUNTU: | 1243 | case DSO__ORIG_UBUNTU: |
| 765 | snprintf(name, size, "/usr/lib/debug%s", self->name); | 1244 | snprintf(name, size, "/usr/lib/debug%s", |
| 1245 | self->long_name); | ||
| 766 | break; | 1246 | break; |
| 767 | case DSO__ORIG_BUILDID: | 1247 | case DSO__ORIG_BUILDID: |
| 768 | build_id = dso__read_build_id(self, v); | 1248 | if (filename__read_build_id(self->long_name, build_id, |
| 769 | if (build_id != NULL) { | 1249 | sizeof(build_id))) { |
| 1250 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
| 1251 | |||
| 1252 | build_id__sprintf(build_id, sizeof(build_id), | ||
| 1253 | build_id_hex); | ||
| 770 | snprintf(name, size, | 1254 | snprintf(name, size, |
| 771 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | 1255 | "/usr/lib/debug/.build-id/%.2s/%s.debug", |
| 772 | build_id, build_id + 2); | 1256 | build_id_hex, build_id_hex + 2); |
| 773 | free(build_id); | 1257 | if (self->has_build_id) |
| 1258 | goto compare_build_id; | ||
| 774 | break; | 1259 | break; |
| 775 | } | 1260 | } |
| 776 | self->origin++; | 1261 | self->origin++; |
| 777 | /* Fall thru */ | 1262 | /* Fall thru */ |
| 778 | case DSO__ORIG_DSO: | 1263 | case DSO__ORIG_DSO: |
| 779 | snprintf(name, size, "%s", self->name); | 1264 | snprintf(name, size, "%s", self->long_name); |
| 780 | break; | 1265 | break; |
| 781 | 1266 | ||
| 782 | default: | 1267 | default: |
| 783 | goto out; | 1268 | goto out; |
| 784 | } | 1269 | } |
| 785 | 1270 | ||
| 1271 | if (self->has_build_id) { | ||
| 1272 | if (filename__read_build_id(name, build_id, | ||
| 1273 | sizeof(build_id)) < 0) | ||
| 1274 | goto more; | ||
| 1275 | compare_build_id: | ||
| 1276 | if (!dso__build_id_equal(self, build_id)) | ||
| 1277 | goto more; | ||
| 1278 | } | ||
| 1279 | |||
| 786 | fd = open(name, O_RDONLY); | 1280 | fd = open(name, O_RDONLY); |
| 787 | } while (fd < 0); | 1281 | } while (fd < 0); |
| 788 | 1282 | ||
| 789 | ret = dso__load_sym(self, fd, name, filter, v, NULL); | 1283 | ret = dso__load_sym(self, map, NULL, name, fd, filter, 0, 0); |
| 790 | close(fd); | 1284 | close(fd); |
| 791 | 1285 | ||
| 792 | /* | 1286 | /* |
| @@ -796,7 +1290,7 @@ more: | |||
| 796 | goto more; | 1290 | goto more; |
| 797 | 1291 | ||
| 798 | if (ret > 0) { | 1292 | if (ret > 0) { |
| 799 | int nr_plt = dso__synthesize_plt_symbols(self, v); | 1293 | int nr_plt = dso__synthesize_plt_symbols(self, map, filter); |
| 800 | if (nr_plt > 0) | 1294 | if (nr_plt > 0) |
| 801 | ret += nr_plt; | 1295 | ret += nr_plt; |
| 802 | } | 1296 | } |
| @@ -807,151 +1301,281 @@ out: | |||
| 807 | return ret; | 1301 | return ret; |
| 808 | } | 1302 | } |
| 809 | 1303 | ||
| 810 | static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name, | 1304 | struct map *map_groups__find_by_name(struct map_groups *self, |
| 811 | symbol_filter_t filter, int v) | 1305 | enum map_type type, const char *name) |
| 812 | { | 1306 | { |
| 813 | struct module *mod = mod_dso__find_module(mods, name); | 1307 | struct rb_node *nd; |
| 814 | int err = 0, fd; | ||
| 815 | 1308 | ||
| 816 | if (mod == NULL || !mod->active) | 1309 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { |
| 817 | return err; | 1310 | struct map *map = rb_entry(nd, struct map, rb_node); |
| 818 | 1311 | ||
| 819 | fd = open(mod->path, O_RDONLY); | 1312 | if (map->dso && strcmp(map->dso->name, name) == 0) |
| 1313 | return map; | ||
| 1314 | } | ||
| 820 | 1315 | ||
| 821 | if (fd < 0) | 1316 | return NULL; |
| 822 | return err; | 1317 | } |
| 823 | 1318 | ||
| 824 | err = dso__load_sym(self, fd, name, filter, v, mod); | 1319 | static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname) |
| 825 | close(fd); | 1320 | { |
| 1321 | struct dirent *dent; | ||
| 1322 | DIR *dir = opendir(dirname); | ||
| 826 | 1323 | ||
| 827 | return err; | 1324 | if (!dir) { |
| 1325 | pr_debug("%s: cannot open %s dir\n", __func__, dirname); | ||
| 1326 | return -1; | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | while ((dent = readdir(dir)) != NULL) { | ||
| 1330 | char path[PATH_MAX]; | ||
| 1331 | |||
| 1332 | if (dent->d_type == DT_DIR) { | ||
| 1333 | if (!strcmp(dent->d_name, ".") || | ||
| 1334 | !strcmp(dent->d_name, "..")) | ||
| 1335 | continue; | ||
| 1336 | |||
| 1337 | snprintf(path, sizeof(path), "%s/%s", | ||
| 1338 | dirname, dent->d_name); | ||
| 1339 | if (perf_session__set_modules_path_dir(self, path) < 0) | ||
| 1340 | goto failure; | ||
| 1341 | } else { | ||
| 1342 | char *dot = strrchr(dent->d_name, '.'), | ||
| 1343 | dso_name[PATH_MAX]; | ||
| 1344 | struct map *map; | ||
| 1345 | char *long_name; | ||
| 1346 | |||
| 1347 | if (dot == NULL || strcmp(dot, ".ko")) | ||
| 1348 | continue; | ||
| 1349 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | ||
| 1350 | (int)(dot - dent->d_name), dent->d_name); | ||
| 1351 | |||
| 1352 | strxfrchar(dso_name, '-', '_'); | ||
| 1353 | map = map_groups__find_by_name(&self->kmaps, MAP__FUNCTION, dso_name); | ||
| 1354 | if (map == NULL) | ||
| 1355 | continue; | ||
| 1356 | |||
| 1357 | snprintf(path, sizeof(path), "%s/%s", | ||
| 1358 | dirname, dent->d_name); | ||
| 1359 | |||
| 1360 | long_name = strdup(path); | ||
| 1361 | if (long_name == NULL) | ||
| 1362 | goto failure; | ||
| 1363 | dso__set_long_name(map->dso, long_name); | ||
| 1364 | } | ||
| 1365 | } | ||
| 1366 | |||
| 1367 | return 0; | ||
| 1368 | failure: | ||
| 1369 | closedir(dir); | ||
| 1370 | return -1; | ||
| 828 | } | 1371 | } |
| 829 | 1372 | ||
| 830 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int v) | 1373 | static int perf_session__set_modules_path(struct perf_session *self) |
| 831 | { | 1374 | { |
| 832 | struct mod_dso *mods = mod_dso__new_dso("modules"); | 1375 | struct utsname uts; |
| 833 | struct module *pos; | 1376 | char modules_path[PATH_MAX]; |
| 834 | struct rb_node *next; | ||
| 835 | int err, count = 0; | ||
| 836 | 1377 | ||
| 837 | err = mod_dso__load_modules(mods); | 1378 | if (uname(&uts) < 0) |
| 1379 | return -1; | ||
| 838 | 1380 | ||
| 839 | if (err <= 0) | 1381 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", |
| 840 | return err; | 1382 | uts.release); |
| 841 | 1383 | ||
| 842 | /* | 1384 | return perf_session__set_modules_path_dir(self, modules_path); |
| 843 | * Iterate over modules, and load active symbols. | 1385 | } |
| 844 | */ | ||
| 845 | next = rb_first(&mods->mods); | ||
| 846 | while (next) { | ||
| 847 | pos = rb_entry(next, struct module, rb_node); | ||
| 848 | err = dso__load_module(self, mods, pos->name, filter, v); | ||
| 849 | |||
| 850 | if (err < 0) | ||
| 851 | break; | ||
| 852 | 1386 | ||
| 853 | next = rb_next(&pos->rb_node); | 1387 | /* |
| 854 | count += err; | 1388 | * Constructor variant for modules (where we know from /proc/modules where |
| 855 | } | 1389 | * they are loaded) and for vmlinux, where only after we load all the |
| 1390 | * symbols we'll know where it starts and ends. | ||
| 1391 | */ | ||
| 1392 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | ||
| 1393 | { | ||
| 1394 | struct map *self = malloc(sizeof(*self)); | ||
| 856 | 1395 | ||
| 857 | if (err < 0) { | 1396 | if (self != NULL) { |
| 858 | mod_dso__delete_modules(mods); | 1397 | /* |
| 859 | mod_dso__delete_self(mods); | 1398 | * ->end will be filled after we load all the symbols |
| 860 | return err; | 1399 | */ |
| 1400 | map__init(self, type, start, 0, 0, dso); | ||
| 861 | } | 1401 | } |
| 862 | 1402 | ||
| 863 | return count; | 1403 | return self; |
| 864 | } | 1404 | } |
| 865 | 1405 | ||
| 866 | static inline void dso__fill_symbol_holes(struct dso *self) | 1406 | static int perf_session__create_module_maps(struct perf_session *self) |
| 867 | { | 1407 | { |
| 868 | struct symbol *prev = NULL; | 1408 | char *line = NULL; |
| 869 | struct rb_node *nd; | 1409 | size_t n; |
| 1410 | FILE *file = fopen("/proc/modules", "r"); | ||
| 1411 | struct map *map; | ||
| 870 | 1412 | ||
| 871 | for (nd = rb_last(&self->syms); nd; nd = rb_prev(nd)) { | 1413 | if (file == NULL) |
| 872 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 1414 | return -1; |
| 873 | 1415 | ||
| 874 | if (prev) { | 1416 | while (!feof(file)) { |
| 875 | u64 hole = 0; | 1417 | char name[PATH_MAX]; |
| 876 | int alias = pos->start == prev->start; | 1418 | u64 start; |
| 1419 | struct dso *dso; | ||
| 1420 | char *sep; | ||
| 1421 | int line_len; | ||
| 877 | 1422 | ||
| 878 | if (!alias) | 1423 | line_len = getline(&line, &n, file); |
| 879 | hole = prev->start - pos->end - 1; | 1424 | if (line_len < 0) |
| 1425 | break; | ||
| 880 | 1426 | ||
| 881 | if (hole || alias) { | 1427 | if (!line) |
| 882 | if (alias) | 1428 | goto out_failure; |
| 883 | pos->end = prev->end; | 1429 | |
| 884 | else if (hole) | 1430 | line[--line_len] = '\0'; /* \n */ |
| 885 | pos->end = prev->start - 1; | 1431 | |
| 886 | } | 1432 | sep = strrchr(line, 'x'); |
| 1433 | if (sep == NULL) | ||
| 1434 | continue; | ||
| 1435 | |||
| 1436 | hex2u64(sep + 1, &start); | ||
| 1437 | |||
| 1438 | sep = strchr(line, ' '); | ||
| 1439 | if (sep == NULL) | ||
| 1440 | continue; | ||
| 1441 | |||
| 1442 | *sep = '\0'; | ||
| 1443 | |||
| 1444 | snprintf(name, sizeof(name), "[%s]", line); | ||
| 1445 | dso = dso__new(name); | ||
| 1446 | |||
| 1447 | if (dso == NULL) | ||
| 1448 | goto out_delete_line; | ||
| 1449 | |||
| 1450 | map = map__new2(start, dso, MAP__FUNCTION); | ||
| 1451 | if (map == NULL) { | ||
| 1452 | dso__delete(dso); | ||
| 1453 | goto out_delete_line; | ||
| 887 | } | 1454 | } |
| 888 | prev = pos; | 1455 | |
| 1456 | snprintf(name, sizeof(name), | ||
| 1457 | "/sys/module/%s/notes/.note.gnu.build-id", line); | ||
| 1458 | if (sysfs__read_build_id(name, dso->build_id, | ||
| 1459 | sizeof(dso->build_id)) == 0) | ||
| 1460 | dso->has_build_id = true; | ||
| 1461 | |||
| 1462 | dso->origin = DSO__ORIG_KMODULE; | ||
| 1463 | map_groups__insert(&self->kmaps, map); | ||
| 1464 | dsos__add(&dsos__kernel, dso); | ||
| 889 | } | 1465 | } |
| 1466 | |||
| 1467 | free(line); | ||
| 1468 | fclose(file); | ||
| 1469 | |||
| 1470 | return perf_session__set_modules_path(self); | ||
| 1471 | |||
| 1472 | out_delete_line: | ||
| 1473 | free(line); | ||
| 1474 | out_failure: | ||
| 1475 | return -1; | ||
| 890 | } | 1476 | } |
| 891 | 1477 | ||
| 892 | static int dso__load_vmlinux(struct dso *self, const char *vmlinux, | 1478 | static int dso__load_vmlinux(struct dso *self, struct map *map, |
| 893 | symbol_filter_t filter, int v) | 1479 | struct perf_session *session, |
| 1480 | const char *vmlinux, symbol_filter_t filter) | ||
| 894 | { | 1481 | { |
| 895 | int err, fd = open(vmlinux, O_RDONLY); | 1482 | int err = -1, fd; |
| 896 | 1483 | ||
| 897 | if (fd < 0) | 1484 | if (self->has_build_id) { |
| 898 | return -1; | 1485 | u8 build_id[BUILD_ID_SIZE]; |
| 899 | 1486 | ||
| 900 | err = dso__load_sym(self, fd, vmlinux, filter, v, NULL); | 1487 | if (filename__read_build_id(vmlinux, build_id, |
| 1488 | sizeof(build_id)) < 0) { | ||
| 1489 | pr_debug("No build_id in %s, ignoring it\n", vmlinux); | ||
| 1490 | return -1; | ||
| 1491 | } | ||
| 1492 | if (!dso__build_id_equal(self, build_id)) { | ||
| 1493 | char expected_build_id[BUILD_ID_SIZE * 2 + 1], | ||
| 1494 | vmlinux_build_id[BUILD_ID_SIZE * 2 + 1]; | ||
| 1495 | |||
| 1496 | build_id__sprintf(self->build_id, | ||
| 1497 | sizeof(self->build_id), | ||
| 1498 | expected_build_id); | ||
| 1499 | build_id__sprintf(build_id, sizeof(build_id), | ||
| 1500 | vmlinux_build_id); | ||
| 1501 | pr_debug("build_id in %s is %s while expected is %s, " | ||
| 1502 | "ignoring it\n", vmlinux, vmlinux_build_id, | ||
| 1503 | expected_build_id); | ||
| 1504 | return -1; | ||
| 1505 | } | ||
| 1506 | } | ||
| 901 | 1507 | ||
| 902 | if (err > 0) | 1508 | fd = open(vmlinux, O_RDONLY); |
| 903 | dso__fill_symbol_holes(self); | 1509 | if (fd < 0) |
| 1510 | return -1; | ||
| 904 | 1511 | ||
| 1512 | dso__set_loaded(self, map->type); | ||
| 1513 | err = dso__load_sym(self, map, session, self->long_name, fd, filter, 1, 0); | ||
| 905 | close(fd); | 1514 | close(fd); |
| 906 | 1515 | ||
| 907 | return err; | 1516 | return err; |
| 908 | } | 1517 | } |
| 909 | 1518 | ||
| 910 | int dso__load_kernel(struct dso *self, const char *vmlinux, | 1519 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
| 911 | symbol_filter_t filter, int v, int use_modules) | 1520 | struct perf_session *session, symbol_filter_t filter) |
| 912 | { | 1521 | { |
| 913 | int err = -1; | 1522 | int err; |
| 914 | 1523 | bool is_kallsyms; | |
| 915 | if (vmlinux) { | 1524 | |
| 916 | err = dso__load_vmlinux(self, vmlinux, filter, v); | 1525 | if (vmlinux_path != NULL) { |
| 917 | if (err > 0 && use_modules) { | 1526 | int i; |
| 918 | int syms = dso__load_modules(self, filter, v); | 1527 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", |
| 919 | 1528 | vmlinux_path__nr_entries); | |
| 920 | if (syms < 0) { | 1529 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { |
| 921 | fprintf(stderr, "dso__load_modules failed!\n"); | 1530 | err = dso__load_vmlinux(self, map, session, |
| 922 | return syms; | 1531 | vmlinux_path[i], filter); |
| 1532 | if (err > 0) { | ||
| 1533 | pr_debug("Using %s for symbols\n", | ||
| 1534 | vmlinux_path[i]); | ||
| 1535 | dso__set_long_name(self, | ||
| 1536 | strdup(vmlinux_path[i])); | ||
| 1537 | goto out_fixup; | ||
| 923 | } | 1538 | } |
| 924 | err += syms; | ||
| 925 | } | 1539 | } |
| 926 | } | 1540 | } |
| 927 | 1541 | ||
| 928 | if (err <= 0) | 1542 | is_kallsyms = self->long_name[0] == '['; |
| 929 | err = dso__load_kallsyms(self, filter, v); | 1543 | if (is_kallsyms) |
| 1544 | goto do_kallsyms; | ||
| 930 | 1545 | ||
| 931 | if (err > 0) | 1546 | err = dso__load_vmlinux(self, map, session, self->long_name, filter); |
| 932 | self->origin = DSO__ORIG_KERNEL; | 1547 | if (err <= 0) { |
| 1548 | pr_info("The file %s cannot be used, " | ||
| 1549 | "trying to use /proc/kallsyms...", self->long_name); | ||
| 1550 | do_kallsyms: | ||
| 1551 | err = dso__load_kallsyms(self, map, session, filter); | ||
| 1552 | if (err > 0 && !is_kallsyms) | ||
| 1553 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | if (err > 0) { | ||
| 1557 | out_fixup: | ||
| 1558 | map__fixup_start(map); | ||
| 1559 | map__fixup_end(map); | ||
| 1560 | } | ||
| 933 | 1561 | ||
| 934 | return err; | 1562 | return err; |
| 935 | } | 1563 | } |
| 936 | 1564 | ||
| 937 | LIST_HEAD(dsos); | 1565 | LIST_HEAD(dsos__user); |
| 938 | struct dso *kernel_dso; | 1566 | LIST_HEAD(dsos__kernel); |
| 939 | struct dso *vdso; | 1567 | struct dso *vdso; |
| 940 | struct dso *hypervisor_dso; | ||
| 941 | 1568 | ||
| 942 | const char *vmlinux_name = "vmlinux"; | 1569 | static void dsos__add(struct list_head *head, struct dso *dso) |
| 943 | int modules; | ||
| 944 | |||
| 945 | static void dsos__add(struct dso *dso) | ||
| 946 | { | 1570 | { |
| 947 | list_add_tail(&dso->node, &dsos); | 1571 | list_add_tail(&dso->node, head); |
| 948 | } | 1572 | } |
| 949 | 1573 | ||
| 950 | static struct dso *dsos__find(const char *name) | 1574 | static struct dso *dsos__find(struct list_head *head, const char *name) |
| 951 | { | 1575 | { |
| 952 | struct dso *pos; | 1576 | struct dso *pos; |
| 953 | 1577 | ||
| 954 | list_for_each_entry(pos, &dsos, node) | 1578 | list_for_each_entry(pos, head, node) |
| 955 | if (strcmp(pos->name, name) == 0) | 1579 | if (strcmp(pos->name, name) == 0) |
| 956 | return pos; | 1580 | return pos; |
| 957 | return NULL; | 1581 | return NULL; |
| @@ -959,79 +1583,227 @@ static struct dso *dsos__find(const char *name) | |||
| 959 | 1583 | ||
| 960 | struct dso *dsos__findnew(const char *name) | 1584 | struct dso *dsos__findnew(const char *name) |
| 961 | { | 1585 | { |
| 962 | struct dso *dso = dsos__find(name); | 1586 | struct dso *dso = dsos__find(&dsos__user, name); |
| 963 | int nr; | ||
| 964 | |||
| 965 | if (dso) | ||
| 966 | return dso; | ||
| 967 | |||
| 968 | dso = dso__new(name, 0); | ||
| 969 | if (!dso) | ||
| 970 | goto out_delete_dso; | ||
| 971 | 1587 | ||
| 972 | nr = dso__load(dso, NULL, verbose); | 1588 | if (!dso) { |
| 973 | if (nr < 0) { | 1589 | dso = dso__new(name); |
| 974 | eprintf("Failed to open: %s\n", name); | 1590 | if (dso != NULL) { |
| 975 | goto out_delete_dso; | 1591 | dsos__add(&dsos__user, dso); |
| 1592 | dso__set_basename(dso); | ||
| 1593 | } | ||
| 976 | } | 1594 | } |
| 977 | if (!nr) | ||
| 978 | eprintf("No symbols found in: %s, maybe install a debug package?\n", name); | ||
| 979 | |||
| 980 | dsos__add(dso); | ||
| 981 | 1595 | ||
| 982 | return dso; | 1596 | return dso; |
| 1597 | } | ||
| 983 | 1598 | ||
| 984 | out_delete_dso: | 1599 | static void __dsos__fprintf(struct list_head *head, FILE *fp) |
| 985 | dso__delete(dso); | 1600 | { |
| 986 | return NULL; | 1601 | struct dso *pos; |
| 1602 | |||
| 1603 | list_for_each_entry(pos, head, node) { | ||
| 1604 | int i; | ||
| 1605 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
| 1606 | dso__fprintf(pos, i, fp); | ||
| 1607 | } | ||
| 987 | } | 1608 | } |
| 988 | 1609 | ||
| 989 | void dsos__fprintf(FILE *fp) | 1610 | void dsos__fprintf(FILE *fp) |
| 990 | { | 1611 | { |
| 1612 | __dsos__fprintf(&dsos__kernel, fp); | ||
| 1613 | __dsos__fprintf(&dsos__user, fp); | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp) | ||
| 1617 | { | ||
| 991 | struct dso *pos; | 1618 | struct dso *pos; |
| 1619 | size_t ret = 0; | ||
| 992 | 1620 | ||
| 993 | list_for_each_entry(pos, &dsos, node) | 1621 | list_for_each_entry(pos, head, node) { |
| 994 | dso__fprintf(pos, fp); | 1622 | ret += dso__fprintf_buildid(pos, fp); |
| 1623 | ret += fprintf(fp, " %s\n", pos->long_name); | ||
| 1624 | } | ||
| 1625 | return ret; | ||
| 995 | } | 1626 | } |
| 996 | 1627 | ||
| 997 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) | 1628 | size_t dsos__fprintf_buildid(FILE *fp) |
| 998 | { | 1629 | { |
| 999 | return dso__find_symbol(dso, ip); | 1630 | return (__dsos__fprintf_buildid(&dsos__kernel, fp) + |
| 1631 | __dsos__fprintf_buildid(&dsos__user, fp)); | ||
| 1000 | } | 1632 | } |
| 1001 | 1633 | ||
| 1002 | int load_kernel(void) | 1634 | static struct dso *dsos__create_kernel( const char *vmlinux) |
| 1003 | { | 1635 | { |
| 1004 | int err; | 1636 | struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]"); |
| 1637 | |||
| 1638 | if (kernel == NULL) | ||
| 1639 | return NULL; | ||
| 1640 | |||
| 1641 | kernel->short_name = "[kernel]"; | ||
| 1642 | kernel->kernel = 1; | ||
| 1643 | |||
| 1644 | vdso = dso__new("[vdso]"); | ||
| 1645 | if (vdso == NULL) | ||
| 1646 | goto out_delete_kernel_dso; | ||
| 1647 | dso__set_loaded(vdso, MAP__FUNCTION); | ||
| 1648 | |||
| 1649 | if (sysfs__read_build_id("/sys/kernel/notes", kernel->build_id, | ||
| 1650 | sizeof(kernel->build_id)) == 0) | ||
| 1651 | kernel->has_build_id = true; | ||
| 1652 | |||
| 1653 | dsos__add(&dsos__kernel, kernel); | ||
| 1654 | dsos__add(&dsos__user, vdso); | ||
| 1005 | 1655 | ||
| 1006 | kernel_dso = dso__new("[kernel]", 0); | 1656 | return kernel; |
| 1007 | if (!kernel_dso) | 1657 | |
| 1658 | out_delete_kernel_dso: | ||
| 1659 | dso__delete(kernel); | ||
| 1660 | return NULL; | ||
| 1661 | } | ||
| 1662 | |||
| 1663 | static int map_groups__create_kernel_maps(struct map_groups *self, const char *vmlinux) | ||
| 1664 | { | ||
| 1665 | struct map *functions, *variables; | ||
| 1666 | struct dso *kernel = dsos__create_kernel(vmlinux); | ||
| 1667 | |||
| 1668 | if (kernel == NULL) | ||
| 1008 | return -1; | 1669 | return -1; |
| 1009 | 1670 | ||
| 1010 | err = dso__load_kernel(kernel_dso, vmlinux_name, NULL, verbose, modules); | 1671 | functions = map__new2(0, kernel, MAP__FUNCTION); |
| 1011 | if (err <= 0) { | 1672 | if (functions == NULL) |
| 1012 | dso__delete(kernel_dso); | 1673 | return -1; |
| 1013 | kernel_dso = NULL; | ||
| 1014 | } else | ||
| 1015 | dsos__add(kernel_dso); | ||
| 1016 | 1674 | ||
| 1017 | vdso = dso__new("[vdso]", 0); | 1675 | variables = map__new2(0, kernel, MAP__VARIABLE); |
| 1018 | if (!vdso) | 1676 | if (variables == NULL) { |
| 1677 | map__delete(functions); | ||
| 1019 | return -1; | 1678 | return -1; |
| 1679 | } | ||
| 1020 | 1680 | ||
| 1021 | vdso->find_symbol = vdso__find_symbol; | 1681 | functions->map_ip = functions->unmap_ip = |
| 1682 | variables->map_ip = variables->unmap_ip = identity__map_ip; | ||
| 1683 | map_groups__insert(self, functions); | ||
| 1684 | map_groups__insert(self, variables); | ||
| 1022 | 1685 | ||
| 1023 | dsos__add(vdso); | 1686 | return 0; |
| 1687 | } | ||
| 1688 | |||
| 1689 | static void vmlinux_path__exit(void) | ||
| 1690 | { | ||
| 1691 | while (--vmlinux_path__nr_entries >= 0) { | ||
| 1692 | free(vmlinux_path[vmlinux_path__nr_entries]); | ||
| 1693 | vmlinux_path[vmlinux_path__nr_entries] = NULL; | ||
| 1694 | } | ||
| 1024 | 1695 | ||
| 1025 | hypervisor_dso = dso__new("[hypervisor]", 0); | 1696 | free(vmlinux_path); |
| 1026 | if (!hypervisor_dso) | 1697 | vmlinux_path = NULL; |
| 1698 | } | ||
| 1699 | |||
| 1700 | static int vmlinux_path__init(void) | ||
| 1701 | { | ||
| 1702 | struct utsname uts; | ||
| 1703 | char bf[PATH_MAX]; | ||
| 1704 | |||
| 1705 | if (uname(&uts) < 0) | ||
| 1027 | return -1; | 1706 | return -1; |
| 1028 | dsos__add(hypervisor_dso); | ||
| 1029 | 1707 | ||
| 1030 | return err; | 1708 | vmlinux_path = malloc(sizeof(char *) * 5); |
| 1709 | if (vmlinux_path == NULL) | ||
| 1710 | return -1; | ||
| 1711 | |||
| 1712 | vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux"); | ||
| 1713 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
| 1714 | goto out_fail; | ||
| 1715 | ++vmlinux_path__nr_entries; | ||
| 1716 | vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux"); | ||
| 1717 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
| 1718 | goto out_fail; | ||
| 1719 | ++vmlinux_path__nr_entries; | ||
| 1720 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); | ||
| 1721 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
| 1722 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
| 1723 | goto out_fail; | ||
| 1724 | ++vmlinux_path__nr_entries; | ||
| 1725 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release); | ||
| 1726 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
| 1727 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
| 1728 | goto out_fail; | ||
| 1729 | ++vmlinux_path__nr_entries; | ||
| 1730 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", | ||
| 1731 | uts.release); | ||
| 1732 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
| 1733 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
| 1734 | goto out_fail; | ||
| 1735 | ++vmlinux_path__nr_entries; | ||
| 1736 | |||
| 1737 | return 0; | ||
| 1738 | |||
| 1739 | out_fail: | ||
| 1740 | vmlinux_path__exit(); | ||
| 1741 | return -1; | ||
| 1031 | } | 1742 | } |
| 1032 | 1743 | ||
| 1744 | static int setup_list(struct strlist **list, const char *list_str, | ||
| 1745 | const char *list_name) | ||
| 1746 | { | ||
| 1747 | if (list_str == NULL) | ||
| 1748 | return 0; | ||
| 1033 | 1749 | ||
| 1034 | void symbol__init(void) | 1750 | *list = strlist__new(true, list_str); |
| 1751 | if (!*list) { | ||
| 1752 | pr_err("problems parsing %s list\n", list_name); | ||
| 1753 | return -1; | ||
| 1754 | } | ||
| 1755 | return 0; | ||
| 1756 | } | ||
| 1757 | |||
| 1758 | int symbol__init(void) | ||
| 1035 | { | 1759 | { |
| 1036 | elf_version(EV_CURRENT); | 1760 | elf_version(EV_CURRENT); |
| 1761 | if (symbol_conf.sort_by_name) | ||
| 1762 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | ||
| 1763 | sizeof(struct symbol)); | ||
| 1764 | |||
| 1765 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) | ||
| 1766 | return -1; | ||
| 1767 | |||
| 1768 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { | ||
| 1769 | pr_err("'.' is the only non valid --field-separator argument\n"); | ||
| 1770 | return -1; | ||
| 1771 | } | ||
| 1772 | |||
| 1773 | if (setup_list(&symbol_conf.dso_list, | ||
| 1774 | symbol_conf.dso_list_str, "dso") < 0) | ||
| 1775 | return -1; | ||
| 1776 | |||
| 1777 | if (setup_list(&symbol_conf.comm_list, | ||
| 1778 | symbol_conf.comm_list_str, "comm") < 0) | ||
| 1779 | goto out_free_dso_list; | ||
| 1780 | |||
| 1781 | if (setup_list(&symbol_conf.sym_list, | ||
| 1782 | symbol_conf.sym_list_str, "symbol") < 0) | ||
| 1783 | goto out_free_comm_list; | ||
| 1784 | |||
| 1785 | return 0; | ||
| 1786 | |||
| 1787 | out_free_dso_list: | ||
| 1788 | strlist__delete(symbol_conf.dso_list); | ||
| 1789 | out_free_comm_list: | ||
| 1790 | strlist__delete(symbol_conf.comm_list); | ||
| 1791 | return -1; | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | int perf_session__create_kernel_maps(struct perf_session *self) | ||
| 1795 | { | ||
| 1796 | if (map_groups__create_kernel_maps(&self->kmaps, | ||
| 1797 | symbol_conf.vmlinux_name) < 0) | ||
| 1798 | return -1; | ||
| 1799 | |||
| 1800 | if (symbol_conf.use_modules && | ||
| 1801 | perf_session__create_module_maps(self) < 0) | ||
| 1802 | pr_debug("Failed to load list of modules for session %s, " | ||
| 1803 | "continuing...\n", self->filename); | ||
| 1804 | /* | ||
| 1805 | * Now that we have all the maps created, just set the ->end of them: | ||
| 1806 | */ | ||
| 1807 | map_groups__fixup_end(&self->kmaps); | ||
| 1808 | return 0; | ||
| 1037 | } | 1809 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 829da9edba64..8aded2356f79 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | #ifndef _PERF_SYMBOL_ | 1 | #ifndef __PERF_SYMBOL |
| 2 | #define _PERF_SYMBOL_ 1 | 2 | #define __PERF_SYMBOL 1 |
| 3 | 3 | ||
| 4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
| 5 | #include <stdbool.h> | ||
| 5 | #include "types.h" | 6 | #include "types.h" |
| 6 | #include <linux/list.h> | 7 | #include <linux/list.h> |
| 7 | #include <linux/rbtree.h> | 8 | #include <linux/rbtree.h> |
| 8 | #include "module.h" | ||
| 9 | #include "event.h" | 9 | #include "event.h" |
| 10 | 10 | ||
| 11 | #ifdef HAVE_CPLUS_DEMANGLE | 11 | #ifdef HAVE_CPLUS_DEMANGLE |
| @@ -46,57 +46,96 @@ struct symbol { | |||
| 46 | struct rb_node rb_node; | 46 | struct rb_node rb_node; |
| 47 | u64 start; | 47 | u64 start; |
| 48 | u64 end; | 48 | u64 end; |
| 49 | u64 obj_start; | ||
| 50 | u64 hist_sum; | ||
| 51 | u64 *hist; | ||
| 52 | struct module *module; | ||
| 53 | void *priv; | ||
| 54 | char name[0]; | 49 | char name[0]; |
| 55 | }; | 50 | }; |
| 56 | 51 | ||
| 52 | struct strlist; | ||
| 53 | |||
| 54 | struct symbol_conf { | ||
| 55 | unsigned short priv_size; | ||
| 56 | bool try_vmlinux_path, | ||
| 57 | use_modules, | ||
| 58 | sort_by_name, | ||
| 59 | show_nr_samples, | ||
| 60 | use_callchain, | ||
| 61 | exclude_other; | ||
| 62 | const char *vmlinux_name, | ||
| 63 | *field_sep; | ||
| 64 | char *dso_list_str, | ||
| 65 | *comm_list_str, | ||
| 66 | *sym_list_str, | ||
| 67 | *col_width_list_str; | ||
| 68 | struct strlist *dso_list, | ||
| 69 | *comm_list, | ||
| 70 | *sym_list; | ||
| 71 | }; | ||
| 72 | |||
| 73 | extern struct symbol_conf symbol_conf; | ||
| 74 | |||
| 75 | static inline void *symbol__priv(struct symbol *self) | ||
| 76 | { | ||
| 77 | return ((void *)self) - symbol_conf.priv_size; | ||
| 78 | } | ||
| 79 | |||
| 80 | struct addr_location { | ||
| 81 | struct thread *thread; | ||
| 82 | struct map *map; | ||
| 83 | struct symbol *sym; | ||
| 84 | u64 addr; | ||
| 85 | char level; | ||
| 86 | bool filtered; | ||
| 87 | }; | ||
| 88 | |||
| 57 | struct dso { | 89 | struct dso { |
| 58 | struct list_head node; | 90 | struct list_head node; |
| 59 | struct rb_root syms; | 91 | struct rb_root symbols[MAP__NR_TYPES]; |
| 60 | struct symbol *(*find_symbol)(struct dso *, u64 ip); | 92 | struct rb_root symbol_names[MAP__NR_TYPES]; |
| 61 | unsigned int sym_priv_size; | 93 | u8 adjust_symbols:1; |
| 62 | unsigned char adjust_symbols; | 94 | u8 slen_calculated:1; |
| 63 | unsigned char slen_calculated; | 95 | u8 has_build_id:1; |
| 96 | u8 kernel:1; | ||
| 64 | unsigned char origin; | 97 | unsigned char origin; |
| 98 | u8 sorted_by_name; | ||
| 99 | u8 loaded; | ||
| 100 | u8 build_id[BUILD_ID_SIZE]; | ||
| 101 | u16 long_name_len; | ||
| 102 | const char *short_name; | ||
| 103 | char *long_name; | ||
| 65 | char name[0]; | 104 | char name[0]; |
| 66 | }; | 105 | }; |
| 67 | 106 | ||
| 68 | extern const char *sym_hist_filter; | 107 | struct dso *dso__new(const char *name); |
| 69 | |||
| 70 | typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym); | ||
| 71 | |||
| 72 | struct dso *dso__new(const char *name, unsigned int sym_priv_size); | ||
| 73 | void dso__delete(struct dso *self); | 108 | void dso__delete(struct dso *self); |
| 74 | 109 | ||
| 75 | static inline void *dso__sym_priv(struct dso *self, struct symbol *sym) | 110 | bool dso__loaded(const struct dso *self, enum map_type type); |
| 76 | { | 111 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); |
| 77 | return ((void *)sym) - self->sym_priv_size; | 112 | |
| 78 | } | 113 | void dso__sort_by_name(struct dso *self, enum map_type type); |
| 79 | 114 | ||
| 80 | struct symbol *dso__find_symbol(struct dso *self, u64 ip); | 115 | struct perf_session; |
| 81 | 116 | ||
| 82 | int dso__load_kernel(struct dso *self, const char *vmlinux, | ||
| 83 | symbol_filter_t filter, int verbose, int modules); | ||
| 84 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose); | ||
| 85 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose); | ||
| 86 | struct dso *dsos__findnew(const char *name); | 117 | struct dso *dsos__findnew(const char *name); |
| 118 | int dso__load(struct dso *self, struct map *map, struct perf_session *session, | ||
| 119 | symbol_filter_t filter); | ||
| 87 | void dsos__fprintf(FILE *fp); | 120 | void dsos__fprintf(FILE *fp); |
| 121 | size_t dsos__fprintf_buildid(FILE *fp); | ||
| 88 | 122 | ||
| 89 | size_t dso__fprintf(struct dso *self, FILE *fp); | 123 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); |
| 124 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); | ||
| 90 | char dso__symtab_origin(const struct dso *self); | 125 | char dso__symtab_origin(const struct dso *self); |
| 126 | void dso__set_build_id(struct dso *self, void *build_id); | ||
| 127 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); | ||
| 128 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | ||
| 129 | const char *name); | ||
| 91 | 130 | ||
| 92 | int load_kernel(void); | 131 | int filename__read_build_id(const char *filename, void *bf, size_t size); |
| 132 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | ||
| 133 | bool dsos__read_build_ids(void); | ||
| 134 | int build_id__sprintf(u8 *self, int len, char *bf); | ||
| 93 | 135 | ||
| 94 | void symbol__init(void); | 136 | int symbol__init(void); |
| 137 | int perf_session__create_kernel_maps(struct perf_session *self); | ||
| 95 | 138 | ||
| 96 | extern struct list_head dsos; | 139 | extern struct list_head dsos__user, dsos__kernel; |
| 97 | extern struct dso *kernel_dso; | ||
| 98 | extern struct dso *vdso; | 140 | extern struct dso *vdso; |
| 99 | extern struct dso *hypervisor_dso; | 141 | #endif /* __PERF_SYMBOL */ |
| 100 | extern const char *vmlinux_name; | ||
| 101 | extern int modules; | ||
| 102 | #endif /* _PERF_SYMBOL_ */ | ||
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 45efb5db0d19..4a08dcf50b68 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -2,20 +2,30 @@ | |||
| 2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
| 3 | #include <stdio.h> | 3 | #include <stdio.h> |
| 4 | #include <string.h> | 4 | #include <string.h> |
| 5 | #include "session.h" | ||
| 5 | #include "thread.h" | 6 | #include "thread.h" |
| 6 | #include "util.h" | 7 | #include "util.h" |
| 7 | #include "debug.h" | 8 | #include "debug.h" |
| 8 | 9 | ||
| 10 | void map_groups__init(struct map_groups *self) | ||
| 11 | { | ||
| 12 | int i; | ||
| 13 | for (i = 0; i < MAP__NR_TYPES; ++i) { | ||
| 14 | self->maps[i] = RB_ROOT; | ||
| 15 | INIT_LIST_HEAD(&self->removed_maps[i]); | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 9 | static struct thread *thread__new(pid_t pid) | 19 | static struct thread *thread__new(pid_t pid) |
| 10 | { | 20 | { |
| 11 | struct thread *self = calloc(1, sizeof(*self)); | 21 | struct thread *self = zalloc(sizeof(*self)); |
| 12 | 22 | ||
| 13 | if (self != NULL) { | 23 | if (self != NULL) { |
| 24 | map_groups__init(&self->mg); | ||
| 14 | self->pid = pid; | 25 | self->pid = pid; |
| 15 | self->comm = malloc(32); | 26 | self->comm = malloc(32); |
| 16 | if (self->comm) | 27 | if (self->comm) |
| 17 | snprintf(self->comm, 32, ":%d", self->pid); | 28 | snprintf(self->comm, 32, ":%d", self->pid); |
| 18 | INIT_LIST_HEAD(&self->maps); | ||
| 19 | } | 29 | } |
| 20 | 30 | ||
| 21 | return self; | 31 | return self; |
| @@ -29,21 +39,90 @@ int thread__set_comm(struct thread *self, const char *comm) | |||
| 29 | return self->comm ? 0 : -ENOMEM; | 39 | return self->comm ? 0 : -ENOMEM; |
| 30 | } | 40 | } |
| 31 | 41 | ||
| 32 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 42 | int thread__comm_len(struct thread *self) |
| 43 | { | ||
| 44 | if (!self->comm_len) { | ||
| 45 | if (!self->comm) | ||
| 46 | return 0; | ||
| 47 | self->comm_len = strlen(self->comm); | ||
| 48 | } | ||
| 49 | |||
| 50 | return self->comm_len; | ||
| 51 | } | ||
| 52 | |||
| 53 | static const char *map_type__name[MAP__NR_TYPES] = { | ||
| 54 | [MAP__FUNCTION] = "Functions", | ||
| 55 | [MAP__VARIABLE] = "Variables", | ||
| 56 | }; | ||
| 57 | |||
| 58 | static size_t __map_groups__fprintf_maps(struct map_groups *self, | ||
| 59 | enum map_type type, FILE *fp) | ||
| 60 | { | ||
| 61 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); | ||
| 62 | struct rb_node *nd; | ||
| 63 | |||
| 64 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | ||
| 65 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
| 66 | printed += fprintf(fp, "Map:"); | ||
| 67 | printed += map__fprintf(pos, fp); | ||
| 68 | if (verbose > 1) { | ||
| 69 | printed += dso__fprintf(pos->dso, type, fp); | ||
| 70 | printed += fprintf(fp, "--\n"); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | return printed; | ||
| 75 | } | ||
| 76 | |||
| 77 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp) | ||
| 78 | { | ||
| 79 | size_t printed = 0, i; | ||
| 80 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
| 81 | printed += __map_groups__fprintf_maps(self, i, fp); | ||
| 82 | return printed; | ||
| 83 | } | ||
| 84 | |||
| 85 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, | ||
| 86 | enum map_type type, FILE *fp) | ||
| 33 | { | 87 | { |
| 34 | struct map *pos; | 88 | struct map *pos; |
| 35 | size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); | 89 | size_t printed = 0; |
| 90 | |||
| 91 | list_for_each_entry(pos, &self->removed_maps[type], node) { | ||
| 92 | printed += fprintf(fp, "Map:"); | ||
| 93 | printed += map__fprintf(pos, fp); | ||
| 94 | if (verbose > 1) { | ||
| 95 | printed += dso__fprintf(pos->dso, type, fp); | ||
| 96 | printed += fprintf(fp, "--\n"); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | return printed; | ||
| 100 | } | ||
| 36 | 101 | ||
| 37 | list_for_each_entry(pos, &self->maps, node) | 102 | static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp) |
| 38 | ret += map__fprintf(pos, fp); | 103 | { |
| 104 | size_t printed = 0, i; | ||
| 105 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
| 106 | printed += __map_groups__fprintf_removed_maps(self, i, fp); | ||
| 107 | return printed; | ||
| 108 | } | ||
| 39 | 109 | ||
| 40 | return ret; | 110 | static size_t map_groups__fprintf(struct map_groups *self, FILE *fp) |
| 111 | { | ||
| 112 | size_t printed = map_groups__fprintf_maps(self, fp); | ||
| 113 | printed += fprintf(fp, "Removed maps:\n"); | ||
| 114 | return printed + map_groups__fprintf_removed_maps(self, fp); | ||
| 115 | } | ||
| 116 | |||
| 117 | static size_t thread__fprintf(struct thread *self, FILE *fp) | ||
| 118 | { | ||
| 119 | return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + | ||
| 120 | map_groups__fprintf(&self->mg, fp); | ||
| 41 | } | 121 | } |
| 42 | 122 | ||
| 43 | struct thread * | 123 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) |
| 44 | threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) | ||
| 45 | { | 124 | { |
| 46 | struct rb_node **p = &threads->rb_node; | 125 | struct rb_node **p = &self->threads.rb_node; |
| 47 | struct rb_node *parent = NULL; | 126 | struct rb_node *parent = NULL; |
| 48 | struct thread *th; | 127 | struct thread *th; |
| 49 | 128 | ||
| @@ -52,15 +131,15 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) | |||
| 52 | * so most of the time we dont have to look up | 131 | * so most of the time we dont have to look up |
| 53 | * the full rbtree: | 132 | * the full rbtree: |
| 54 | */ | 133 | */ |
| 55 | if (*last_match && (*last_match)->pid == pid) | 134 | if (self->last_match && self->last_match->pid == pid) |
| 56 | return *last_match; | 135 | return self->last_match; |
| 57 | 136 | ||
| 58 | while (*p != NULL) { | 137 | while (*p != NULL) { |
| 59 | parent = *p; | 138 | parent = *p; |
| 60 | th = rb_entry(parent, struct thread, rb_node); | 139 | th = rb_entry(parent, struct thread, rb_node); |
| 61 | 140 | ||
| 62 | if (th->pid == pid) { | 141 | if (th->pid == pid) { |
| 63 | *last_match = th; | 142 | self->last_match = th; |
| 64 | return th; | 143 | return th; |
| 65 | } | 144 | } |
| 66 | 145 | ||
| @@ -73,99 +152,127 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) | |||
| 73 | th = thread__new(pid); | 152 | th = thread__new(pid); |
| 74 | if (th != NULL) { | 153 | if (th != NULL) { |
| 75 | rb_link_node(&th->rb_node, parent, p); | 154 | rb_link_node(&th->rb_node, parent, p); |
| 76 | rb_insert_color(&th->rb_node, threads); | 155 | rb_insert_color(&th->rb_node, &self->threads); |
| 77 | *last_match = th; | 156 | self->last_match = th; |
| 78 | } | 157 | } |
| 79 | 158 | ||
| 80 | return th; | 159 | return th; |
| 81 | } | 160 | } |
| 82 | 161 | ||
| 83 | struct thread * | 162 | static void map_groups__remove_overlappings(struct map_groups *self, |
| 84 | register_idle_thread(struct rb_root *threads, struct thread **last_match) | 163 | struct map *map) |
| 85 | { | 164 | { |
| 86 | struct thread *thread = threads__findnew(0, threads, last_match); | 165 | struct rb_root *root = &self->maps[map->type]; |
| 166 | struct rb_node *next = rb_first(root); | ||
| 87 | 167 | ||
| 88 | if (!thread || thread__set_comm(thread, "swapper")) { | 168 | while (next) { |
| 89 | fprintf(stderr, "problem inserting idle task.\n"); | 169 | struct map *pos = rb_entry(next, struct map, rb_node); |
| 90 | exit(-1); | 170 | next = rb_next(&pos->rb_node); |
| 91 | } | 171 | |
| 172 | if (!map__overlap(pos, map)) | ||
| 173 | continue; | ||
| 92 | 174 | ||
| 93 | return thread; | 175 | if (verbose >= 2) { |
| 176 | fputs("overlapping maps:\n", stderr); | ||
| 177 | map__fprintf(map, stderr); | ||
| 178 | map__fprintf(pos, stderr); | ||
| 179 | } | ||
| 180 | |||
| 181 | rb_erase(&pos->rb_node, root); | ||
| 182 | /* | ||
| 183 | * We may have references to this map, for instance in some | ||
| 184 | * hist_entry instances, so just move them to a separate | ||
| 185 | * list. | ||
| 186 | */ | ||
| 187 | list_add_tail(&pos->node, &self->removed_maps[map->type]); | ||
| 188 | } | ||
| 94 | } | 189 | } |
| 95 | 190 | ||
| 96 | void thread__insert_map(struct thread *self, struct map *map) | 191 | void maps__insert(struct rb_root *maps, struct map *map) |
| 97 | { | 192 | { |
| 98 | struct map *pos, *tmp; | 193 | struct rb_node **p = &maps->rb_node; |
| 99 | 194 | struct rb_node *parent = NULL; | |
| 100 | list_for_each_entry_safe(pos, tmp, &self->maps, node) { | 195 | const u64 ip = map->start; |
| 101 | if (map__overlap(pos, map)) { | 196 | struct map *m; |
| 102 | if (verbose >= 2) { | ||
| 103 | printf("overlapping maps:\n"); | ||
| 104 | map__fprintf(map, stdout); | ||
| 105 | map__fprintf(pos, stdout); | ||
| 106 | } | ||
| 107 | 197 | ||
| 108 | if (map->start <= pos->start && map->end > pos->start) | 198 | while (*p != NULL) { |
| 109 | pos->start = map->end; | 199 | parent = *p; |
| 200 | m = rb_entry(parent, struct map, rb_node); | ||
| 201 | if (ip < m->start) | ||
| 202 | p = &(*p)->rb_left; | ||
| 203 | else | ||
| 204 | p = &(*p)->rb_right; | ||
| 205 | } | ||
| 110 | 206 | ||
| 111 | if (map->end >= pos->end && map->start < pos->end) | 207 | rb_link_node(&map->rb_node, parent, p); |
| 112 | pos->end = map->start; | 208 | rb_insert_color(&map->rb_node, maps); |
| 209 | } | ||
| 113 | 210 | ||
| 114 | if (verbose >= 2) { | 211 | struct map *maps__find(struct rb_root *maps, u64 ip) |
| 115 | printf("after collision:\n"); | 212 | { |
| 116 | map__fprintf(pos, stdout); | 213 | struct rb_node **p = &maps->rb_node; |
| 117 | } | 214 | struct rb_node *parent = NULL; |
| 215 | struct map *m; | ||
| 118 | 216 | ||
| 119 | if (pos->start >= pos->end) { | 217 | while (*p != NULL) { |
| 120 | list_del_init(&pos->node); | 218 | parent = *p; |
| 121 | free(pos); | 219 | m = rb_entry(parent, struct map, rb_node); |
| 122 | } | 220 | if (ip < m->start) |
| 123 | } | 221 | p = &(*p)->rb_left; |
| 222 | else if (ip > m->end) | ||
| 223 | p = &(*p)->rb_right; | ||
| 224 | else | ||
| 225 | return m; | ||
| 124 | } | 226 | } |
| 125 | 227 | ||
| 126 | list_add_tail(&map->node, &self->maps); | 228 | return NULL; |
| 127 | } | 229 | } |
| 128 | 230 | ||
| 129 | int thread__fork(struct thread *self, struct thread *parent) | 231 | void thread__insert_map(struct thread *self, struct map *map) |
| 130 | { | 232 | { |
| 131 | struct map *map; | 233 | map_groups__remove_overlappings(&self->mg, map); |
| 132 | 234 | map_groups__insert(&self->mg, map); | |
| 133 | if (self->comm) | 235 | } |
| 134 | free(self->comm); | ||
| 135 | self->comm = strdup(parent->comm); | ||
| 136 | if (!self->comm) | ||
| 137 | return -ENOMEM; | ||
| 138 | 236 | ||
| 139 | list_for_each_entry(map, &parent->maps, node) { | 237 | /* |
| 238 | * XXX This should not really _copy_ te maps, but refcount them. | ||
| 239 | */ | ||
| 240 | static int map_groups__clone(struct map_groups *self, | ||
| 241 | struct map_groups *parent, enum map_type type) | ||
| 242 | { | ||
| 243 | struct rb_node *nd; | ||
| 244 | for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { | ||
| 245 | struct map *map = rb_entry(nd, struct map, rb_node); | ||
| 140 | struct map *new = map__clone(map); | 246 | struct map *new = map__clone(map); |
| 141 | if (!new) | 247 | if (new == NULL) |
| 142 | return -ENOMEM; | 248 | return -ENOMEM; |
| 143 | thread__insert_map(self, new); | 249 | map_groups__insert(self, new); |
| 144 | } | 250 | } |
| 145 | |||
| 146 | return 0; | 251 | return 0; |
| 147 | } | 252 | } |
| 148 | 253 | ||
| 149 | struct map *thread__find_map(struct thread *self, u64 ip) | 254 | int thread__fork(struct thread *self, struct thread *parent) |
| 150 | { | 255 | { |
| 151 | struct map *pos; | 256 | int i; |
| 152 | 257 | ||
| 153 | if (self == NULL) | 258 | if (self->comm) |
| 154 | return NULL; | 259 | free(self->comm); |
| 155 | 260 | self->comm = strdup(parent->comm); | |
| 156 | list_for_each_entry(pos, &self->maps, node) | 261 | if (!self->comm) |
| 157 | if (ip >= pos->start && ip <= pos->end) | 262 | return -ENOMEM; |
| 158 | return pos; | ||
| 159 | 263 | ||
| 160 | return NULL; | 264 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 265 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) | ||
| 266 | return -ENOMEM; | ||
| 267 | return 0; | ||
| 161 | } | 268 | } |
| 162 | 269 | ||
| 163 | size_t threads__fprintf(FILE *fp, struct rb_root *threads) | 270 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp) |
| 164 | { | 271 | { |
| 165 | size_t ret = 0; | 272 | size_t ret = 0; |
| 166 | struct rb_node *nd; | 273 | struct rb_node *nd; |
| 167 | 274 | ||
| 168 | for (nd = rb_first(threads); nd; nd = rb_next(nd)) { | 275 | for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) { |
| 169 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | 276 | struct thread *pos = rb_entry(nd, struct thread, rb_node); |
| 170 | 277 | ||
| 171 | ret += thread__fprintf(pos, fp); | 278 | ret += thread__fprintf(pos, fp); |
| @@ -173,3 +280,16 @@ size_t threads__fprintf(FILE *fp, struct rb_root *threads) | |||
| 173 | 280 | ||
| 174 | return ret; | 281 | return ret; |
| 175 | } | 282 | } |
| 283 | |||
| 284 | struct symbol *map_groups__find_symbol(struct map_groups *self, | ||
| 285 | struct perf_session *session, | ||
| 286 | enum map_type type, u64 addr, | ||
| 287 | symbol_filter_t filter) | ||
| 288 | { | ||
| 289 | struct map *map = map_groups__find(self, type, addr); | ||
| 290 | |||
| 291 | if (map != NULL) | ||
| 292 | return map__find_symbol(map, session, map->map_ip(map, addr), filter); | ||
| 293 | |||
| 294 | return NULL; | ||
| 295 | } | ||
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 32aea3c1c2ad..c206f72c8881 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -1,22 +1,70 @@ | |||
| 1 | #ifndef __PERF_THREAD_H | ||
| 2 | #define __PERF_THREAD_H | ||
| 3 | |||
| 1 | #include <linux/rbtree.h> | 4 | #include <linux/rbtree.h> |
| 2 | #include <linux/list.h> | ||
| 3 | #include <unistd.h> | 5 | #include <unistd.h> |
| 4 | #include "symbol.h" | 6 | #include "symbol.h" |
| 5 | 7 | ||
| 8 | struct map_groups { | ||
| 9 | struct rb_root maps[MAP__NR_TYPES]; | ||
| 10 | struct list_head removed_maps[MAP__NR_TYPES]; | ||
| 11 | }; | ||
| 12 | |||
| 6 | struct thread { | 13 | struct thread { |
| 7 | struct rb_node rb_node; | 14 | struct rb_node rb_node; |
| 8 | struct list_head maps; | 15 | struct map_groups mg; |
| 9 | pid_t pid; | 16 | pid_t pid; |
| 10 | char shortname[3]; | 17 | char shortname[3]; |
| 11 | char *comm; | 18 | char *comm; |
| 19 | int comm_len; | ||
| 12 | }; | 20 | }; |
| 13 | 21 | ||
| 22 | void map_groups__init(struct map_groups *self); | ||
| 14 | int thread__set_comm(struct thread *self, const char *comm); | 23 | int thread__set_comm(struct thread *self, const char *comm); |
| 15 | struct thread * | 24 | int thread__comm_len(struct thread *self); |
| 16 | threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match); | 25 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
| 17 | struct thread * | ||
| 18 | register_idle_thread(struct rb_root *threads, struct thread **last_match); | ||
| 19 | void thread__insert_map(struct thread *self, struct map *map); | 26 | void thread__insert_map(struct thread *self, struct map *map); |
| 20 | int thread__fork(struct thread *self, struct thread *parent); | 27 | int thread__fork(struct thread *self, struct thread *parent); |
| 21 | struct map *thread__find_map(struct thread *self, u64 ip); | 28 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); |
| 22 | size_t threads__fprintf(FILE *fp, struct rb_root *threads); | 29 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); |
| 30 | |||
| 31 | void maps__insert(struct rb_root *maps, struct map *map); | ||
| 32 | struct map *maps__find(struct rb_root *maps, u64 addr); | ||
| 33 | |||
| 34 | static inline void map_groups__insert(struct map_groups *self, struct map *map) | ||
| 35 | { | ||
| 36 | maps__insert(&self->maps[map->type], map); | ||
| 37 | } | ||
| 38 | |||
| 39 | static inline struct map *map_groups__find(struct map_groups *self, | ||
| 40 | enum map_type type, u64 addr) | ||
| 41 | { | ||
| 42 | return maps__find(&self->maps[type], addr); | ||
| 43 | } | ||
| 44 | |||
| 45 | static inline struct map *thread__find_map(struct thread *self, | ||
| 46 | enum map_type type, u64 addr) | ||
| 47 | { | ||
| 48 | return self ? map_groups__find(&self->mg, type, addr) : NULL; | ||
| 49 | } | ||
| 50 | |||
| 51 | void thread__find_addr_location(struct thread *self, | ||
| 52 | struct perf_session *session, u8 cpumode, | ||
| 53 | enum map_type type, u64 addr, | ||
| 54 | struct addr_location *al, | ||
| 55 | symbol_filter_t filter); | ||
| 56 | struct symbol *map_groups__find_symbol(struct map_groups *self, | ||
| 57 | struct perf_session *session, | ||
| 58 | enum map_type type, u64 addr, | ||
| 59 | symbol_filter_t filter); | ||
| 60 | |||
| 61 | static inline struct symbol * | ||
| 62 | map_groups__find_function(struct map_groups *self, struct perf_session *session, | ||
| 63 | u64 addr, symbol_filter_t filter) | ||
| 64 | { | ||
| 65 | return map_groups__find_symbol(self, session, MAP__FUNCTION, addr, filter); | ||
| 66 | } | ||
| 67 | |||
| 68 | struct map *map_groups__find_by_name(struct map_groups *self, | ||
| 69 | enum map_type type, const char *name); | ||
| 70 | #endif /* __PERF_THREAD_H */ | ||
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index af4b0573b37f..cace35595530 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
| @@ -33,11 +33,11 @@ | |||
| 33 | #include <ctype.h> | 33 | #include <ctype.h> |
| 34 | #include <errno.h> | 34 | #include <errno.h> |
| 35 | #include <stdbool.h> | 35 | #include <stdbool.h> |
| 36 | #include <linux/kernel.h> | ||
| 36 | 37 | ||
| 37 | #include "../perf.h" | 38 | #include "../perf.h" |
| 38 | #include "trace-event.h" | 39 | #include "trace-event.h" |
| 39 | 40 | ||
| 40 | |||
| 41 | #define VERSION "0.5" | 41 | #define VERSION "0.5" |
| 42 | 42 | ||
| 43 | #define _STR(x) #x | 43 | #define _STR(x) #x |
| @@ -483,27 +483,33 @@ static struct tracepoint_path * | |||
| 483 | get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) | 483 | get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) |
| 484 | { | 484 | { |
| 485 | struct tracepoint_path path, *ppath = &path; | 485 | struct tracepoint_path path, *ppath = &path; |
| 486 | int i; | 486 | int i, nr_tracepoints = 0; |
| 487 | 487 | ||
| 488 | for (i = 0; i < nb_events; i++) { | 488 | for (i = 0; i < nb_events; i++) { |
| 489 | if (pattrs[i].type != PERF_TYPE_TRACEPOINT) | 489 | if (pattrs[i].type != PERF_TYPE_TRACEPOINT) |
| 490 | continue; | 490 | continue; |
| 491 | ++nr_tracepoints; | ||
| 491 | ppath->next = tracepoint_id_to_path(pattrs[i].config); | 492 | ppath->next = tracepoint_id_to_path(pattrs[i].config); |
| 492 | if (!ppath->next) | 493 | if (!ppath->next) |
| 493 | die("%s\n", "No memory to alloc tracepoints list"); | 494 | die("%s\n", "No memory to alloc tracepoints list"); |
| 494 | ppath = ppath->next; | 495 | ppath = ppath->next; |
| 495 | } | 496 | } |
| 496 | 497 | ||
| 497 | return path.next; | 498 | return nr_tracepoints > 0 ? path.next : NULL; |
| 498 | } | 499 | } |
| 499 | void read_tracing_data(struct perf_event_attr *pattrs, int nb_events) | 500 | |
| 501 | int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) | ||
| 500 | { | 502 | { |
| 501 | char buf[BUFSIZ]; | 503 | char buf[BUFSIZ]; |
| 502 | struct tracepoint_path *tps; | 504 | struct tracepoint_path *tps = get_tracepoints_path(pattrs, nb_events); |
| 505 | |||
| 506 | /* | ||
| 507 | * What? No tracepoints? No sense writing anything here, bail out. | ||
| 508 | */ | ||
| 509 | if (tps == NULL) | ||
| 510 | return -1; | ||
| 503 | 511 | ||
| 504 | output_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); | 512 | output_fd = fd; |
| 505 | if (output_fd < 0) | ||
| 506 | die("creating file '%s'", output_file); | ||
| 507 | 513 | ||
| 508 | buf[0] = 23; | 514 | buf[0] = 23; |
| 509 | buf[1] = 8; | 515 | buf[1] = 8; |
| @@ -530,11 +536,11 @@ void read_tracing_data(struct perf_event_attr *pattrs, int nb_events) | |||
| 530 | page_size = getpagesize(); | 536 | page_size = getpagesize(); |
| 531 | write_or_die(&page_size, 4); | 537 | write_or_die(&page_size, 4); |
| 532 | 538 | ||
| 533 | tps = get_tracepoints_path(pattrs, nb_events); | ||
| 534 | |||
| 535 | read_header_files(); | 539 | read_header_files(); |
| 536 | read_ftrace_files(tps); | 540 | read_ftrace_files(tps); |
| 537 | read_event_files(tps); | 541 | read_event_files(tps); |
| 538 | read_proc_kallsyms(); | 542 | read_proc_kallsyms(); |
| 539 | read_ftrace_printk(); | 543 | read_ftrace_printk(); |
| 544 | |||
| 545 | return 0; | ||
| 540 | } | 546 | } |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 55c9659a56e2..c5c32be040bf 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
| @@ -40,12 +40,19 @@ int header_page_size_size; | |||
| 40 | int header_page_data_offset; | 40 | int header_page_data_offset; |
| 41 | int header_page_data_size; | 41 | int header_page_data_size; |
| 42 | 42 | ||
| 43 | int latency_format; | ||
| 44 | |||
| 43 | static char *input_buf; | 45 | static char *input_buf; |
| 44 | static unsigned long long input_buf_ptr; | 46 | static unsigned long long input_buf_ptr; |
| 45 | static unsigned long long input_buf_siz; | 47 | static unsigned long long input_buf_siz; |
| 46 | 48 | ||
| 47 | static int cpus; | 49 | static int cpus; |
| 48 | static int long_size; | 50 | static int long_size; |
| 51 | static int is_flag_field; | ||
| 52 | static int is_symbolic_field; | ||
| 53 | |||
| 54 | static struct format_field * | ||
| 55 | find_any_field(struct event *event, const char *name); | ||
| 49 | 56 | ||
| 50 | static void init_input_buf(char *buf, unsigned long long size) | 57 | static void init_input_buf(char *buf, unsigned long long size) |
| 51 | { | 58 | { |
| @@ -170,7 +177,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused) | |||
| 170 | func_count++; | 177 | func_count++; |
| 171 | } | 178 | } |
| 172 | 179 | ||
| 173 | func_list = malloc_or_die(sizeof(*func_list) * func_count + 1); | 180 | func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1)); |
| 174 | 181 | ||
| 175 | i = 0; | 182 | i = 0; |
| 176 | while (list) { | 183 | while (list) { |
| @@ -284,18 +291,19 @@ void parse_ftrace_printk(char *file, unsigned int size __unused) | |||
| 284 | char *line; | 291 | char *line; |
| 285 | char *next = NULL; | 292 | char *next = NULL; |
| 286 | char *addr_str; | 293 | char *addr_str; |
| 287 | int ret; | ||
| 288 | int i; | 294 | int i; |
| 289 | 295 | ||
| 290 | line = strtok_r(file, "\n", &next); | 296 | line = strtok_r(file, "\n", &next); |
| 291 | while (line) { | 297 | while (line) { |
| 298 | addr_str = strsep(&line, ":"); | ||
| 299 | if (!line) { | ||
| 300 | warning("error parsing print strings"); | ||
| 301 | break; | ||
| 302 | } | ||
| 292 | item = malloc_or_die(sizeof(*item)); | 303 | item = malloc_or_die(sizeof(*item)); |
| 293 | ret = sscanf(line, "%as : %as", | ||
| 294 | (float *)(void *)&addr_str, /* workaround gcc warning */ | ||
| 295 | (float *)(void *)&item->printk); | ||
| 296 | item->addr = strtoull(addr_str, NULL, 16); | 304 | item->addr = strtoull(addr_str, NULL, 16); |
| 297 | free(addr_str); | 305 | /* fmt still has a space, skip it */ |
| 298 | 306 | item->printk = strdup(line+1); | |
| 299 | item->next = list; | 307 | item->next = list; |
| 300 | list = item; | 308 | list = item; |
| 301 | line = strtok_r(NULL, "\n", &next); | 309 | line = strtok_r(NULL, "\n", &next); |
| @@ -522,7 +530,10 @@ static enum event_type __read_token(char **tok) | |||
| 522 | last_ch = ch; | 530 | last_ch = ch; |
| 523 | ch = __read_char(); | 531 | ch = __read_char(); |
| 524 | buf[i++] = ch; | 532 | buf[i++] = ch; |
| 525 | } while (ch != quote_ch && last_ch != '\\'); | 533 | /* the '\' '\' will cancel itself */ |
| 534 | if (ch == '\\' && last_ch == '\\') | ||
| 535 | last_ch = 0; | ||
| 536 | } while (ch != quote_ch || last_ch == '\\'); | ||
| 526 | /* remove the last quote */ | 537 | /* remove the last quote */ |
| 527 | i--; | 538 | i--; |
| 528 | goto out; | 539 | goto out; |
| @@ -610,7 +621,7 @@ static enum event_type read_token_item(char **tok) | |||
| 610 | static int test_type(enum event_type type, enum event_type expect) | 621 | static int test_type(enum event_type type, enum event_type expect) |
| 611 | { | 622 | { |
| 612 | if (type != expect) { | 623 | if (type != expect) { |
| 613 | die("Error: expected type %d but read %d", | 624 | warning("Error: expected type %d but read %d", |
| 614 | expect, type); | 625 | expect, type); |
| 615 | return -1; | 626 | return -1; |
| 616 | } | 627 | } |
| @@ -621,13 +632,13 @@ static int test_type_token(enum event_type type, char *token, | |||
| 621 | enum event_type expect, const char *expect_tok) | 632 | enum event_type expect, const char *expect_tok) |
| 622 | { | 633 | { |
| 623 | if (type != expect) { | 634 | if (type != expect) { |
| 624 | die("Error: expected type %d but read %d", | 635 | warning("Error: expected type %d but read %d", |
| 625 | expect, type); | 636 | expect, type); |
| 626 | return -1; | 637 | return -1; |
| 627 | } | 638 | } |
| 628 | 639 | ||
| 629 | if (strcmp(token, expect_tok) != 0) { | 640 | if (strcmp(token, expect_tok) != 0) { |
| 630 | die("Error: expected '%s' but read '%s'", | 641 | warning("Error: expected '%s' but read '%s'", |
| 631 | expect_tok, token); | 642 | expect_tok, token); |
| 632 | return -1; | 643 | return -1; |
| 633 | } | 644 | } |
| @@ -665,7 +676,7 @@ static int __read_expected(enum event_type expect, const char *str, int newline_ | |||
| 665 | 676 | ||
| 666 | free_token(token); | 677 | free_token(token); |
| 667 | 678 | ||
| 668 | return 0; | 679 | return ret; |
| 669 | } | 680 | } |
| 670 | 681 | ||
| 671 | static int read_expected(enum event_type expect, const char *str) | 682 | static int read_expected(enum event_type expect, const char *str) |
| @@ -682,10 +693,10 @@ static char *event_read_name(void) | |||
| 682 | { | 693 | { |
| 683 | char *token; | 694 | char *token; |
| 684 | 695 | ||
| 685 | if (read_expected(EVENT_ITEM, (char *)"name") < 0) | 696 | if (read_expected(EVENT_ITEM, "name") < 0) |
| 686 | return NULL; | 697 | return NULL; |
| 687 | 698 | ||
| 688 | if (read_expected(EVENT_OP, (char *)":") < 0) | 699 | if (read_expected(EVENT_OP, ":") < 0) |
| 689 | return NULL; | 700 | return NULL; |
| 690 | 701 | ||
| 691 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 702 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
| @@ -703,10 +714,10 @@ static int event_read_id(void) | |||
| 703 | char *token; | 714 | char *token; |
| 704 | int id; | 715 | int id; |
| 705 | 716 | ||
| 706 | if (read_expected_item(EVENT_ITEM, (char *)"ID") < 0) | 717 | if (read_expected_item(EVENT_ITEM, "ID") < 0) |
| 707 | return -1; | 718 | return -1; |
| 708 | 719 | ||
| 709 | if (read_expected(EVENT_OP, (char *)":") < 0) | 720 | if (read_expected(EVENT_OP, ":") < 0) |
| 710 | return -1; | 721 | return -1; |
| 711 | 722 | ||
| 712 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 723 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
| @@ -721,6 +732,24 @@ static int event_read_id(void) | |||
| 721 | return -1; | 732 | return -1; |
| 722 | } | 733 | } |
| 723 | 734 | ||
| 735 | static int field_is_string(struct format_field *field) | ||
| 736 | { | ||
| 737 | if ((field->flags & FIELD_IS_ARRAY) && | ||
| 738 | (!strstr(field->type, "char") || !strstr(field->type, "u8") || | ||
| 739 | !strstr(field->type, "s8"))) | ||
| 740 | return 1; | ||
| 741 | |||
| 742 | return 0; | ||
| 743 | } | ||
| 744 | |||
| 745 | static int field_is_dynamic(struct format_field *field) | ||
| 746 | { | ||
| 747 | if (!strcmp(field->type, "__data_loc")) | ||
| 748 | return 1; | ||
| 749 | |||
| 750 | return 0; | ||
| 751 | } | ||
| 752 | |||
| 724 | static int event_read_fields(struct event *event, struct format_field **fields) | 753 | static int event_read_fields(struct event *event, struct format_field **fields) |
| 725 | { | 754 | { |
| 726 | struct format_field *field = NULL; | 755 | struct format_field *field = NULL; |
| @@ -738,7 +767,7 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
| 738 | 767 | ||
| 739 | count++; | 768 | count++; |
| 740 | 769 | ||
| 741 | if (test_type_token(type, token, EVENT_ITEM, (char *)"field")) | 770 | if (test_type_token(type, token, EVENT_ITEM, "field")) |
| 742 | goto fail; | 771 | goto fail; |
| 743 | free_token(token); | 772 | free_token(token); |
| 744 | 773 | ||
| @@ -753,7 +782,7 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
| 753 | type = read_token(&token); | 782 | type = read_token(&token); |
| 754 | } | 783 | } |
| 755 | 784 | ||
| 756 | if (test_type_token(type, token, EVENT_OP, (char *)":") < 0) | 785 | if (test_type_token(type, token, EVENT_OP, ":") < 0) |
| 757 | return -1; | 786 | return -1; |
| 758 | 787 | ||
| 759 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 788 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
| @@ -865,14 +894,20 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
| 865 | free(brackets); | 894 | free(brackets); |
| 866 | } | 895 | } |
| 867 | 896 | ||
| 868 | if (test_type_token(type, token, EVENT_OP, (char *)";")) | 897 | if (field_is_string(field)) { |
| 898 | field->flags |= FIELD_IS_STRING; | ||
| 899 | if (field_is_dynamic(field)) | ||
| 900 | field->flags |= FIELD_IS_DYNAMIC; | ||
| 901 | } | ||
| 902 | |||
| 903 | if (test_type_token(type, token, EVENT_OP, ";")) | ||
| 869 | goto fail; | 904 | goto fail; |
| 870 | free_token(token); | 905 | free_token(token); |
| 871 | 906 | ||
| 872 | if (read_expected(EVENT_ITEM, (char *)"offset") < 0) | 907 | if (read_expected(EVENT_ITEM, "offset") < 0) |
| 873 | goto fail_expect; | 908 | goto fail_expect; |
| 874 | 909 | ||
| 875 | if (read_expected(EVENT_OP, (char *)":") < 0) | 910 | if (read_expected(EVENT_OP, ":") < 0) |
| 876 | goto fail_expect; | 911 | goto fail_expect; |
| 877 | 912 | ||
| 878 | if (read_expect_type(EVENT_ITEM, &token)) | 913 | if (read_expect_type(EVENT_ITEM, &token)) |
| @@ -880,13 +915,13 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
| 880 | field->offset = strtoul(token, NULL, 0); | 915 | field->offset = strtoul(token, NULL, 0); |
| 881 | free_token(token); | 916 | free_token(token); |
| 882 | 917 | ||
| 883 | if (read_expected(EVENT_OP, (char *)";") < 0) | 918 | if (read_expected(EVENT_OP, ";") < 0) |
| 884 | goto fail_expect; | 919 | goto fail_expect; |
| 885 | 920 | ||
| 886 | if (read_expected(EVENT_ITEM, (char *)"size") < 0) | 921 | if (read_expected(EVENT_ITEM, "size") < 0) |
| 887 | goto fail_expect; | 922 | goto fail_expect; |
| 888 | 923 | ||
| 889 | if (read_expected(EVENT_OP, (char *)":") < 0) | 924 | if (read_expected(EVENT_OP, ":") < 0) |
| 890 | goto fail_expect; | 925 | goto fail_expect; |
| 891 | 926 | ||
| 892 | if (read_expect_type(EVENT_ITEM, &token)) | 927 | if (read_expect_type(EVENT_ITEM, &token)) |
| @@ -894,11 +929,34 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
| 894 | field->size = strtoul(token, NULL, 0); | 929 | field->size = strtoul(token, NULL, 0); |
| 895 | free_token(token); | 930 | free_token(token); |
| 896 | 931 | ||
| 897 | if (read_expected(EVENT_OP, (char *)";") < 0) | 932 | if (read_expected(EVENT_OP, ";") < 0) |
| 898 | goto fail_expect; | 933 | goto fail_expect; |
| 899 | 934 | ||
| 900 | if (read_expect_type(EVENT_NEWLINE, &token) < 0) | 935 | type = read_token(&token); |
| 901 | goto fail; | 936 | if (type != EVENT_NEWLINE) { |
| 937 | /* newer versions of the kernel have a "signed" type */ | ||
| 938 | if (test_type_token(type, token, EVENT_ITEM, "signed")) | ||
| 939 | goto fail; | ||
| 940 | |||
| 941 | free_token(token); | ||
| 942 | |||
| 943 | if (read_expected(EVENT_OP, ":") < 0) | ||
| 944 | goto fail_expect; | ||
| 945 | |||
| 946 | if (read_expect_type(EVENT_ITEM, &token)) | ||
| 947 | goto fail; | ||
| 948 | |||
| 949 | if (strtoul(token, NULL, 0)) | ||
| 950 | field->flags |= FIELD_IS_SIGNED; | ||
| 951 | |||
| 952 | free_token(token); | ||
| 953 | if (read_expected(EVENT_OP, ";") < 0) | ||
| 954 | goto fail_expect; | ||
| 955 | |||
| 956 | if (read_expect_type(EVENT_NEWLINE, &token)) | ||
| 957 | goto fail; | ||
| 958 | } | ||
| 959 | |||
| 902 | free_token(token); | 960 | free_token(token); |
| 903 | 961 | ||
| 904 | *fields = field; | 962 | *fields = field; |
| @@ -921,10 +979,10 @@ static int event_read_format(struct event *event) | |||
| 921 | char *token; | 979 | char *token; |
| 922 | int ret; | 980 | int ret; |
| 923 | 981 | ||
| 924 | if (read_expected_item(EVENT_ITEM, (char *)"format") < 0) | 982 | if (read_expected_item(EVENT_ITEM, "format") < 0) |
| 925 | return -1; | 983 | return -1; |
| 926 | 984 | ||
| 927 | if (read_expected(EVENT_OP, (char *)":") < 0) | 985 | if (read_expected(EVENT_OP, ":") < 0) |
| 928 | return -1; | 986 | return -1; |
| 929 | 987 | ||
| 930 | if (read_expect_type(EVENT_NEWLINE, &token)) | 988 | if (read_expect_type(EVENT_NEWLINE, &token)) |
| @@ -984,7 +1042,7 @@ process_cond(struct event *event, struct print_arg *top, char **tok) | |||
| 984 | 1042 | ||
| 985 | *tok = NULL; | 1043 | *tok = NULL; |
| 986 | type = process_arg(event, left, &token); | 1044 | type = process_arg(event, left, &token); |
| 987 | if (test_type_token(type, token, EVENT_OP, (char *)":")) | 1045 | if (test_type_token(type, token, EVENT_OP, ":")) |
| 988 | goto out_free; | 1046 | goto out_free; |
| 989 | 1047 | ||
| 990 | arg->op.op = token; | 1048 | arg->op.op = token; |
| @@ -1004,6 +1062,35 @@ out_free: | |||
| 1004 | return EVENT_ERROR; | 1062 | return EVENT_ERROR; |
| 1005 | } | 1063 | } |
| 1006 | 1064 | ||
| 1065 | static enum event_type | ||
| 1066 | process_array(struct event *event, struct print_arg *top, char **tok) | ||
| 1067 | { | ||
| 1068 | struct print_arg *arg; | ||
| 1069 | enum event_type type; | ||
| 1070 | char *token = NULL; | ||
| 1071 | |||
| 1072 | arg = malloc_or_die(sizeof(*arg)); | ||
| 1073 | memset(arg, 0, sizeof(*arg)); | ||
| 1074 | |||
| 1075 | *tok = NULL; | ||
| 1076 | type = process_arg(event, arg, &token); | ||
| 1077 | if (test_type_token(type, token, EVENT_OP, "]")) | ||
| 1078 | goto out_free; | ||
| 1079 | |||
| 1080 | top->op.right = arg; | ||
| 1081 | |||
| 1082 | free_token(token); | ||
| 1083 | type = read_token_item(&token); | ||
| 1084 | *tok = token; | ||
| 1085 | |||
| 1086 | return type; | ||
| 1087 | |||
| 1088 | out_free: | ||
| 1089 | free_token(*tok); | ||
| 1090 | free_arg(arg); | ||
| 1091 | return EVENT_ERROR; | ||
| 1092 | } | ||
| 1093 | |||
| 1007 | static int get_op_prio(char *op) | 1094 | static int get_op_prio(char *op) |
| 1008 | { | 1095 | { |
| 1009 | if (!op[1]) { | 1096 | if (!op[1]) { |
| @@ -1128,6 +1215,8 @@ process_op(struct event *event, struct print_arg *arg, char **tok) | |||
| 1128 | strcmp(token, "*") == 0 || | 1215 | strcmp(token, "*") == 0 || |
| 1129 | strcmp(token, "^") == 0 || | 1216 | strcmp(token, "^") == 0 || |
| 1130 | strcmp(token, "/") == 0 || | 1217 | strcmp(token, "/") == 0 || |
| 1218 | strcmp(token, "<") == 0 || | ||
| 1219 | strcmp(token, ">") == 0 || | ||
| 1131 | strcmp(token, "==") == 0 || | 1220 | strcmp(token, "==") == 0 || |
| 1132 | strcmp(token, "!=") == 0) { | 1221 | strcmp(token, "!=") == 0) { |
| 1133 | 1222 | ||
| @@ -1144,17 +1233,46 @@ process_op(struct event *event, struct print_arg *arg, char **tok) | |||
| 1144 | 1233 | ||
| 1145 | right = malloc_or_die(sizeof(*right)); | 1234 | right = malloc_or_die(sizeof(*right)); |
| 1146 | 1235 | ||
| 1147 | type = process_arg(event, right, tok); | 1236 | type = read_token_item(&token); |
| 1237 | *tok = token; | ||
| 1238 | |||
| 1239 | /* could just be a type pointer */ | ||
| 1240 | if ((strcmp(arg->op.op, "*") == 0) && | ||
| 1241 | type == EVENT_DELIM && (strcmp(token, ")") == 0)) { | ||
| 1242 | if (left->type != PRINT_ATOM) | ||
| 1243 | die("bad pointer type"); | ||
| 1244 | left->atom.atom = realloc(left->atom.atom, | ||
| 1245 | sizeof(left->atom.atom) + 3); | ||
| 1246 | strcat(left->atom.atom, " *"); | ||
| 1247 | *arg = *left; | ||
| 1248 | free(arg); | ||
| 1249 | |||
| 1250 | return type; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | type = process_arg_token(event, right, tok, type); | ||
| 1148 | 1254 | ||
| 1149 | arg->op.right = right; | 1255 | arg->op.right = right; |
| 1150 | 1256 | ||
| 1257 | } else if (strcmp(token, "[") == 0) { | ||
| 1258 | |||
| 1259 | left = malloc_or_die(sizeof(*left)); | ||
| 1260 | *left = *arg; | ||
| 1261 | |||
| 1262 | arg->type = PRINT_OP; | ||
| 1263 | arg->op.op = token; | ||
| 1264 | arg->op.left = left; | ||
| 1265 | |||
| 1266 | arg->op.prio = 0; | ||
| 1267 | type = process_array(event, arg, tok); | ||
| 1268 | |||
| 1151 | } else { | 1269 | } else { |
| 1152 | die("unknown op '%s'", token); | 1270 | warning("unknown op '%s'", token); |
| 1271 | event->flags |= EVENT_FL_FAILED; | ||
| 1153 | /* the arg is now the left side */ | 1272 | /* the arg is now the left side */ |
| 1154 | return EVENT_NONE; | 1273 | return EVENT_NONE; |
| 1155 | } | 1274 | } |
| 1156 | 1275 | ||
| 1157 | |||
| 1158 | if (type == EVENT_OP) { | 1276 | if (type == EVENT_OP) { |
| 1159 | int prio; | 1277 | int prio; |
| 1160 | 1278 | ||
| @@ -1178,7 +1296,7 @@ process_entry(struct event *event __unused, struct print_arg *arg, | |||
| 1178 | char *field; | 1296 | char *field; |
| 1179 | char *token; | 1297 | char *token; |
| 1180 | 1298 | ||
| 1181 | if (read_expected(EVENT_OP, (char *)"->") < 0) | 1299 | if (read_expected(EVENT_OP, "->") < 0) |
| 1182 | return EVENT_ERROR; | 1300 | return EVENT_ERROR; |
| 1183 | 1301 | ||
| 1184 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 1302 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
| @@ -1188,6 +1306,16 @@ process_entry(struct event *event __unused, struct print_arg *arg, | |||
| 1188 | arg->type = PRINT_FIELD; | 1306 | arg->type = PRINT_FIELD; |
| 1189 | arg->field.name = field; | 1307 | arg->field.name = field; |
| 1190 | 1308 | ||
| 1309 | if (is_flag_field) { | ||
| 1310 | arg->field.field = find_any_field(event, arg->field.name); | ||
| 1311 | arg->field.field->flags |= FIELD_IS_FLAG; | ||
| 1312 | is_flag_field = 0; | ||
| 1313 | } else if (is_symbolic_field) { | ||
| 1314 | arg->field.field = find_any_field(event, arg->field.name); | ||
| 1315 | arg->field.field->flags |= FIELD_IS_SYMBOLIC; | ||
| 1316 | is_symbolic_field = 0; | ||
| 1317 | } | ||
| 1318 | |||
| 1191 | type = read_token(&token); | 1319 | type = read_token(&token); |
| 1192 | *tok = token; | 1320 | *tok = token; |
| 1193 | 1321 | ||
| @@ -1338,25 +1466,25 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok) | |||
| 1338 | do { | 1466 | do { |
| 1339 | free_token(token); | 1467 | free_token(token); |
| 1340 | type = read_token_item(&token); | 1468 | type = read_token_item(&token); |
| 1341 | if (test_type_token(type, token, EVENT_OP, (char *)"{")) | 1469 | if (test_type_token(type, token, EVENT_OP, "{")) |
| 1342 | break; | 1470 | break; |
| 1343 | 1471 | ||
| 1344 | arg = malloc_or_die(sizeof(*arg)); | 1472 | arg = malloc_or_die(sizeof(*arg)); |
| 1345 | 1473 | ||
| 1346 | free_token(token); | 1474 | free_token(token); |
| 1347 | type = process_arg(event, arg, &token); | 1475 | type = process_arg(event, arg, &token); |
| 1348 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1476 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
| 1349 | goto out_free; | 1477 | goto out_free; |
| 1350 | 1478 | ||
| 1351 | field = malloc_or_die(sizeof(*field)); | 1479 | field = malloc_or_die(sizeof(*field)); |
| 1352 | memset(field, 0, sizeof(field)); | 1480 | memset(field, 0, sizeof(*field)); |
| 1353 | 1481 | ||
| 1354 | value = arg_eval(arg); | 1482 | value = arg_eval(arg); |
| 1355 | field->value = strdup(value); | 1483 | field->value = strdup(value); |
| 1356 | 1484 | ||
| 1357 | free_token(token); | 1485 | free_token(token); |
| 1358 | type = process_arg(event, arg, &token); | 1486 | type = process_arg(event, arg, &token); |
| 1359 | if (test_type_token(type, token, EVENT_OP, (char *)"}")) | 1487 | if (test_type_token(type, token, EVENT_OP, "}")) |
| 1360 | goto out_free; | 1488 | goto out_free; |
| 1361 | 1489 | ||
| 1362 | value = arg_eval(arg); | 1490 | value = arg_eval(arg); |
| @@ -1391,13 +1519,13 @@ process_flags(struct event *event, struct print_arg *arg, char **tok) | |||
| 1391 | memset(arg, 0, sizeof(*arg)); | 1519 | memset(arg, 0, sizeof(*arg)); |
| 1392 | arg->type = PRINT_FLAGS; | 1520 | arg->type = PRINT_FLAGS; |
| 1393 | 1521 | ||
| 1394 | if (read_expected_item(EVENT_DELIM, (char *)"(") < 0) | 1522 | if (read_expected_item(EVENT_DELIM, "(") < 0) |
| 1395 | return EVENT_ERROR; | 1523 | return EVENT_ERROR; |
| 1396 | 1524 | ||
| 1397 | field = malloc_or_die(sizeof(*field)); | 1525 | field = malloc_or_die(sizeof(*field)); |
| 1398 | 1526 | ||
| 1399 | type = process_arg(event, field, &token); | 1527 | type = process_arg(event, field, &token); |
| 1400 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1528 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
| 1401 | goto out_free; | 1529 | goto out_free; |
| 1402 | 1530 | ||
| 1403 | arg->flags.field = field; | 1531 | arg->flags.field = field; |
| @@ -1408,11 +1536,11 @@ process_flags(struct event *event, struct print_arg *arg, char **tok) | |||
| 1408 | type = read_token_item(&token); | 1536 | type = read_token_item(&token); |
| 1409 | } | 1537 | } |
| 1410 | 1538 | ||
| 1411 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1539 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
| 1412 | goto out_free; | 1540 | goto out_free; |
| 1413 | 1541 | ||
| 1414 | type = process_fields(event, &arg->flags.flags, &token); | 1542 | type = process_fields(event, &arg->flags.flags, &token); |
| 1415 | if (test_type_token(type, token, EVENT_DELIM, (char *)")")) | 1543 | if (test_type_token(type, token, EVENT_DELIM, ")")) |
| 1416 | goto out_free; | 1544 | goto out_free; |
| 1417 | 1545 | ||
| 1418 | free_token(token); | 1546 | free_token(token); |
| @@ -1434,19 +1562,19 @@ process_symbols(struct event *event, struct print_arg *arg, char **tok) | |||
| 1434 | memset(arg, 0, sizeof(*arg)); | 1562 | memset(arg, 0, sizeof(*arg)); |
| 1435 | arg->type = PRINT_SYMBOL; | 1563 | arg->type = PRINT_SYMBOL; |
| 1436 | 1564 | ||
| 1437 | if (read_expected_item(EVENT_DELIM, (char *)"(") < 0) | 1565 | if (read_expected_item(EVENT_DELIM, "(") < 0) |
| 1438 | return EVENT_ERROR; | 1566 | return EVENT_ERROR; |
| 1439 | 1567 | ||
| 1440 | field = malloc_or_die(sizeof(*field)); | 1568 | field = malloc_or_die(sizeof(*field)); |
| 1441 | 1569 | ||
| 1442 | type = process_arg(event, field, &token); | 1570 | type = process_arg(event, field, &token); |
| 1443 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1571 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
| 1444 | goto out_free; | 1572 | goto out_free; |
| 1445 | 1573 | ||
| 1446 | arg->symbol.field = field; | 1574 | arg->symbol.field = field; |
| 1447 | 1575 | ||
| 1448 | type = process_fields(event, &arg->symbol.symbols, &token); | 1576 | type = process_fields(event, &arg->symbol.symbols, &token); |
| 1449 | if (test_type_token(type, token, EVENT_DELIM, (char *)")")) | 1577 | if (test_type_token(type, token, EVENT_DELIM, ")")) |
| 1450 | goto out_free; | 1578 | goto out_free; |
| 1451 | 1579 | ||
| 1452 | free_token(token); | 1580 | free_token(token); |
| @@ -1463,7 +1591,6 @@ process_paren(struct event *event, struct print_arg *arg, char **tok) | |||
| 1463 | { | 1591 | { |
| 1464 | struct print_arg *item_arg; | 1592 | struct print_arg *item_arg; |
| 1465 | enum event_type type; | 1593 | enum event_type type; |
| 1466 | int ptr_cast = 0; | ||
| 1467 | char *token; | 1594 | char *token; |
| 1468 | 1595 | ||
| 1469 | type = process_arg(event, arg, &token); | 1596 | type = process_arg(event, arg, &token); |
| @@ -1471,28 +1598,13 @@ process_paren(struct event *event, struct print_arg *arg, char **tok) | |||
| 1471 | if (type == EVENT_ERROR) | 1598 | if (type == EVENT_ERROR) |
| 1472 | return EVENT_ERROR; | 1599 | return EVENT_ERROR; |
| 1473 | 1600 | ||
| 1474 | if (type == EVENT_OP) { | 1601 | if (type == EVENT_OP) |
| 1475 | /* handle the ptr casts */ | 1602 | type = process_op(event, arg, &token); |
| 1476 | if (!strcmp(token, "*")) { | ||
| 1477 | /* | ||
| 1478 | * FIXME: should we zapp whitespaces before ')' ? | ||
| 1479 | * (may require a peek_token_item()) | ||
| 1480 | */ | ||
| 1481 | if (__peek_char() == ')') { | ||
| 1482 | ptr_cast = 1; | ||
| 1483 | free_token(token); | ||
| 1484 | type = read_token_item(&token); | ||
| 1485 | } | ||
| 1486 | } | ||
| 1487 | if (!ptr_cast) { | ||
| 1488 | type = process_op(event, arg, &token); | ||
| 1489 | 1603 | ||
| 1490 | if (type == EVENT_ERROR) | 1604 | if (type == EVENT_ERROR) |
| 1491 | return EVENT_ERROR; | 1605 | return EVENT_ERROR; |
| 1492 | } | ||
| 1493 | } | ||
| 1494 | 1606 | ||
| 1495 | if (test_type_token(type, token, EVENT_DELIM, (char *)")")) { | 1607 | if (test_type_token(type, token, EVENT_DELIM, ")")) { |
| 1496 | free_token(token); | 1608 | free_token(token); |
| 1497 | return EVENT_ERROR; | 1609 | return EVENT_ERROR; |
| 1498 | } | 1610 | } |
| @@ -1516,13 +1628,6 @@ process_paren(struct event *event, struct print_arg *arg, char **tok) | |||
| 1516 | item_arg = malloc_or_die(sizeof(*item_arg)); | 1628 | item_arg = malloc_or_die(sizeof(*item_arg)); |
| 1517 | 1629 | ||
| 1518 | arg->type = PRINT_TYPE; | 1630 | arg->type = PRINT_TYPE; |
| 1519 | if (ptr_cast) { | ||
| 1520 | char *old = arg->atom.atom; | ||
| 1521 | |||
| 1522 | arg->atom.atom = malloc_or_die(strlen(old + 3)); | ||
| 1523 | sprintf(arg->atom.atom, "%s *", old); | ||
| 1524 | free(old); | ||
| 1525 | } | ||
| 1526 | arg->typecast.type = arg->atom.atom; | 1631 | arg->typecast.type = arg->atom.atom; |
| 1527 | arg->typecast.item = item_arg; | 1632 | arg->typecast.item = item_arg; |
| 1528 | type = process_arg_token(event, item_arg, &token, type); | 1633 | type = process_arg_token(event, item_arg, &token, type); |
| @@ -1540,7 +1645,7 @@ process_str(struct event *event __unused, struct print_arg *arg, char **tok) | |||
| 1540 | enum event_type type; | 1645 | enum event_type type; |
| 1541 | char *token; | 1646 | char *token; |
| 1542 | 1647 | ||
| 1543 | if (read_expected(EVENT_DELIM, (char *)"(") < 0) | 1648 | if (read_expected(EVENT_DELIM, "(") < 0) |
| 1544 | return EVENT_ERROR; | 1649 | return EVENT_ERROR; |
| 1545 | 1650 | ||
| 1546 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 1651 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
| @@ -1550,7 +1655,7 @@ process_str(struct event *event __unused, struct print_arg *arg, char **tok) | |||
| 1550 | arg->string.string = token; | 1655 | arg->string.string = token; |
| 1551 | arg->string.offset = -1; | 1656 | arg->string.offset = -1; |
| 1552 | 1657 | ||
| 1553 | if (read_expected(EVENT_DELIM, (char *)")") < 0) | 1658 | if (read_expected(EVENT_DELIM, ")") < 0) |
| 1554 | return EVENT_ERROR; | 1659 | return EVENT_ERROR; |
| 1555 | 1660 | ||
| 1556 | type = read_token(&token); | 1661 | type = read_token(&token); |
| @@ -1578,9 +1683,11 @@ process_arg_token(struct event *event, struct print_arg *arg, | |||
| 1578 | type = process_entry(event, arg, &token); | 1683 | type = process_entry(event, arg, &token); |
| 1579 | } else if (strcmp(token, "__print_flags") == 0) { | 1684 | } else if (strcmp(token, "__print_flags") == 0) { |
| 1580 | free_token(token); | 1685 | free_token(token); |
| 1686 | is_flag_field = 1; | ||
| 1581 | type = process_flags(event, arg, &token); | 1687 | type = process_flags(event, arg, &token); |
| 1582 | } else if (strcmp(token, "__print_symbolic") == 0) { | 1688 | } else if (strcmp(token, "__print_symbolic") == 0) { |
| 1583 | free_token(token); | 1689 | free_token(token); |
| 1690 | is_symbolic_field = 1; | ||
| 1584 | type = process_symbols(event, arg, &token); | 1691 | type = process_symbols(event, arg, &token); |
| 1585 | } else if (strcmp(token, "__get_str") == 0) { | 1692 | } else if (strcmp(token, "__get_str") == 0) { |
| 1586 | free_token(token); | 1693 | free_token(token); |
| @@ -1637,12 +1744,18 @@ process_arg_token(struct event *event, struct print_arg *arg, | |||
| 1637 | 1744 | ||
| 1638 | static int event_read_print_args(struct event *event, struct print_arg **list) | 1745 | static int event_read_print_args(struct event *event, struct print_arg **list) |
| 1639 | { | 1746 | { |
| 1640 | enum event_type type; | 1747 | enum event_type type = EVENT_ERROR; |
| 1641 | struct print_arg *arg; | 1748 | struct print_arg *arg; |
| 1642 | char *token; | 1749 | char *token; |
| 1643 | int args = 0; | 1750 | int args = 0; |
| 1644 | 1751 | ||
| 1645 | do { | 1752 | do { |
| 1753 | if (type == EVENT_NEWLINE) { | ||
| 1754 | free_token(token); | ||
| 1755 | type = read_token_item(&token); | ||
| 1756 | continue; | ||
| 1757 | } | ||
| 1758 | |||
| 1646 | arg = malloc_or_die(sizeof(*arg)); | 1759 | arg = malloc_or_die(sizeof(*arg)); |
| 1647 | memset(arg, 0, sizeof(*arg)); | 1760 | memset(arg, 0, sizeof(*arg)); |
| 1648 | 1761 | ||
| @@ -1683,18 +1796,19 @@ static int event_read_print(struct event *event) | |||
| 1683 | char *token; | 1796 | char *token; |
| 1684 | int ret; | 1797 | int ret; |
| 1685 | 1798 | ||
| 1686 | if (read_expected_item(EVENT_ITEM, (char *)"print") < 0) | 1799 | if (read_expected_item(EVENT_ITEM, "print") < 0) |
| 1687 | return -1; | 1800 | return -1; |
| 1688 | 1801 | ||
| 1689 | if (read_expected(EVENT_ITEM, (char *)"fmt") < 0) | 1802 | if (read_expected(EVENT_ITEM, "fmt") < 0) |
| 1690 | return -1; | 1803 | return -1; |
| 1691 | 1804 | ||
| 1692 | if (read_expected(EVENT_OP, (char *)":") < 0) | 1805 | if (read_expected(EVENT_OP, ":") < 0) |
| 1693 | return -1; | 1806 | return -1; |
| 1694 | 1807 | ||
| 1695 | if (read_expect_type(EVENT_DQUOTE, &token) < 0) | 1808 | if (read_expect_type(EVENT_DQUOTE, &token) < 0) |
| 1696 | goto fail; | 1809 | goto fail; |
| 1697 | 1810 | ||
| 1811 | concat: | ||
| 1698 | event->print_fmt.format = token; | 1812 | event->print_fmt.format = token; |
| 1699 | event->print_fmt.args = NULL; | 1813 | event->print_fmt.args = NULL; |
| 1700 | 1814 | ||
| @@ -1704,7 +1818,22 @@ static int event_read_print(struct event *event) | |||
| 1704 | if (type == EVENT_NONE) | 1818 | if (type == EVENT_NONE) |
| 1705 | return 0; | 1819 | return 0; |
| 1706 | 1820 | ||
| 1707 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1821 | /* Handle concatination of print lines */ |
| 1822 | if (type == EVENT_DQUOTE) { | ||
| 1823 | char *cat; | ||
| 1824 | |||
| 1825 | cat = malloc_or_die(strlen(event->print_fmt.format) + | ||
| 1826 | strlen(token) + 1); | ||
| 1827 | strcpy(cat, event->print_fmt.format); | ||
| 1828 | strcat(cat, token); | ||
| 1829 | free_token(token); | ||
| 1830 | free_token(event->print_fmt.format); | ||
| 1831 | event->print_fmt.format = NULL; | ||
| 1832 | token = cat; | ||
| 1833 | goto concat; | ||
| 1834 | } | ||
| 1835 | |||
| 1836 | if (test_type_token(type, token, EVENT_DELIM, ",")) | ||
| 1708 | goto fail; | 1837 | goto fail; |
| 1709 | 1838 | ||
| 1710 | free_token(token); | 1839 | free_token(token); |
| @@ -1713,7 +1842,7 @@ static int event_read_print(struct event *event) | |||
| 1713 | if (ret < 0) | 1842 | if (ret < 0) |
| 1714 | return -1; | 1843 | return -1; |
| 1715 | 1844 | ||
| 1716 | return 0; | 1845 | return ret; |
| 1717 | 1846 | ||
| 1718 | fail: | 1847 | fail: |
| 1719 | free_token(token); | 1848 | free_token(token); |
| @@ -1759,7 +1888,7 @@ find_any_field(struct event *event, const char *name) | |||
| 1759 | return find_field(event, name); | 1888 | return find_field(event, name); |
| 1760 | } | 1889 | } |
| 1761 | 1890 | ||
| 1762 | static unsigned long long read_size(void *ptr, int size) | 1891 | unsigned long long read_size(void *ptr, int size) |
| 1763 | { | 1892 | { |
| 1764 | switch (size) { | 1893 | switch (size) { |
| 1765 | case 1: | 1894 | case 1: |
| @@ -1822,37 +1951,67 @@ static int get_common_info(const char *type, int *offset, int *size) | |||
| 1822 | return 0; | 1951 | return 0; |
| 1823 | } | 1952 | } |
| 1824 | 1953 | ||
| 1825 | int trace_parse_common_type(void *data) | 1954 | static int __parse_common(void *data, int *size, int *offset, |
| 1955 | const char *name) | ||
| 1826 | { | 1956 | { |
| 1827 | static int type_offset; | ||
| 1828 | static int type_size; | ||
| 1829 | int ret; | 1957 | int ret; |
| 1830 | 1958 | ||
| 1831 | if (!type_size) { | 1959 | if (!*size) { |
| 1832 | ret = get_common_info("common_type", | 1960 | ret = get_common_info(name, offset, size); |
| 1833 | &type_offset, | ||
| 1834 | &type_size); | ||
| 1835 | if (ret < 0) | 1961 | if (ret < 0) |
| 1836 | return ret; | 1962 | return ret; |
| 1837 | } | 1963 | } |
| 1838 | return read_size(data + type_offset, type_size); | 1964 | return read_size(data + *offset, *size); |
| 1965 | } | ||
| 1966 | |||
| 1967 | int trace_parse_common_type(void *data) | ||
| 1968 | { | ||
| 1969 | static int type_offset; | ||
| 1970 | static int type_size; | ||
| 1971 | |||
| 1972 | return __parse_common(data, &type_size, &type_offset, | ||
| 1973 | "common_type"); | ||
| 1839 | } | 1974 | } |
| 1840 | 1975 | ||
| 1841 | static int parse_common_pid(void *data) | 1976 | int trace_parse_common_pid(void *data) |
| 1842 | { | 1977 | { |
| 1843 | static int pid_offset; | 1978 | static int pid_offset; |
| 1844 | static int pid_size; | 1979 | static int pid_size; |
| 1980 | |||
| 1981 | return __parse_common(data, &pid_size, &pid_offset, | ||
| 1982 | "common_pid"); | ||
| 1983 | } | ||
| 1984 | |||
| 1985 | int parse_common_pc(void *data) | ||
| 1986 | { | ||
| 1987 | static int pc_offset; | ||
| 1988 | static int pc_size; | ||
| 1989 | |||
| 1990 | return __parse_common(data, &pc_size, &pc_offset, | ||
| 1991 | "common_preempt_count"); | ||
| 1992 | } | ||
| 1993 | |||
| 1994 | int parse_common_flags(void *data) | ||
| 1995 | { | ||
| 1996 | static int flags_offset; | ||
| 1997 | static int flags_size; | ||
| 1998 | |||
| 1999 | return __parse_common(data, &flags_size, &flags_offset, | ||
| 2000 | "common_flags"); | ||
| 2001 | } | ||
| 2002 | |||
| 2003 | int parse_common_lock_depth(void *data) | ||
| 2004 | { | ||
| 2005 | static int ld_offset; | ||
| 2006 | static int ld_size; | ||
| 1845 | int ret; | 2007 | int ret; |
| 1846 | 2008 | ||
| 1847 | if (!pid_size) { | 2009 | ret = __parse_common(data, &ld_size, &ld_offset, |
| 1848 | ret = get_common_info("common_pid", | 2010 | "common_lock_depth"); |
| 1849 | &pid_offset, | 2011 | if (ret < 0) |
| 1850 | &pid_size); | 2012 | return -1; |
| 1851 | if (ret < 0) | ||
| 1852 | return ret; | ||
| 1853 | } | ||
| 1854 | 2013 | ||
| 1855 | return read_size(data + pid_offset, pid_size); | 2014 | return ret; |
| 1856 | } | 2015 | } |
| 1857 | 2016 | ||
| 1858 | struct event *trace_find_event(int id) | 2017 | struct event *trace_find_event(int id) |
| @@ -1866,11 +2025,20 @@ struct event *trace_find_event(int id) | |||
| 1866 | return event; | 2025 | return event; |
| 1867 | } | 2026 | } |
| 1868 | 2027 | ||
| 2028 | struct event *trace_find_next_event(struct event *event) | ||
| 2029 | { | ||
| 2030 | if (!event) | ||
| 2031 | return event_list; | ||
| 2032 | |||
| 2033 | return event->next; | ||
| 2034 | } | ||
| 2035 | |||
| 1869 | static unsigned long long eval_num_arg(void *data, int size, | 2036 | static unsigned long long eval_num_arg(void *data, int size, |
| 1870 | struct event *event, struct print_arg *arg) | 2037 | struct event *event, struct print_arg *arg) |
| 1871 | { | 2038 | { |
| 1872 | unsigned long long val = 0; | 2039 | unsigned long long val = 0; |
| 1873 | unsigned long long left, right; | 2040 | unsigned long long left, right; |
| 2041 | struct print_arg *larg; | ||
| 1874 | 2042 | ||
| 1875 | switch (arg->type) { | 2043 | switch (arg->type) { |
| 1876 | case PRINT_NULL: | 2044 | case PRINT_NULL: |
| @@ -1897,6 +2065,26 @@ static unsigned long long eval_num_arg(void *data, int size, | |||
| 1897 | return 0; | 2065 | return 0; |
| 1898 | break; | 2066 | break; |
| 1899 | case PRINT_OP: | 2067 | case PRINT_OP: |
| 2068 | if (strcmp(arg->op.op, "[") == 0) { | ||
| 2069 | /* | ||
| 2070 | * Arrays are special, since we don't want | ||
| 2071 | * to read the arg as is. | ||
| 2072 | */ | ||
| 2073 | if (arg->op.left->type != PRINT_FIELD) | ||
| 2074 | goto default_op; /* oops, all bets off */ | ||
| 2075 | larg = arg->op.left; | ||
| 2076 | if (!larg->field.field) { | ||
| 2077 | larg->field.field = | ||
| 2078 | find_any_field(event, larg->field.name); | ||
| 2079 | if (!larg->field.field) | ||
| 2080 | die("field %s not found", larg->field.name); | ||
| 2081 | } | ||
| 2082 | right = eval_num_arg(data, size, event, arg->op.right); | ||
| 2083 | val = read_size(data + larg->field.field->offset + | ||
| 2084 | right * long_size, long_size); | ||
| 2085 | break; | ||
| 2086 | } | ||
| 2087 | default_op: | ||
| 1900 | left = eval_num_arg(data, size, event, arg->op.left); | 2088 | left = eval_num_arg(data, size, event, arg->op.left); |
| 1901 | right = eval_num_arg(data, size, event, arg->op.right); | 2089 | right = eval_num_arg(data, size, event, arg->op.right); |
| 1902 | switch (arg->op.op[0]) { | 2090 | switch (arg->op.op[0]) { |
| @@ -1947,6 +2135,12 @@ static unsigned long long eval_num_arg(void *data, int size, | |||
| 1947 | die("unknown op '%s'", arg->op.op); | 2135 | die("unknown op '%s'", arg->op.op); |
| 1948 | val = left == right; | 2136 | val = left == right; |
| 1949 | break; | 2137 | break; |
| 2138 | case '-': | ||
| 2139 | val = left - right; | ||
| 2140 | break; | ||
| 2141 | case '+': | ||
| 2142 | val = left + right; | ||
| 2143 | break; | ||
| 1950 | default: | 2144 | default: |
| 1951 | die("unknown op '%s'", arg->op.op); | 2145 | die("unknown op '%s'", arg->op.op); |
| 1952 | } | 2146 | } |
| @@ -1978,7 +2172,7 @@ static const struct flag flags[] = { | |||
| 1978 | { "HRTIMER_RESTART", 1 }, | 2172 | { "HRTIMER_RESTART", 1 }, |
| 1979 | }; | 2173 | }; |
| 1980 | 2174 | ||
| 1981 | static unsigned long long eval_flag(const char *flag) | 2175 | unsigned long long eval_flag(const char *flag) |
| 1982 | { | 2176 | { |
| 1983 | int i; | 2177 | int i; |
| 1984 | 2178 | ||
| @@ -2145,8 +2339,9 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc | |||
| 2145 | case 'u': | 2339 | case 'u': |
| 2146 | case 'x': | 2340 | case 'x': |
| 2147 | case 'i': | 2341 | case 'i': |
| 2148 | bptr = (void *)(((unsigned long)bptr + (long_size - 1)) & | 2342 | /* the pointers are always 4 bytes aligned */ |
| 2149 | ~(long_size - 1)); | 2343 | bptr = (void *)(((unsigned long)bptr + 3) & |
| 2344 | ~3); | ||
| 2150 | switch (ls) { | 2345 | switch (ls) { |
| 2151 | case 0: | 2346 | case 0: |
| 2152 | case 1: | 2347 | case 1: |
| @@ -2270,7 +2465,27 @@ static void pretty_print(void *data, int size, struct event *event) | |||
| 2270 | 2465 | ||
| 2271 | for (; *ptr; ptr++) { | 2466 | for (; *ptr; ptr++) { |
| 2272 | ls = 0; | 2467 | ls = 0; |
| 2273 | if (*ptr == '%') { | 2468 | if (*ptr == '\\') { |
| 2469 | ptr++; | ||
| 2470 | switch (*ptr) { | ||
| 2471 | case 'n': | ||
| 2472 | printf("\n"); | ||
| 2473 | break; | ||
| 2474 | case 't': | ||
| 2475 | printf("\t"); | ||
| 2476 | break; | ||
| 2477 | case 'r': | ||
| 2478 | printf("\r"); | ||
| 2479 | break; | ||
| 2480 | case '\\': | ||
| 2481 | printf("\\"); | ||
| 2482 | break; | ||
| 2483 | default: | ||
| 2484 | printf("%c", *ptr); | ||
| 2485 | break; | ||
| 2486 | } | ||
| 2487 | |||
| 2488 | } else if (*ptr == '%') { | ||
| 2274 | saveptr = ptr; | 2489 | saveptr = ptr; |
| 2275 | show_func = 0; | 2490 | show_func = 0; |
| 2276 | cont_process: | 2491 | cont_process: |
| @@ -2377,6 +2592,41 @@ static inline int log10_cpu(int nb) | |||
| 2377 | return 1; | 2592 | return 1; |
| 2378 | } | 2593 | } |
| 2379 | 2594 | ||
| 2595 | static void print_lat_fmt(void *data, int size __unused) | ||
| 2596 | { | ||
| 2597 | unsigned int lat_flags; | ||
| 2598 | unsigned int pc; | ||
| 2599 | int lock_depth; | ||
| 2600 | int hardirq; | ||
| 2601 | int softirq; | ||
| 2602 | |||
| 2603 | lat_flags = parse_common_flags(data); | ||
| 2604 | pc = parse_common_pc(data); | ||
| 2605 | lock_depth = parse_common_lock_depth(data); | ||
| 2606 | |||
| 2607 | hardirq = lat_flags & TRACE_FLAG_HARDIRQ; | ||
| 2608 | softirq = lat_flags & TRACE_FLAG_SOFTIRQ; | ||
| 2609 | |||
| 2610 | printf("%c%c%c", | ||
| 2611 | (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' : | ||
| 2612 | (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ? | ||
| 2613 | 'X' : '.', | ||
| 2614 | (lat_flags & TRACE_FLAG_NEED_RESCHED) ? | ||
| 2615 | 'N' : '.', | ||
| 2616 | (hardirq && softirq) ? 'H' : | ||
| 2617 | hardirq ? 'h' : softirq ? 's' : '.'); | ||
| 2618 | |||
| 2619 | if (pc) | ||
| 2620 | printf("%x", pc); | ||
| 2621 | else | ||
| 2622 | printf("."); | ||
| 2623 | |||
| 2624 | if (lock_depth < 0) | ||
| 2625 | printf("."); | ||
| 2626 | else | ||
| 2627 | printf("%d", lock_depth); | ||
| 2628 | } | ||
| 2629 | |||
| 2380 | /* taken from Linux, written by Frederic Weisbecker */ | 2630 | /* taken from Linux, written by Frederic Weisbecker */ |
| 2381 | static void print_graph_cpu(int cpu) | 2631 | static void print_graph_cpu(int cpu) |
| 2382 | { | 2632 | { |
| @@ -2452,7 +2702,7 @@ get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, | |||
| 2452 | if (!(event->flags & EVENT_FL_ISFUNCRET)) | 2702 | if (!(event->flags & EVENT_FL_ISFUNCRET)) |
| 2453 | return NULL; | 2703 | return NULL; |
| 2454 | 2704 | ||
| 2455 | pid = parse_common_pid(next->data); | 2705 | pid = trace_parse_common_pid(next->data); |
| 2456 | field = find_field(event, "func"); | 2706 | field = find_field(event, "func"); |
| 2457 | if (!field) | 2707 | if (!field) |
| 2458 | die("function return does not have field func"); | 2708 | die("function return does not have field func"); |
| @@ -2620,6 +2870,11 @@ pretty_print_func_ent(void *data, int size, struct event *event, | |||
| 2620 | 2870 | ||
| 2621 | printf(" | "); | 2871 | printf(" | "); |
| 2622 | 2872 | ||
| 2873 | if (latency_format) { | ||
| 2874 | print_lat_fmt(data, size); | ||
| 2875 | printf(" | "); | ||
| 2876 | } | ||
| 2877 | |||
| 2623 | field = find_field(event, "func"); | 2878 | field = find_field(event, "func"); |
| 2624 | if (!field) | 2879 | if (!field) |
| 2625 | die("function entry does not have func field"); | 2880 | die("function entry does not have func field"); |
| @@ -2663,6 +2918,11 @@ pretty_print_func_ret(void *data, int size __unused, struct event *event, | |||
| 2663 | 2918 | ||
| 2664 | printf(" | "); | 2919 | printf(" | "); |
| 2665 | 2920 | ||
| 2921 | if (latency_format) { | ||
| 2922 | print_lat_fmt(data, size); | ||
| 2923 | printf(" | "); | ||
| 2924 | } | ||
| 2925 | |||
| 2666 | field = find_field(event, "rettime"); | 2926 | field = find_field(event, "rettime"); |
| 2667 | if (!field) | 2927 | if (!field) |
| 2668 | die("can't find rettime in return graph"); | 2928 | die("can't find rettime in return graph"); |
| @@ -2724,19 +2984,30 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs, | |||
| 2724 | 2984 | ||
| 2725 | event = trace_find_event(type); | 2985 | event = trace_find_event(type); |
| 2726 | if (!event) { | 2986 | if (!event) { |
| 2727 | printf("ug! no event found for type %d\n", type); | 2987 | warning("ug! no event found for type %d", type); |
| 2728 | return; | 2988 | return; |
| 2729 | } | 2989 | } |
| 2730 | 2990 | ||
| 2731 | pid = parse_common_pid(data); | 2991 | pid = trace_parse_common_pid(data); |
| 2732 | 2992 | ||
| 2733 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) | 2993 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) |
| 2734 | return pretty_print_func_graph(data, size, event, cpu, | 2994 | return pretty_print_func_graph(data, size, event, cpu, |
| 2735 | pid, comm, secs, usecs); | 2995 | pid, comm, secs, usecs); |
| 2736 | 2996 | ||
| 2737 | printf("%16s-%-5d [%03d] %5lu.%09Lu: %s: ", | 2997 | if (latency_format) { |
| 2738 | comm, pid, cpu, | 2998 | printf("%8.8s-%-5d %3d", |
| 2739 | secs, nsecs, event->name); | 2999 | comm, pid, cpu); |
| 3000 | print_lat_fmt(data, size); | ||
| 3001 | } else | ||
| 3002 | printf("%16s-%-5d [%03d]", comm, pid, cpu); | ||
| 3003 | |||
| 3004 | printf(" %5lu.%06lu: %s: ", secs, usecs, event->name); | ||
| 3005 | |||
| 3006 | if (event->flags & EVENT_FL_FAILED) { | ||
| 3007 | printf("EVENT '%s' FAILED TO PARSE\n", | ||
| 3008 | event->name); | ||
| 3009 | return; | ||
| 3010 | } | ||
| 2740 | 3011 | ||
| 2741 | pretty_print(data, size, event); | 3012 | pretty_print(data, size, event); |
| 2742 | printf("\n"); | 3013 | printf("\n"); |
| @@ -2807,46 +3078,71 @@ static void print_args(struct print_arg *args) | |||
| 2807 | } | 3078 | } |
| 2808 | } | 3079 | } |
| 2809 | 3080 | ||
| 2810 | static void parse_header_field(char *type, | 3081 | static void parse_header_field(const char *field, |
| 2811 | int *offset, int *size) | 3082 | int *offset, int *size) |
| 2812 | { | 3083 | { |
| 2813 | char *token; | 3084 | char *token; |
| 3085 | int type; | ||
| 2814 | 3086 | ||
| 2815 | if (read_expected(EVENT_ITEM, (char *)"field") < 0) | 3087 | if (read_expected(EVENT_ITEM, "field") < 0) |
| 2816 | return; | 3088 | return; |
| 2817 | if (read_expected(EVENT_OP, (char *)":") < 0) | 3089 | if (read_expected(EVENT_OP, ":") < 0) |
| 2818 | return; | 3090 | return; |
| 3091 | |||
| 2819 | /* type */ | 3092 | /* type */ |
| 2820 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 3093 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
| 2821 | return; | 3094 | goto fail; |
| 2822 | free_token(token); | 3095 | free_token(token); |
| 2823 | 3096 | ||
| 2824 | if (read_expected(EVENT_ITEM, type) < 0) | 3097 | if (read_expected(EVENT_ITEM, field) < 0) |
| 2825 | return; | 3098 | return; |
| 2826 | if (read_expected(EVENT_OP, (char *)";") < 0) | 3099 | if (read_expected(EVENT_OP, ";") < 0) |
| 2827 | return; | 3100 | return; |
| 2828 | if (read_expected(EVENT_ITEM, (char *)"offset") < 0) | 3101 | if (read_expected(EVENT_ITEM, "offset") < 0) |
| 2829 | return; | 3102 | return; |
| 2830 | if (read_expected(EVENT_OP, (char *)":") < 0) | 3103 | if (read_expected(EVENT_OP, ":") < 0) |
| 2831 | return; | 3104 | return; |
| 2832 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 3105 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
| 2833 | return; | 3106 | goto fail; |
| 2834 | *offset = atoi(token); | 3107 | *offset = atoi(token); |
| 2835 | free_token(token); | 3108 | free_token(token); |
| 2836 | if (read_expected(EVENT_OP, (char *)";") < 0) | 3109 | if (read_expected(EVENT_OP, ";") < 0) |
| 2837 | return; | 3110 | return; |
| 2838 | if (read_expected(EVENT_ITEM, (char *)"size") < 0) | 3111 | if (read_expected(EVENT_ITEM, "size") < 0) |
| 2839 | return; | 3112 | return; |
| 2840 | if (read_expected(EVENT_OP, (char *)":") < 0) | 3113 | if (read_expected(EVENT_OP, ":") < 0) |
| 2841 | return; | 3114 | return; |
| 2842 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 3115 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
| 2843 | return; | 3116 | goto fail; |
| 2844 | *size = atoi(token); | 3117 | *size = atoi(token); |
| 2845 | free_token(token); | 3118 | free_token(token); |
| 2846 | if (read_expected(EVENT_OP, (char *)";") < 0) | 3119 | if (read_expected(EVENT_OP, ";") < 0) |
| 2847 | return; | ||
| 2848 | if (read_expect_type(EVENT_NEWLINE, &token) < 0) | ||
| 2849 | return; | 3120 | return; |
| 3121 | type = read_token(&token); | ||
| 3122 | if (type != EVENT_NEWLINE) { | ||
| 3123 | /* newer versions of the kernel have a "signed" type */ | ||
| 3124 | if (type != EVENT_ITEM) | ||
| 3125 | goto fail; | ||
| 3126 | |||
| 3127 | if (strcmp(token, "signed") != 0) | ||
| 3128 | goto fail; | ||
| 3129 | |||
| 3130 | free_token(token); | ||
| 3131 | |||
| 3132 | if (read_expected(EVENT_OP, ":") < 0) | ||
| 3133 | return; | ||
| 3134 | |||
| 3135 | if (read_expect_type(EVENT_ITEM, &token)) | ||
| 3136 | goto fail; | ||
| 3137 | |||
| 3138 | free_token(token); | ||
| 3139 | if (read_expected(EVENT_OP, ";") < 0) | ||
| 3140 | return; | ||
| 3141 | |||
| 3142 | if (read_expect_type(EVENT_NEWLINE, &token)) | ||
| 3143 | goto fail; | ||
| 3144 | } | ||
| 3145 | fail: | ||
| 2850 | free_token(token); | 3146 | free_token(token); |
| 2851 | } | 3147 | } |
| 2852 | 3148 | ||
| @@ -2854,11 +3150,11 @@ int parse_header_page(char *buf, unsigned long size) | |||
| 2854 | { | 3150 | { |
| 2855 | init_input_buf(buf, size); | 3151 | init_input_buf(buf, size); |
| 2856 | 3152 | ||
| 2857 | parse_header_field((char *)"timestamp", &header_page_ts_offset, | 3153 | parse_header_field("timestamp", &header_page_ts_offset, |
| 2858 | &header_page_ts_size); | 3154 | &header_page_ts_size); |
| 2859 | parse_header_field((char *)"commit", &header_page_size_offset, | 3155 | parse_header_field("commit", &header_page_size_offset, |
| 2860 | &header_page_size_size); | 3156 | &header_page_size_size); |
| 2861 | parse_header_field((char *)"data", &header_page_data_offset, | 3157 | parse_header_field("data", &header_page_data_offset, |
| 2862 | &header_page_data_size); | 3158 | &header_page_data_size); |
| 2863 | 3159 | ||
| 2864 | return 0; | 3160 | return 0; |
| @@ -2909,6 +3205,9 @@ int parse_ftrace_file(char *buf, unsigned long size) | |||
| 2909 | if (ret < 0) | 3205 | if (ret < 0) |
| 2910 | die("failed to read ftrace event print fmt"); | 3206 | die("failed to read ftrace event print fmt"); |
| 2911 | 3207 | ||
| 3208 | /* New ftrace handles args */ | ||
| 3209 | if (ret > 0) | ||
| 3210 | return 0; | ||
| 2912 | /* | 3211 | /* |
| 2913 | * The arguments for ftrace files are parsed by the fields. | 3212 | * The arguments for ftrace files are parsed by the fields. |
| 2914 | * Set up the fields as their arguments. | 3213 | * Set up the fields as their arguments. |
| @@ -2926,7 +3225,7 @@ int parse_ftrace_file(char *buf, unsigned long size) | |||
| 2926 | return 0; | 3225 | return 0; |
| 2927 | } | 3226 | } |
| 2928 | 3227 | ||
| 2929 | int parse_event_file(char *buf, unsigned long size, char *system__unused __unused) | 3228 | int parse_event_file(char *buf, unsigned long size, char *sys) |
| 2930 | { | 3229 | { |
| 2931 | struct event *event; | 3230 | struct event *event; |
| 2932 | int ret; | 3231 | int ret; |
| @@ -2946,12 +3245,18 @@ int parse_event_file(char *buf, unsigned long size, char *system__unused __unuse | |||
| 2946 | die("failed to read event id"); | 3245 | die("failed to read event id"); |
| 2947 | 3246 | ||
| 2948 | ret = event_read_format(event); | 3247 | ret = event_read_format(event); |
| 2949 | if (ret < 0) | 3248 | if (ret < 0) { |
| 2950 | die("failed to read event format"); | 3249 | warning("failed to read event format for %s", event->name); |
| 3250 | goto event_failed; | ||
| 3251 | } | ||
| 2951 | 3252 | ||
| 2952 | ret = event_read_print(event); | 3253 | ret = event_read_print(event); |
| 2953 | if (ret < 0) | 3254 | if (ret < 0) { |
| 2954 | die("failed to read event print fmt"); | 3255 | warning("failed to read event print fmt for %s", event->name); |
| 3256 | goto event_failed; | ||
| 3257 | } | ||
| 3258 | |||
| 3259 | event->system = strdup(sys); | ||
| 2955 | 3260 | ||
| 2956 | #define PRINT_ARGS 0 | 3261 | #define PRINT_ARGS 0 |
| 2957 | if (PRINT_ARGS && event->print_fmt.args) | 3262 | if (PRINT_ARGS && event->print_fmt.args) |
| @@ -2959,6 +3264,12 @@ int parse_event_file(char *buf, unsigned long size, char *system__unused __unuse | |||
| 2959 | 3264 | ||
| 2960 | add_event(event); | 3265 | add_event(event); |
| 2961 | return 0; | 3266 | return 0; |
| 3267 | |||
| 3268 | event_failed: | ||
| 3269 | event->flags |= EVENT_FL_FAILED; | ||
| 3270 | /* still add it even if it failed */ | ||
| 3271 | add_event(event); | ||
| 3272 | return -1; | ||
| 2962 | } | 3273 | } |
| 2963 | 3274 | ||
| 2964 | void parse_set_info(int nr_cpus, int long_sz) | 3275 | void parse_set_info(int nr_cpus, int long_sz) |
diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/trace-event-perl.c new file mode 100644 index 000000000000..6d6d76b8a21e --- /dev/null +++ b/tools/perf/util/trace-event-perl.c | |||
| @@ -0,0 +1,661 @@ | |||
| 1 | /* | ||
| 2 | * trace-event-perl. Feed perf trace events to an embedded Perl interpreter. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <stdio.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #include <string.h> | ||
| 25 | #include <ctype.h> | ||
| 26 | #include <errno.h> | ||
| 27 | |||
| 28 | #include "../perf.h" | ||
| 29 | #include "util.h" | ||
| 30 | #include "trace-event.h" | ||
| 31 | #include "trace-event-perl.h" | ||
| 32 | |||
| 33 | void xs_init(pTHX); | ||
| 34 | |||
| 35 | void xs_init(pTHX) | ||
| 36 | { | ||
| 37 | const char *file = __FILE__; | ||
| 38 | dXSUB_SYS; | ||
| 39 | |||
| 40 | newXS("Perf::Trace::Context::bootstrap", boot_Perf__Trace__Context, | ||
| 41 | file); | ||
| 42 | newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); | ||
| 43 | } | ||
| 44 | |||
| 45 | INTERP my_perl; | ||
| 46 | |||
| 47 | #define FTRACE_MAX_EVENT \ | ||
| 48 | ((1 << (sizeof(unsigned short) * 8)) - 1) | ||
| 49 | |||
| 50 | struct event *events[FTRACE_MAX_EVENT]; | ||
| 51 | |||
| 52 | static struct scripting_context *scripting_context; | ||
| 53 | |||
| 54 | static char *cur_field_name; | ||
| 55 | static int zero_flag_atom; | ||
| 56 | |||
| 57 | static void define_symbolic_value(const char *ev_name, | ||
| 58 | const char *field_name, | ||
| 59 | const char *field_value, | ||
| 60 | const char *field_str) | ||
| 61 | { | ||
| 62 | unsigned long long value; | ||
| 63 | dSP; | ||
| 64 | |||
| 65 | value = eval_flag(field_value); | ||
| 66 | |||
| 67 | ENTER; | ||
| 68 | SAVETMPS; | ||
| 69 | PUSHMARK(SP); | ||
| 70 | |||
| 71 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
| 72 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
| 73 | XPUSHs(sv_2mortal(newSVuv(value))); | ||
| 74 | XPUSHs(sv_2mortal(newSVpv(field_str, 0))); | ||
| 75 | |||
| 76 | PUTBACK; | ||
| 77 | if (get_cv("main::define_symbolic_value", 0)) | ||
| 78 | call_pv("main::define_symbolic_value", G_SCALAR); | ||
| 79 | SPAGAIN; | ||
| 80 | PUTBACK; | ||
| 81 | FREETMPS; | ||
| 82 | LEAVE; | ||
| 83 | } | ||
| 84 | |||
| 85 | static void define_symbolic_values(struct print_flag_sym *field, | ||
| 86 | const char *ev_name, | ||
| 87 | const char *field_name) | ||
| 88 | { | ||
| 89 | define_symbolic_value(ev_name, field_name, field->value, field->str); | ||
| 90 | if (field->next) | ||
| 91 | define_symbolic_values(field->next, ev_name, field_name); | ||
| 92 | } | ||
| 93 | |||
| 94 | static void define_symbolic_field(const char *ev_name, | ||
| 95 | const char *field_name) | ||
| 96 | { | ||
| 97 | dSP; | ||
| 98 | |||
| 99 | ENTER; | ||
| 100 | SAVETMPS; | ||
| 101 | PUSHMARK(SP); | ||
| 102 | |||
| 103 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
| 104 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
| 105 | |||
| 106 | PUTBACK; | ||
| 107 | if (get_cv("main::define_symbolic_field", 0)) | ||
| 108 | call_pv("main::define_symbolic_field", G_SCALAR); | ||
| 109 | SPAGAIN; | ||
| 110 | PUTBACK; | ||
| 111 | FREETMPS; | ||
| 112 | LEAVE; | ||
| 113 | } | ||
| 114 | |||
| 115 | static void define_flag_value(const char *ev_name, | ||
| 116 | const char *field_name, | ||
| 117 | const char *field_value, | ||
| 118 | const char *field_str) | ||
| 119 | { | ||
| 120 | unsigned long long value; | ||
| 121 | dSP; | ||
| 122 | |||
| 123 | value = eval_flag(field_value); | ||
| 124 | |||
| 125 | ENTER; | ||
| 126 | SAVETMPS; | ||
| 127 | PUSHMARK(SP); | ||
| 128 | |||
| 129 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
| 130 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
| 131 | XPUSHs(sv_2mortal(newSVuv(value))); | ||
| 132 | XPUSHs(sv_2mortal(newSVpv(field_str, 0))); | ||
| 133 | |||
| 134 | PUTBACK; | ||
| 135 | if (get_cv("main::define_flag_value", 0)) | ||
| 136 | call_pv("main::define_flag_value", G_SCALAR); | ||
| 137 | SPAGAIN; | ||
| 138 | PUTBACK; | ||
| 139 | FREETMPS; | ||
| 140 | LEAVE; | ||
| 141 | } | ||
| 142 | |||
| 143 | static void define_flag_values(struct print_flag_sym *field, | ||
| 144 | const char *ev_name, | ||
| 145 | const char *field_name) | ||
| 146 | { | ||
| 147 | define_flag_value(ev_name, field_name, field->value, field->str); | ||
| 148 | if (field->next) | ||
| 149 | define_flag_values(field->next, ev_name, field_name); | ||
| 150 | } | ||
| 151 | |||
| 152 | static void define_flag_field(const char *ev_name, | ||
| 153 | const char *field_name, | ||
| 154 | const char *delim) | ||
| 155 | { | ||
| 156 | dSP; | ||
| 157 | |||
| 158 | ENTER; | ||
| 159 | SAVETMPS; | ||
| 160 | PUSHMARK(SP); | ||
| 161 | |||
| 162 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
| 163 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
| 164 | XPUSHs(sv_2mortal(newSVpv(delim, 0))); | ||
| 165 | |||
| 166 | PUTBACK; | ||
| 167 | if (get_cv("main::define_flag_field", 0)) | ||
| 168 | call_pv("main::define_flag_field", G_SCALAR); | ||
| 169 | SPAGAIN; | ||
| 170 | PUTBACK; | ||
| 171 | FREETMPS; | ||
| 172 | LEAVE; | ||
| 173 | } | ||
| 174 | |||
| 175 | static void define_event_symbols(struct event *event, | ||
| 176 | const char *ev_name, | ||
| 177 | struct print_arg *args) | ||
| 178 | { | ||
| 179 | switch (args->type) { | ||
| 180 | case PRINT_NULL: | ||
| 181 | break; | ||
| 182 | case PRINT_ATOM: | ||
| 183 | define_flag_value(ev_name, cur_field_name, "0", | ||
| 184 | args->atom.atom); | ||
| 185 | zero_flag_atom = 0; | ||
| 186 | break; | ||
| 187 | case PRINT_FIELD: | ||
| 188 | if (cur_field_name) | ||
| 189 | free(cur_field_name); | ||
| 190 | cur_field_name = strdup(args->field.name); | ||
| 191 | break; | ||
| 192 | case PRINT_FLAGS: | ||
| 193 | define_event_symbols(event, ev_name, args->flags.field); | ||
| 194 | define_flag_field(ev_name, cur_field_name, args->flags.delim); | ||
| 195 | define_flag_values(args->flags.flags, ev_name, cur_field_name); | ||
| 196 | break; | ||
| 197 | case PRINT_SYMBOL: | ||
| 198 | define_event_symbols(event, ev_name, args->symbol.field); | ||
| 199 | define_symbolic_field(ev_name, cur_field_name); | ||
| 200 | define_symbolic_values(args->symbol.symbols, ev_name, | ||
| 201 | cur_field_name); | ||
| 202 | break; | ||
| 203 | case PRINT_STRING: | ||
| 204 | break; | ||
| 205 | case PRINT_TYPE: | ||
| 206 | define_event_symbols(event, ev_name, args->typecast.item); | ||
| 207 | break; | ||
| 208 | case PRINT_OP: | ||
| 209 | if (strcmp(args->op.op, ":") == 0) | ||
| 210 | zero_flag_atom = 1; | ||
| 211 | define_event_symbols(event, ev_name, args->op.left); | ||
| 212 | define_event_symbols(event, ev_name, args->op.right); | ||
| 213 | break; | ||
| 214 | default: | ||
| 215 | /* we should warn... */ | ||
| 216 | return; | ||
| 217 | } | ||
| 218 | |||
| 219 | if (args->next) | ||
| 220 | define_event_symbols(event, ev_name, args->next); | ||
| 221 | } | ||
| 222 | |||
| 223 | static inline struct event *find_cache_event(int type) | ||
| 224 | { | ||
| 225 | static char ev_name[256]; | ||
| 226 | struct event *event; | ||
| 227 | |||
| 228 | if (events[type]) | ||
| 229 | return events[type]; | ||
| 230 | |||
| 231 | events[type] = event = trace_find_event(type); | ||
| 232 | if (!event) | ||
| 233 | return NULL; | ||
| 234 | |||
| 235 | sprintf(ev_name, "%s::%s", event->system, event->name); | ||
| 236 | |||
| 237 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
| 238 | |||
| 239 | return event; | ||
| 240 | } | ||
| 241 | |||
| 242 | int common_pc(struct scripting_context *context) | ||
| 243 | { | ||
| 244 | int pc; | ||
| 245 | |||
| 246 | pc = parse_common_pc(context->event_data); | ||
| 247 | |||
| 248 | return pc; | ||
| 249 | } | ||
| 250 | |||
| 251 | int common_flags(struct scripting_context *context) | ||
| 252 | { | ||
| 253 | int flags; | ||
| 254 | |||
| 255 | flags = parse_common_flags(context->event_data); | ||
| 256 | |||
| 257 | return flags; | ||
| 258 | } | ||
| 259 | |||
| 260 | int common_lock_depth(struct scripting_context *context) | ||
| 261 | { | ||
| 262 | int lock_depth; | ||
| 263 | |||
| 264 | lock_depth = parse_common_lock_depth(context->event_data); | ||
| 265 | |||
| 266 | return lock_depth; | ||
| 267 | } | ||
| 268 | |||
| 269 | static void perl_process_event(int cpu, void *data, | ||
| 270 | int size __unused, | ||
| 271 | unsigned long long nsecs, char *comm) | ||
| 272 | { | ||
| 273 | struct format_field *field; | ||
| 274 | static char handler[256]; | ||
| 275 | unsigned long long val; | ||
| 276 | unsigned long s, ns; | ||
| 277 | struct event *event; | ||
| 278 | int type; | ||
| 279 | int pid; | ||
| 280 | |||
| 281 | dSP; | ||
| 282 | |||
| 283 | type = trace_parse_common_type(data); | ||
| 284 | |||
| 285 | event = find_cache_event(type); | ||
| 286 | if (!event) | ||
| 287 | die("ug! no event found for type %d", type); | ||
| 288 | |||
| 289 | pid = trace_parse_common_pid(data); | ||
| 290 | |||
| 291 | sprintf(handler, "%s::%s", event->system, event->name); | ||
| 292 | |||
| 293 | s = nsecs / NSECS_PER_SEC; | ||
| 294 | ns = nsecs - s * NSECS_PER_SEC; | ||
| 295 | |||
| 296 | scripting_context->event_data = data; | ||
| 297 | |||
| 298 | ENTER; | ||
| 299 | SAVETMPS; | ||
| 300 | PUSHMARK(SP); | ||
| 301 | |||
| 302 | XPUSHs(sv_2mortal(newSVpv(handler, 0))); | ||
| 303 | XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context)))); | ||
| 304 | XPUSHs(sv_2mortal(newSVuv(cpu))); | ||
| 305 | XPUSHs(sv_2mortal(newSVuv(s))); | ||
| 306 | XPUSHs(sv_2mortal(newSVuv(ns))); | ||
| 307 | XPUSHs(sv_2mortal(newSViv(pid))); | ||
| 308 | XPUSHs(sv_2mortal(newSVpv(comm, 0))); | ||
| 309 | |||
| 310 | /* common fields other than pid can be accessed via xsub fns */ | ||
| 311 | |||
| 312 | for (field = event->format.fields; field; field = field->next) { | ||
| 313 | if (field->flags & FIELD_IS_STRING) { | ||
| 314 | int offset; | ||
| 315 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
| 316 | offset = *(int *)(data + field->offset); | ||
| 317 | offset &= 0xffff; | ||
| 318 | } else | ||
| 319 | offset = field->offset; | ||
| 320 | XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0))); | ||
| 321 | } else { /* FIELD_IS_NUMERIC */ | ||
| 322 | val = read_size(data + field->offset, field->size); | ||
| 323 | if (field->flags & FIELD_IS_SIGNED) { | ||
| 324 | XPUSHs(sv_2mortal(newSViv(val))); | ||
| 325 | } else { | ||
| 326 | XPUSHs(sv_2mortal(newSVuv(val))); | ||
| 327 | } | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | PUTBACK; | ||
| 332 | |||
| 333 | if (get_cv(handler, 0)) | ||
| 334 | call_pv(handler, G_SCALAR); | ||
| 335 | else if (get_cv("main::trace_unhandled", 0)) { | ||
| 336 | XPUSHs(sv_2mortal(newSVpv(handler, 0))); | ||
| 337 | XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context)))); | ||
| 338 | XPUSHs(sv_2mortal(newSVuv(cpu))); | ||
| 339 | XPUSHs(sv_2mortal(newSVuv(nsecs))); | ||
| 340 | XPUSHs(sv_2mortal(newSViv(pid))); | ||
| 341 | XPUSHs(sv_2mortal(newSVpv(comm, 0))); | ||
| 342 | call_pv("main::trace_unhandled", G_SCALAR); | ||
| 343 | } | ||
| 344 | SPAGAIN; | ||
| 345 | PUTBACK; | ||
| 346 | FREETMPS; | ||
| 347 | LEAVE; | ||
| 348 | } | ||
| 349 | |||
| 350 | static void run_start_sub(void) | ||
| 351 | { | ||
| 352 | dSP; /* access to Perl stack */ | ||
| 353 | PUSHMARK(SP); | ||
| 354 | |||
| 355 | if (get_cv("main::trace_begin", 0)) | ||
| 356 | call_pv("main::trace_begin", G_DISCARD | G_NOARGS); | ||
| 357 | } | ||
| 358 | |||
| 359 | /* | ||
| 360 | * Start trace script | ||
| 361 | */ | ||
| 362 | static int perl_start_script(const char *script, int argc, const char **argv) | ||
| 363 | { | ||
| 364 | const char **command_line; | ||
| 365 | int i, err = 0; | ||
| 366 | |||
| 367 | command_line = malloc((argc + 2) * sizeof(const char *)); | ||
| 368 | command_line[0] = ""; | ||
| 369 | command_line[1] = script; | ||
| 370 | for (i = 2; i < argc + 2; i++) | ||
| 371 | command_line[i] = argv[i - 2]; | ||
| 372 | |||
| 373 | my_perl = perl_alloc(); | ||
| 374 | perl_construct(my_perl); | ||
| 375 | |||
| 376 | if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line, | ||
| 377 | (char **)NULL)) { | ||
| 378 | err = -1; | ||
| 379 | goto error; | ||
| 380 | } | ||
| 381 | |||
| 382 | if (perl_run(my_perl)) { | ||
| 383 | err = -1; | ||
| 384 | goto error; | ||
| 385 | } | ||
| 386 | |||
| 387 | if (SvTRUE(ERRSV)) { | ||
| 388 | err = -1; | ||
| 389 | goto error; | ||
| 390 | } | ||
| 391 | |||
| 392 | run_start_sub(); | ||
| 393 | |||
| 394 | free(command_line); | ||
| 395 | fprintf(stderr, "perf trace started with Perl script %s\n\n", script); | ||
| 396 | return 0; | ||
| 397 | error: | ||
| 398 | perl_free(my_perl); | ||
| 399 | free(command_line); | ||
| 400 | |||
| 401 | return err; | ||
| 402 | } | ||
| 403 | |||
| 404 | /* | ||
| 405 | * Stop trace script | ||
| 406 | */ | ||
| 407 | static int perl_stop_script(void) | ||
| 408 | { | ||
| 409 | dSP; /* access to Perl stack */ | ||
| 410 | PUSHMARK(SP); | ||
| 411 | |||
| 412 | if (get_cv("main::trace_end", 0)) | ||
| 413 | call_pv("main::trace_end", G_DISCARD | G_NOARGS); | ||
| 414 | |||
| 415 | perl_destruct(my_perl); | ||
| 416 | perl_free(my_perl); | ||
| 417 | |||
| 418 | fprintf(stderr, "\nperf trace Perl script stopped\n"); | ||
| 419 | |||
| 420 | return 0; | ||
| 421 | } | ||
| 422 | |||
| 423 | static int perl_generate_script(const char *outfile) | ||
| 424 | { | ||
| 425 | struct event *event = NULL; | ||
| 426 | struct format_field *f; | ||
| 427 | char fname[PATH_MAX]; | ||
| 428 | int not_first, count; | ||
| 429 | FILE *ofp; | ||
| 430 | |||
| 431 | sprintf(fname, "%s.pl", outfile); | ||
| 432 | ofp = fopen(fname, "w"); | ||
| 433 | if (ofp == NULL) { | ||
| 434 | fprintf(stderr, "couldn't open %s\n", fname); | ||
| 435 | return -1; | ||
| 436 | } | ||
| 437 | |||
| 438 | fprintf(ofp, "# perf trace event handlers, " | ||
| 439 | "generated by perf trace -g perl\n"); | ||
| 440 | |||
| 441 | fprintf(ofp, "# Licensed under the terms of the GNU GPL" | ||
| 442 | " License version 2\n\n"); | ||
| 443 | |||
| 444 | fprintf(ofp, "# The common_* event handler fields are the most useful " | ||
| 445 | "fields common to\n"); | ||
| 446 | |||
| 447 | fprintf(ofp, "# all events. They don't necessarily correspond to " | ||
| 448 | "the 'common_*' fields\n"); | ||
| 449 | |||
| 450 | fprintf(ofp, "# in the format files. Those fields not available as " | ||
| 451 | "handler params can\n"); | ||
| 452 | |||
| 453 | fprintf(ofp, "# be retrieved using Perl functions of the form " | ||
| 454 | "common_*($context).\n"); | ||
| 455 | |||
| 456 | fprintf(ofp, "# See Context.pm for the list of available " | ||
| 457 | "functions.\n\n"); | ||
| 458 | |||
| 459 | fprintf(ofp, "use lib \"$ENV{'PERF_EXEC_PATH'}/scripts/perl/" | ||
| 460 | "Perf-Trace-Util/lib\";\n"); | ||
| 461 | |||
| 462 | fprintf(ofp, "use lib \"./Perf-Trace-Util/lib\";\n"); | ||
| 463 | fprintf(ofp, "use Perf::Trace::Core;\n"); | ||
| 464 | fprintf(ofp, "use Perf::Trace::Context;\n"); | ||
| 465 | fprintf(ofp, "use Perf::Trace::Util;\n\n"); | ||
| 466 | |||
| 467 | fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n"); | ||
| 468 | fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n"); | ||
| 469 | |||
| 470 | while ((event = trace_find_next_event(event))) { | ||
| 471 | fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name); | ||
| 472 | fprintf(ofp, "\tmy ("); | ||
| 473 | |||
| 474 | fprintf(ofp, "$event_name, "); | ||
| 475 | fprintf(ofp, "$context, "); | ||
| 476 | fprintf(ofp, "$common_cpu, "); | ||
| 477 | fprintf(ofp, "$common_secs, "); | ||
| 478 | fprintf(ofp, "$common_nsecs,\n"); | ||
| 479 | fprintf(ofp, "\t $common_pid, "); | ||
| 480 | fprintf(ofp, "$common_comm,\n\t "); | ||
| 481 | |||
| 482 | not_first = 0; | ||
| 483 | count = 0; | ||
| 484 | |||
| 485 | for (f = event->format.fields; f; f = f->next) { | ||
| 486 | if (not_first++) | ||
| 487 | fprintf(ofp, ", "); | ||
| 488 | if (++count % 5 == 0) | ||
| 489 | fprintf(ofp, "\n\t "); | ||
| 490 | |||
| 491 | fprintf(ofp, "$%s", f->name); | ||
| 492 | } | ||
| 493 | fprintf(ofp, ") = @_;\n\n"); | ||
| 494 | |||
| 495 | fprintf(ofp, "\tprint_header($event_name, $common_cpu, " | ||
| 496 | "$common_secs, $common_nsecs,\n\t " | ||
| 497 | "$common_pid, $common_comm);\n\n"); | ||
| 498 | |||
| 499 | fprintf(ofp, "\tprintf(\""); | ||
| 500 | |||
| 501 | not_first = 0; | ||
| 502 | count = 0; | ||
| 503 | |||
| 504 | for (f = event->format.fields; f; f = f->next) { | ||
| 505 | if (not_first++) | ||
| 506 | fprintf(ofp, ", "); | ||
| 507 | if (count && count % 4 == 0) { | ||
| 508 | fprintf(ofp, "\".\n\t \""); | ||
| 509 | } | ||
| 510 | count++; | ||
| 511 | |||
| 512 | fprintf(ofp, "%s=", f->name); | ||
| 513 | if (f->flags & FIELD_IS_STRING || | ||
| 514 | f->flags & FIELD_IS_FLAG || | ||
| 515 | f->flags & FIELD_IS_SYMBOLIC) | ||
| 516 | fprintf(ofp, "%%s"); | ||
| 517 | else if (f->flags & FIELD_IS_SIGNED) | ||
| 518 | fprintf(ofp, "%%d"); | ||
| 519 | else | ||
| 520 | fprintf(ofp, "%%u"); | ||
| 521 | } | ||
| 522 | |||
| 523 | fprintf(ofp, "\\n\",\n\t "); | ||
| 524 | |||
| 525 | not_first = 0; | ||
| 526 | count = 0; | ||
| 527 | |||
| 528 | for (f = event->format.fields; f; f = f->next) { | ||
| 529 | if (not_first++) | ||
| 530 | fprintf(ofp, ", "); | ||
| 531 | |||
| 532 | if (++count % 5 == 0) | ||
| 533 | fprintf(ofp, "\n\t "); | ||
| 534 | |||
| 535 | if (f->flags & FIELD_IS_FLAG) { | ||
| 536 | if ((count - 1) % 5 != 0) { | ||
| 537 | fprintf(ofp, "\n\t "); | ||
| 538 | count = 4; | ||
| 539 | } | ||
| 540 | fprintf(ofp, "flag_str(\""); | ||
| 541 | fprintf(ofp, "%s::%s\", ", event->system, | ||
| 542 | event->name); | ||
| 543 | fprintf(ofp, "\"%s\", $%s)", f->name, | ||
| 544 | f->name); | ||
| 545 | } else if (f->flags & FIELD_IS_SYMBOLIC) { | ||
| 546 | if ((count - 1) % 5 != 0) { | ||
| 547 | fprintf(ofp, "\n\t "); | ||
| 548 | count = 4; | ||
| 549 | } | ||
| 550 | fprintf(ofp, "symbol_str(\""); | ||
| 551 | fprintf(ofp, "%s::%s\", ", event->system, | ||
| 552 | event->name); | ||
| 553 | fprintf(ofp, "\"%s\", $%s)", f->name, | ||
| 554 | f->name); | ||
| 555 | } else | ||
| 556 | fprintf(ofp, "$%s", f->name); | ||
| 557 | } | ||
| 558 | |||
| 559 | fprintf(ofp, ");\n"); | ||
| 560 | fprintf(ofp, "}\n\n"); | ||
| 561 | } | ||
| 562 | |||
| 563 | fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, " | ||
| 564 | "$common_cpu, $common_secs, $common_nsecs,\n\t " | ||
| 565 | "$common_pid, $common_comm) = @_;\n\n"); | ||
| 566 | |||
| 567 | fprintf(ofp, "\tprint_header($event_name, $common_cpu, " | ||
| 568 | "$common_secs, $common_nsecs,\n\t $common_pid, " | ||
| 569 | "$common_comm);\n}\n\n"); | ||
| 570 | |||
| 571 | fprintf(ofp, "sub print_header\n{\n" | ||
| 572 | "\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n" | ||
| 573 | "\tprintf(\"%%-20s %%5u %%05u.%%09u %%8u %%-20s \",\n\t " | ||
| 574 | "$event_name, $cpu, $secs, $nsecs, $pid, $comm);\n}"); | ||
| 575 | |||
| 576 | fclose(ofp); | ||
| 577 | |||
| 578 | fprintf(stderr, "generated Perl script: %s\n", fname); | ||
| 579 | |||
| 580 | return 0; | ||
| 581 | } | ||
| 582 | |||
| 583 | struct scripting_ops perl_scripting_ops = { | ||
| 584 | .name = "Perl", | ||
| 585 | .start_script = perl_start_script, | ||
| 586 | .stop_script = perl_stop_script, | ||
| 587 | .process_event = perl_process_event, | ||
| 588 | .generate_script = perl_generate_script, | ||
| 589 | }; | ||
| 590 | |||
| 591 | static void print_unsupported_msg(void) | ||
| 592 | { | ||
| 593 | fprintf(stderr, "Perl scripting not supported." | ||
| 594 | " Install libperl and rebuild perf to enable it.\n" | ||
| 595 | "For example:\n # apt-get install libperl-dev (ubuntu)" | ||
| 596 | "\n # yum install perl-ExtUtils-Embed (Fedora)" | ||
| 597 | "\n etc.\n"); | ||
| 598 | } | ||
| 599 | |||
| 600 | static int perl_start_script_unsupported(const char *script __unused, | ||
| 601 | int argc __unused, | ||
| 602 | const char **argv __unused) | ||
| 603 | { | ||
| 604 | print_unsupported_msg(); | ||
| 605 | |||
| 606 | return -1; | ||
| 607 | } | ||
| 608 | |||
| 609 | static int perl_stop_script_unsupported(void) | ||
| 610 | { | ||
| 611 | return 0; | ||
| 612 | } | ||
| 613 | |||
| 614 | static void perl_process_event_unsupported(int cpu __unused, | ||
| 615 | void *data __unused, | ||
| 616 | int size __unused, | ||
| 617 | unsigned long long nsecs __unused, | ||
| 618 | char *comm __unused) | ||
| 619 | { | ||
| 620 | } | ||
| 621 | |||
| 622 | static int perl_generate_script_unsupported(const char *outfile __unused) | ||
| 623 | { | ||
| 624 | print_unsupported_msg(); | ||
| 625 | |||
| 626 | return -1; | ||
| 627 | } | ||
| 628 | |||
| 629 | struct scripting_ops perl_scripting_unsupported_ops = { | ||
| 630 | .name = "Perl", | ||
| 631 | .start_script = perl_start_script_unsupported, | ||
| 632 | .stop_script = perl_stop_script_unsupported, | ||
| 633 | .process_event = perl_process_event_unsupported, | ||
| 634 | .generate_script = perl_generate_script_unsupported, | ||
| 635 | }; | ||
| 636 | |||
| 637 | static void register_perl_scripting(struct scripting_ops *scripting_ops) | ||
| 638 | { | ||
| 639 | int err; | ||
| 640 | err = script_spec_register("Perl", scripting_ops); | ||
| 641 | if (err) | ||
| 642 | die("error registering Perl script extension"); | ||
| 643 | |||
| 644 | err = script_spec_register("pl", scripting_ops); | ||
| 645 | if (err) | ||
| 646 | die("error registering pl script extension"); | ||
| 647 | |||
| 648 | scripting_context = malloc(sizeof(struct scripting_context)); | ||
| 649 | } | ||
| 650 | |||
| 651 | #ifdef NO_LIBPERL | ||
| 652 | void setup_perl_scripting(void) | ||
| 653 | { | ||
| 654 | register_perl_scripting(&perl_scripting_unsupported_ops); | ||
| 655 | } | ||
| 656 | #else | ||
| 657 | void setup_perl_scripting(void) | ||
| 658 | { | ||
| 659 | register_perl_scripting(&perl_scripting_ops); | ||
| 660 | } | ||
| 661 | #endif | ||
diff --git a/tools/perf/util/trace-event-perl.h b/tools/perf/util/trace-event-perl.h new file mode 100644 index 000000000000..e88fb26137bb --- /dev/null +++ b/tools/perf/util/trace-event-perl.h | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | #ifndef __PERF_TRACE_EVENT_PERL_H | ||
| 2 | #define __PERF_TRACE_EVENT_PERL_H | ||
| 3 | #ifdef NO_LIBPERL | ||
| 4 | typedef int INTERP; | ||
| 5 | #define dSP | ||
| 6 | #define ENTER | ||
| 7 | #define SAVETMPS | ||
| 8 | #define PUTBACK | ||
| 9 | #define SPAGAIN | ||
| 10 | #define FREETMPS | ||
| 11 | #define LEAVE | ||
| 12 | #define SP | ||
| 13 | #define ERRSV | ||
| 14 | #define G_SCALAR (0) | ||
| 15 | #define G_DISCARD (0) | ||
| 16 | #define G_NOARGS (0) | ||
| 17 | #define PUSHMARK(a) | ||
| 18 | #define SvTRUE(a) (0) | ||
| 19 | #define XPUSHs(s) | ||
| 20 | #define sv_2mortal(a) | ||
| 21 | #define newSVpv(a,b) | ||
| 22 | #define newSVuv(a) | ||
| 23 | #define newSViv(a) | ||
| 24 | #define get_cv(a,b) (0) | ||
| 25 | #define call_pv(a,b) (0) | ||
| 26 | #define perl_alloc() (0) | ||
| 27 | #define perl_construct(a) (0) | ||
| 28 | #define perl_parse(a,b,c,d,e) (0) | ||
| 29 | #define perl_run(a) (0) | ||
| 30 | #define perl_destruct(a) (0) | ||
| 31 | #define perl_free(a) (0) | ||
| 32 | #define pTHX void | ||
| 33 | #define CV void | ||
| 34 | #define dXSUB_SYS | ||
| 35 | #define pTHX_ | ||
| 36 | static inline void newXS(const char *a, void *b, const char *c) {} | ||
| 37 | static void boot_Perf__Trace__Context(pTHX_ CV *cv) {} | ||
| 38 | static void boot_DynaLoader(pTHX_ CV *cv) {} | ||
| 39 | #else | ||
| 40 | #include <EXTERN.h> | ||
| 41 | #include <perl.h> | ||
| 42 | void boot_Perf__Trace__Context(pTHX_ CV *cv); | ||
| 43 | void boot_DynaLoader(pTHX_ CV *cv); | ||
| 44 | typedef PerlInterpreter * INTERP; | ||
| 45 | #endif | ||
| 46 | |||
| 47 | struct scripting_context { | ||
| 48 | void *event_data; | ||
| 49 | }; | ||
| 50 | |||
| 51 | int common_pc(struct scripting_context *context); | ||
| 52 | int common_flags(struct scripting_context *context); | ||
| 53 | int common_lock_depth(struct scripting_context *context); | ||
| 54 | |||
| 55 | #endif /* __PERF_TRACE_EVENT_PERL_H */ | ||
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 1b5c847d2c22..1744422cafcb 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
| @@ -145,8 +145,9 @@ static void read_proc_kallsyms(void) | |||
| 145 | if (!size) | 145 | if (!size) |
| 146 | return; | 146 | return; |
| 147 | 147 | ||
| 148 | buf = malloc_or_die(size); | 148 | buf = malloc_or_die(size + 1); |
| 149 | read_or_die(buf, size); | 149 | read_or_die(buf, size); |
| 150 | buf[size] = '\0'; | ||
| 150 | 151 | ||
| 151 | parse_proc_kallsyms(buf, size); | 152 | parse_proc_kallsyms(buf, size); |
| 152 | 153 | ||
| @@ -458,9 +459,8 @@ struct record *trace_read_data(int cpu) | |||
| 458 | return data; | 459 | return data; |
| 459 | } | 460 | } |
| 460 | 461 | ||
| 461 | void trace_report(void) | 462 | void trace_report(int fd) |
| 462 | { | 463 | { |
| 463 | const char *input_file = "trace.info"; | ||
| 464 | char buf[BUFSIZ]; | 464 | char buf[BUFSIZ]; |
| 465 | char test[] = { 23, 8, 68 }; | 465 | char test[] = { 23, 8, 68 }; |
| 466 | char *version; | 466 | char *version; |
| @@ -468,17 +468,15 @@ void trace_report(void) | |||
| 468 | int show_funcs = 0; | 468 | int show_funcs = 0; |
| 469 | int show_printk = 0; | 469 | int show_printk = 0; |
| 470 | 470 | ||
| 471 | input_fd = open(input_file, O_RDONLY); | 471 | input_fd = fd; |
| 472 | if (input_fd < 0) | ||
| 473 | die("opening '%s'\n", input_file); | ||
| 474 | 472 | ||
| 475 | read_or_die(buf, 3); | 473 | read_or_die(buf, 3); |
| 476 | if (memcmp(buf, test, 3) != 0) | 474 | if (memcmp(buf, test, 3) != 0) |
| 477 | die("not an trace data file"); | 475 | die("no trace data in the file"); |
| 478 | 476 | ||
| 479 | read_or_die(buf, 7); | 477 | read_or_die(buf, 7); |
| 480 | if (memcmp(buf, "tracing", 7) != 0) | 478 | if (memcmp(buf, "tracing", 7) != 0) |
| 481 | die("not a trace file (missing tracing)"); | 479 | die("not a trace file (missing 'tracing' tag)"); |
| 482 | 480 | ||
| 483 | version = read_string(); | 481 | version = read_string(); |
| 484 | if (show_version) | 482 | if (show_version) |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 693f815c9429..6ad405620c9b 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef _TRACE_EVENTS_H | 1 | #ifndef __PERF_TRACE_EVENTS_H |
| 2 | #define _TRACE_EVENTS_H | 2 | #define __PERF_TRACE_EVENTS_H |
| 3 | 3 | ||
| 4 | #include "parse-events.h" | 4 | #include "parse-events.h" |
| 5 | 5 | ||
| @@ -26,6 +26,11 @@ enum { | |||
| 26 | enum format_flags { | 26 | enum format_flags { |
| 27 | FIELD_IS_ARRAY = 1, | 27 | FIELD_IS_ARRAY = 1, |
| 28 | FIELD_IS_POINTER = 2, | 28 | FIELD_IS_POINTER = 2, |
| 29 | FIELD_IS_SIGNED = 4, | ||
| 30 | FIELD_IS_STRING = 8, | ||
| 31 | FIELD_IS_DYNAMIC = 16, | ||
| 32 | FIELD_IS_FLAG = 32, | ||
| 33 | FIELD_IS_SYMBOLIC = 64, | ||
| 29 | }; | 34 | }; |
| 30 | 35 | ||
| 31 | struct format_field { | 36 | struct format_field { |
| @@ -132,15 +137,18 @@ struct event { | |||
| 132 | int flags; | 137 | int flags; |
| 133 | struct format format; | 138 | struct format format; |
| 134 | struct print_fmt print_fmt; | 139 | struct print_fmt print_fmt; |
| 140 | char *system; | ||
| 135 | }; | 141 | }; |
| 136 | 142 | ||
| 137 | enum { | 143 | enum { |
| 138 | EVENT_FL_ISFTRACE = 1, | 144 | EVENT_FL_ISFTRACE = 0x01, |
| 139 | EVENT_FL_ISPRINT = 2, | 145 | EVENT_FL_ISPRINT = 0x02, |
| 140 | EVENT_FL_ISBPRINT = 4, | 146 | EVENT_FL_ISBPRINT = 0x04, |
| 141 | EVENT_FL_ISFUNC = 8, | 147 | EVENT_FL_ISFUNC = 0x08, |
| 142 | EVENT_FL_ISFUNCENT = 16, | 148 | EVENT_FL_ISFUNCENT = 0x10, |
| 143 | EVENT_FL_ISFUNCRET = 32, | 149 | EVENT_FL_ISFUNCRET = 0x20, |
| 150 | |||
| 151 | EVENT_FL_FAILED = 0x80000000 | ||
| 144 | }; | 152 | }; |
| 145 | 153 | ||
| 146 | struct record { | 154 | struct record { |
| @@ -154,7 +162,7 @@ struct record *trace_read_data(int cpu); | |||
| 154 | 162 | ||
| 155 | void parse_set_info(int nr_cpus, int long_sz); | 163 | void parse_set_info(int nr_cpus, int long_sz); |
| 156 | 164 | ||
| 157 | void trace_report(void); | 165 | void trace_report(int fd); |
| 158 | 166 | ||
| 159 | void *malloc_or_die(unsigned int size); | 167 | void *malloc_or_die(unsigned int size); |
| 160 | 168 | ||
| @@ -166,7 +174,7 @@ void print_funcs(void); | |||
| 166 | void print_printk(void); | 174 | void print_printk(void); |
| 167 | 175 | ||
| 168 | int parse_ftrace_file(char *buf, unsigned long size); | 176 | int parse_ftrace_file(char *buf, unsigned long size); |
| 169 | int parse_event_file(char *buf, unsigned long size, char *system); | 177 | int parse_event_file(char *buf, unsigned long size, char *sys); |
| 170 | void print_event(int cpu, void *data, int size, unsigned long long nsecs, | 178 | void print_event(int cpu, void *data, int size, unsigned long long nsecs, |
| 171 | char *comm); | 179 | char *comm); |
| 172 | 180 | ||
| @@ -233,13 +241,45 @@ extern int header_page_size_size; | |||
| 233 | extern int header_page_data_offset; | 241 | extern int header_page_data_offset; |
| 234 | extern int header_page_data_size; | 242 | extern int header_page_data_size; |
| 235 | 243 | ||
| 244 | extern int latency_format; | ||
| 245 | |||
| 236 | int parse_header_page(char *buf, unsigned long size); | 246 | int parse_header_page(char *buf, unsigned long size); |
| 237 | int trace_parse_common_type(void *data); | 247 | int trace_parse_common_type(void *data); |
| 248 | int trace_parse_common_pid(void *data); | ||
| 249 | int parse_common_pc(void *data); | ||
| 250 | int parse_common_flags(void *data); | ||
| 251 | int parse_common_lock_depth(void *data); | ||
| 238 | struct event *trace_find_event(int id); | 252 | struct event *trace_find_event(int id); |
| 253 | struct event *trace_find_next_event(struct event *event); | ||
| 254 | unsigned long long read_size(void *ptr, int size); | ||
| 239 | unsigned long long | 255 | unsigned long long |
| 240 | raw_field_value(struct event *event, const char *name, void *data); | 256 | raw_field_value(struct event *event, const char *name, void *data); |
| 241 | void *raw_field_ptr(struct event *event, const char *name, void *data); | 257 | void *raw_field_ptr(struct event *event, const char *name, void *data); |
| 258 | unsigned long long eval_flag(const char *flag); | ||
| 259 | |||
| 260 | int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events); | ||
| 261 | |||
| 262 | /* taken from kernel/trace/trace.h */ | ||
| 263 | enum trace_flag_type { | ||
| 264 | TRACE_FLAG_IRQS_OFF = 0x01, | ||
| 265 | TRACE_FLAG_IRQS_NOSUPPORT = 0x02, | ||
| 266 | TRACE_FLAG_NEED_RESCHED = 0x04, | ||
| 267 | TRACE_FLAG_HARDIRQ = 0x08, | ||
| 268 | TRACE_FLAG_SOFTIRQ = 0x10, | ||
| 269 | }; | ||
| 270 | |||
| 271 | struct scripting_ops { | ||
| 272 | const char *name; | ||
| 273 | int (*start_script) (const char *script, int argc, const char **argv); | ||
| 274 | int (*stop_script) (void); | ||
| 275 | void (*process_event) (int cpu, void *data, int size, | ||
| 276 | unsigned long long nsecs, char *comm); | ||
| 277 | int (*generate_script) (const char *outfile); | ||
| 278 | }; | ||
| 279 | |||
| 280 | int script_spec_register(const char *spec, struct scripting_ops *ops); | ||
| 242 | 281 | ||
| 243 | void read_tracing_data(struct perf_event_attr *pattrs, int nb_events); | 282 | extern struct scripting_ops perl_scripting_ops; |
| 283 | void setup_perl_scripting(void); | ||
| 244 | 284 | ||
| 245 | #endif /* _TRACE_EVENTS_H */ | 285 | #endif /* __PERF_TRACE_EVENTS_H */ |
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h index 5e75f9005940..7d6b8331f898 100644 --- a/tools/perf/util/types.h +++ b/tools/perf/util/types.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef _PERF_TYPES_H | 1 | #ifndef __PERF_TYPES_H |
| 2 | #define _PERF_TYPES_H | 2 | #define __PERF_TYPES_H |
| 3 | 3 | ||
| 4 | /* | 4 | /* |
| 5 | * We define u64 as unsigned long long for every architecture | 5 | * We define u64 as unsigned long long for every architecture |
| @@ -14,4 +14,4 @@ typedef signed short s16; | |||
| 14 | typedef unsigned char u8; | 14 | typedef unsigned char u8; |
| 15 | typedef signed char s8; | 15 | typedef signed char s8; |
| 16 | 16 | ||
| 17 | #endif /* _PERF_TYPES_H */ | 17 | #endif /* __PERF_TYPES_H */ |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 9de2329dd44d..c673d8825883 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
| @@ -84,6 +84,9 @@ | |||
| 84 | #include <iconv.h> | 84 | #include <iconv.h> |
| 85 | #endif | 85 | #endif |
| 86 | 86 | ||
| 87 | extern const char *graph_line; | ||
| 88 | extern const char *graph_dotted_line; | ||
| 89 | |||
| 87 | /* On most systems <limits.h> would have given us this, but | 90 | /* On most systems <limits.h> would have given us this, but |
| 88 | * not on some systems (e.g. GNU/Hurd). | 91 | * not on some systems (e.g. GNU/Hurd). |
| 89 | */ | 92 | */ |
| @@ -134,6 +137,15 @@ extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, | |||
| 134 | extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); | 137 | extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); |
| 135 | extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); | 138 | extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); |
| 136 | 139 | ||
| 140 | #include "../../../include/linux/stringify.h" | ||
| 141 | |||
| 142 | #define DIE_IF(cnd) \ | ||
| 143 | do { if (cnd) \ | ||
| 144 | die(" at (" __FILE__ ":" __stringify(__LINE__) "): " \ | ||
| 145 | __stringify(cnd) "\n"); \ | ||
| 146 | } while (0) | ||
| 147 | |||
| 148 | |||
| 137 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); | 149 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); |
| 138 | 150 | ||
| 139 | extern int prefixcmp(const char *str, const char *prefix); | 151 | extern int prefixcmp(const char *str, const char *prefix); |
| @@ -278,17 +290,15 @@ static inline char *gitstrchrnul(const char *s, int c) | |||
| 278 | * Wrappers: | 290 | * Wrappers: |
| 279 | */ | 291 | */ |
| 280 | extern char *xstrdup(const char *str); | 292 | extern char *xstrdup(const char *str); |
| 281 | extern void *xmalloc(size_t size); | 293 | extern void *xmalloc(size_t size) __attribute__((weak)); |
| 282 | extern void *xmemdupz(const void *data, size_t len); | 294 | extern void *xmemdupz(const void *data, size_t len); |
| 283 | extern char *xstrndup(const char *str, size_t len); | 295 | extern char *xstrndup(const char *str, size_t len); |
| 284 | extern void *xrealloc(void *ptr, size_t size); | 296 | extern void *xrealloc(void *ptr, size_t size) __attribute__((weak)); |
| 285 | extern void *xcalloc(size_t nmemb, size_t size); | 297 | |
| 286 | extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); | 298 | static inline void *zalloc(size_t size) |
| 287 | extern ssize_t xread(int fd, void *buf, size_t len); | 299 | { |
| 288 | extern ssize_t xwrite(int fd, const void *buf, size_t len); | 300 | return calloc(1, size); |
| 289 | extern int xdup(int fd); | 301 | } |
| 290 | extern FILE *xfdopen(int fd, const char *mode); | ||
| 291 | extern int xmkstemp(char *template); | ||
| 292 | 302 | ||
| 293 | static inline size_t xsize_t(off_t len) | 303 | static inline size_t xsize_t(off_t len) |
| 294 | { | 304 | { |
| @@ -306,6 +316,7 @@ static inline int has_extension(const char *filename, const char *ext) | |||
| 306 | #undef isascii | 316 | #undef isascii |
| 307 | #undef isspace | 317 | #undef isspace |
| 308 | #undef isdigit | 318 | #undef isdigit |
| 319 | #undef isxdigit | ||
| 309 | #undef isalpha | 320 | #undef isalpha |
| 310 | #undef isprint | 321 | #undef isprint |
| 311 | #undef isalnum | 322 | #undef isalnum |
| @@ -323,6 +334,8 @@ extern unsigned char sane_ctype[256]; | |||
| 323 | #define isascii(x) (((x) & ~0x7f) == 0) | 334 | #define isascii(x) (((x) & ~0x7f) == 0) |
| 324 | #define isspace(x) sane_istest(x,GIT_SPACE) | 335 | #define isspace(x) sane_istest(x,GIT_SPACE) |
| 325 | #define isdigit(x) sane_istest(x,GIT_DIGIT) | 336 | #define isdigit(x) sane_istest(x,GIT_DIGIT) |
| 337 | #define isxdigit(x) \ | ||
| 338 | (sane_istest(toupper(x), GIT_ALPHA | GIT_DIGIT) && toupper(x) < 'G') | ||
| 326 | #define isalpha(x) sane_istest(x,GIT_ALPHA) | 339 | #define isalpha(x) sane_istest(x,GIT_ALPHA) |
| 327 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) | 340 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) |
| 328 | #define isprint(x) sane_istest(x,GIT_PRINT) | 341 | #define isprint(x) sane_istest(x,GIT_PRINT) |
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h index cadf8cf2a590..2fa967e1a88a 100644 --- a/tools/perf/util/values.h +++ b/tools/perf/util/values.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #ifndef _PERF_VALUES_H | 1 | #ifndef __PERF_VALUES_H |
| 2 | #define _PERF_VALUES_H | 2 | #define __PERF_VALUES_H |
| 3 | 3 | ||
| 4 | #include "types.h" | 4 | #include "types.h" |
| 5 | 5 | ||
| @@ -24,4 +24,4 @@ void perf_read_values_add_value(struct perf_read_values *values, | |||
| 24 | void perf_read_values_display(FILE *fp, struct perf_read_values *values, | 24 | void perf_read_values_display(FILE *fp, struct perf_read_values *values, |
| 25 | int raw); | 25 | int raw); |
| 26 | 26 | ||
| 27 | #endif /* _PERF_VALUES_H */ | 27 | #endif /* __PERF_VALUES_H */ |
diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c index 4574ac28396f..bf44ca85d23b 100644 --- a/tools/perf/util/wrapper.c +++ b/tools/perf/util/wrapper.c | |||
| @@ -79,43 +79,12 @@ void *xrealloc(void *ptr, size_t size) | |||
| 79 | return ret; | 79 | return ret; |
| 80 | } | 80 | } |
| 81 | 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 | /* | 82 | /* |
| 114 | * xread() is the same a read(), but it automatically restarts read() | 83 | * xread() is the same a read(), but it automatically restarts read() |
| 115 | * operations with a recoverable error (EAGAIN and EINTR). xread() | 84 | * operations with a recoverable error (EAGAIN and EINTR). xread() |
| 116 | * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. | 85 | * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. |
| 117 | */ | 86 | */ |
| 118 | ssize_t xread(int fd, void *buf, size_t len) | 87 | static ssize_t xread(int fd, void *buf, size_t len) |
| 119 | { | 88 | { |
| 120 | ssize_t nr; | 89 | ssize_t nr; |
| 121 | while (1) { | 90 | while (1) { |
| @@ -131,7 +100,7 @@ ssize_t xread(int fd, void *buf, size_t len) | |||
| 131 | * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT | 100 | * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT |
| 132 | * GUARANTEE that "len" bytes is written even if the operation is successful. | 101 | * GUARANTEE that "len" bytes is written even if the operation is successful. |
| 133 | */ | 102 | */ |
| 134 | ssize_t xwrite(int fd, const void *buf, size_t len) | 103 | static ssize_t xwrite(int fd, const void *buf, size_t len) |
| 135 | { | 104 | { |
| 136 | ssize_t nr; | 105 | ssize_t nr; |
| 137 | while (1) { | 106 | while (1) { |
| @@ -179,29 +148,3 @@ ssize_t write_in_full(int fd, const void *buf, size_t count) | |||
| 179 | 148 | ||
| 180 | return total; | 149 | return total; |
| 181 | } | 150 | } |
| 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 | } | ||
