diff options
| author | Jiri Kosina <jkosina@suse.cz> | 2010-02-02 17:10:39 -0500 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2010-02-02 17:10:39 -0500 |
| commit | e1a0bdd8022317e98650e70850de73eccfcde5ad (patch) | |
| tree | 462f63307118b95c8cbacee6954e4d09ee85b8d1 /tools/perf/util | |
| parent | 8127f4e883666c9960cfa89cffd36313748f8bab (diff) | |
| parent | 1a45dcfe2525e9432cb4aba461d4994fc2befe42 (diff) | |
Merge branch 'master' into upstream
Conflicts:
drivers/hid/hid-ids.h
Diffstat (limited to 'tools/perf/util')
32 files changed, 1956 insertions, 618 deletions
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c index ca0bedf637c2..b557b836de3d 100644 --- a/tools/perf/util/data_map.c +++ b/tools/perf/util/data_map.c | |||
| @@ -1,20 +1,17 @@ | |||
| 1 | #include "data_map.h" | ||
| 2 | #include "symbol.h" | 1 | #include "symbol.h" |
| 3 | #include "util.h" | 2 | #include "util.h" |
| 4 | #include "debug.h" | 3 | #include "debug.h" |
| 4 | #include "thread.h" | ||
| 5 | #include "session.h" | ||
| 5 | 6 | ||
| 6 | 7 | static int process_event_stub(event_t *event __used, | |
| 7 | static struct perf_file_handler *curr_handler; | 8 | struct perf_session *session __used) |
| 8 | static unsigned long mmap_window = 32; | ||
| 9 | static char __cwd[PATH_MAX]; | ||
| 10 | |||
| 11 | static int process_event_stub(event_t *event __used) | ||
| 12 | { | 9 | { |
| 13 | dump_printf(": unhandled!\n"); | 10 | dump_printf(": unhandled!\n"); |
| 14 | return 0; | 11 | return 0; |
| 15 | } | 12 | } |
| 16 | 13 | ||
| 17 | void register_perf_file_handler(struct perf_file_handler *handler) | 14 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) |
| 18 | { | 15 | { |
| 19 | if (!handler->process_sample_event) | 16 | if (!handler->process_sample_event) |
| 20 | handler->process_sample_event = process_event_stub; | 17 | handler->process_sample_event = process_event_stub; |
| @@ -34,8 +31,6 @@ void register_perf_file_handler(struct perf_file_handler *handler) | |||
| 34 | handler->process_throttle_event = process_event_stub; | 31 | handler->process_throttle_event = process_event_stub; |
| 35 | if (!handler->process_unthrottle_event) | 32 | if (!handler->process_unthrottle_event) |
| 36 | handler->process_unthrottle_event = process_event_stub; | 33 | handler->process_unthrottle_event = process_event_stub; |
| 37 | |||
| 38 | curr_handler = handler; | ||
| 39 | } | 34 | } |
| 40 | 35 | ||
| 41 | static const char *event__name[] = { | 36 | static const char *event__name[] = { |
| @@ -61,8 +56,9 @@ void event__print_totals(void) | |||
| 61 | event__name[i], event__total[i]); | 56 | event__name[i], event__total[i]); |
| 62 | } | 57 | } |
| 63 | 58 | ||
| 64 | static int | 59 | static int process_event(event_t *event, struct perf_session *session, |
| 65 | process_event(event_t *event, unsigned long offset, unsigned long head) | 60 | struct perf_event_ops *ops, |
| 61 | unsigned long offset, unsigned long head) | ||
| 66 | { | 62 | { |
| 67 | trace_event(event); | 63 | trace_event(event); |
| 68 | 64 | ||
| @@ -77,34 +73,34 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 77 | 73 | ||
| 78 | switch (event->header.type) { | 74 | switch (event->header.type) { |
| 79 | case PERF_RECORD_SAMPLE: | 75 | case PERF_RECORD_SAMPLE: |
| 80 | return curr_handler->process_sample_event(event); | 76 | return ops->process_sample_event(event, session); |
| 81 | case PERF_RECORD_MMAP: | 77 | case PERF_RECORD_MMAP: |
| 82 | return curr_handler->process_mmap_event(event); | 78 | return ops->process_mmap_event(event, session); |
| 83 | case PERF_RECORD_COMM: | 79 | case PERF_RECORD_COMM: |
| 84 | return curr_handler->process_comm_event(event); | 80 | return ops->process_comm_event(event, session); |
| 85 | case PERF_RECORD_FORK: | 81 | case PERF_RECORD_FORK: |
| 86 | return curr_handler->process_fork_event(event); | 82 | return ops->process_fork_event(event, session); |
| 87 | case PERF_RECORD_EXIT: | 83 | case PERF_RECORD_EXIT: |
| 88 | return curr_handler->process_exit_event(event); | 84 | return ops->process_exit_event(event, session); |
| 89 | case PERF_RECORD_LOST: | 85 | case PERF_RECORD_LOST: |
| 90 | return curr_handler->process_lost_event(event); | 86 | return ops->process_lost_event(event, session); |
| 91 | case PERF_RECORD_READ: | 87 | case PERF_RECORD_READ: |
| 92 | return curr_handler->process_read_event(event); | 88 | return ops->process_read_event(event, session); |
| 93 | case PERF_RECORD_THROTTLE: | 89 | case PERF_RECORD_THROTTLE: |
| 94 | return curr_handler->process_throttle_event(event); | 90 | return ops->process_throttle_event(event, session); |
| 95 | case PERF_RECORD_UNTHROTTLE: | 91 | case PERF_RECORD_UNTHROTTLE: |
| 96 | return curr_handler->process_unthrottle_event(event); | 92 | return ops->process_unthrottle_event(event, session); |
| 97 | default: | 93 | default: |
| 98 | curr_handler->total_unknown++; | 94 | ops->total_unknown++; |
| 99 | return -1; | 95 | return -1; |
| 100 | } | 96 | } |
| 101 | } | 97 | } |
| 102 | 98 | ||
| 103 | int perf_header__read_build_ids(int input, off_t offset, off_t size) | 99 | int perf_header__read_build_ids(int input, u64 offset, u64 size) |
| 104 | { | 100 | { |
| 105 | struct build_id_event bev; | 101 | struct build_id_event bev; |
| 106 | char filename[PATH_MAX]; | 102 | char filename[PATH_MAX]; |
| 107 | off_t limit = offset + size; | 103 | u64 limit = offset + size; |
| 108 | int err = -1; | 104 | int err = -1; |
| 109 | 105 | ||
| 110 | while (offset < limit) { | 106 | while (offset < limit) { |
| @@ -129,88 +125,58 @@ out: | |||
| 129 | return err; | 125 | return err; |
| 130 | } | 126 | } |
| 131 | 127 | ||
| 132 | int mmap_dispatch_perf_file(struct perf_header **pheader, | 128 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) |
| 133 | const char *input_name, | 129 | { |
| 134 | int force, | 130 | struct thread *thread = perf_session__findnew(self, 0); |
| 135 | int full_paths, | 131 | |
| 136 | int *cwdlen, | 132 | if (!thread || thread__set_comm(thread, "swapper")) { |
| 137 | char **cwd) | 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) | ||
| 138 | { | 142 | { |
| 139 | int err; | 143 | int err; |
| 140 | struct perf_header *header; | ||
| 141 | unsigned long head, shift; | 144 | unsigned long head, shift; |
| 142 | unsigned long offset = 0; | 145 | unsigned long offset = 0; |
| 143 | struct stat input_stat; | ||
| 144 | size_t page_size; | 146 | size_t page_size; |
| 145 | u64 sample_type; | ||
| 146 | event_t *event; | 147 | event_t *event; |
| 147 | uint32_t size; | 148 | uint32_t size; |
| 148 | int input; | ||
| 149 | char *buf; | 149 | char *buf; |
| 150 | 150 | ||
| 151 | if (curr_handler == NULL) { | 151 | if (perf_session__register_idle_thread(self) == NULL) |
| 152 | pr_debug("Forgot to register perf file handler\n"); | 152 | return -ENOMEM; |
| 153 | return -EINVAL; | ||
| 154 | } | ||
| 155 | |||
| 156 | page_size = getpagesize(); | ||
| 157 | |||
| 158 | input = open(input_name, O_RDONLY); | ||
| 159 | if (input < 0) { | ||
| 160 | pr_err("Failed to open file: %s", input_name); | ||
| 161 | if (!strcmp(input_name, "perf.data")) | ||
| 162 | pr_err(" (try 'perf record' first)"); | ||
| 163 | pr_err("\n"); | ||
| 164 | return -errno; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (fstat(input, &input_stat) < 0) { | ||
| 168 | pr_err("failed to stat file"); | ||
| 169 | err = -errno; | ||
| 170 | goto out_close; | ||
| 171 | } | ||
| 172 | |||
| 173 | err = -EACCES; | ||
| 174 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
| 175 | pr_err("file: %s not owned by current user or root\n", | ||
| 176 | input_name); | ||
| 177 | goto out_close; | ||
| 178 | } | ||
| 179 | |||
| 180 | if (input_stat.st_size == 0) { | ||
| 181 | pr_info("zero-sized file, nothing to do!\n"); | ||
| 182 | goto done; | ||
| 183 | } | ||
| 184 | 153 | ||
| 185 | err = -ENOMEM; | 154 | perf_event_ops__fill_defaults(ops); |
| 186 | header = perf_header__new(); | ||
| 187 | if (header == NULL) | ||
| 188 | goto out_close; | ||
| 189 | 155 | ||
| 190 | err = perf_header__read(header, input); | 156 | page_size = getpagesize(); |
| 191 | if (err < 0) | ||
| 192 | goto out_delete; | ||
| 193 | *pheader = header; | ||
| 194 | head = header->data_offset; | ||
| 195 | 157 | ||
| 196 | sample_type = perf_header__sample_type(header); | 158 | head = self->header.data_offset; |
| 159 | self->sample_type = perf_header__sample_type(&self->header); | ||
| 197 | 160 | ||
| 198 | err = -EINVAL; | 161 | err = -EINVAL; |
| 199 | if (curr_handler->sample_type_check && | 162 | if (ops->sample_type_check && ops->sample_type_check(self) < 0) |
| 200 | curr_handler->sample_type_check(sample_type) < 0) | 163 | goto out_err; |
| 201 | goto out_delete; | ||
| 202 | 164 | ||
| 203 | if (!full_paths) { | 165 | if (!ops->full_paths) { |
| 204 | if (getcwd(__cwd, sizeof(__cwd)) == NULL) { | 166 | char bf[PATH_MAX]; |
| 205 | pr_err("failed to get the current directory\n"); | 167 | |
| 168 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
| 206 | err = -errno; | 169 | err = -errno; |
| 207 | goto out_delete; | 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; | ||
| 208 | } | 178 | } |
| 209 | *cwd = __cwd; | 179 | self->cwdlen = strlen(self->cwd); |
| 210 | *cwdlen = strlen(*cwd); | ||
| 211 | } else { | ||
| 212 | *cwd = NULL; | ||
| 213 | *cwdlen = 0; | ||
| 214 | } | 180 | } |
| 215 | 181 | ||
| 216 | shift = page_size * (head / page_size); | 182 | shift = page_size * (head / page_size); |
| @@ -218,12 +184,12 @@ int mmap_dispatch_perf_file(struct perf_header **pheader, | |||
| 218 | head -= shift; | 184 | head -= shift; |
| 219 | 185 | ||
| 220 | remap: | 186 | remap: |
| 221 | buf = mmap(NULL, page_size * mmap_window, PROT_READ, | 187 | buf = mmap(NULL, page_size * self->mmap_window, PROT_READ, |
| 222 | MAP_SHARED, input, offset); | 188 | MAP_SHARED, self->fd, offset); |
| 223 | if (buf == MAP_FAILED) { | 189 | if (buf == MAP_FAILED) { |
| 224 | pr_err("failed to mmap file\n"); | 190 | pr_err("failed to mmap file\n"); |
| 225 | err = -errno; | 191 | err = -errno; |
| 226 | goto out_delete; | 192 | goto out_err; |
| 227 | } | 193 | } |
| 228 | 194 | ||
| 229 | more: | 195 | more: |
| @@ -233,12 +199,12 @@ more: | |||
| 233 | if (!size) | 199 | if (!size) |
| 234 | size = 8; | 200 | size = 8; |
| 235 | 201 | ||
| 236 | if (head + event->header.size >= page_size * mmap_window) { | 202 | if (head + event->header.size >= page_size * self->mmap_window) { |
| 237 | int munmap_ret; | 203 | int munmap_ret; |
| 238 | 204 | ||
| 239 | shift = page_size * (head / page_size); | 205 | shift = page_size * (head / page_size); |
| 240 | 206 | ||
| 241 | munmap_ret = munmap(buf, page_size * mmap_window); | 207 | munmap_ret = munmap(buf, page_size * self->mmap_window); |
| 242 | assert(munmap_ret == 0); | 208 | assert(munmap_ret == 0); |
| 243 | 209 | ||
| 244 | offset += shift; | 210 | offset += shift; |
| @@ -253,7 +219,7 @@ more: | |||
| 253 | (void *)(long)event->header.size, | 219 | (void *)(long)event->header.size, |
| 254 | event->header.type); | 220 | event->header.type); |
| 255 | 221 | ||
| 256 | if (!size || process_event(event, offset, head) < 0) { | 222 | if (!size || process_event(event, self, ops, offset, head) < 0) { |
| 257 | 223 | ||
| 258 | dump_printf("%p [%p]: skipping unknown header type: %d\n", | 224 | dump_printf("%p [%p]: skipping unknown header type: %d\n", |
| 259 | (void *)(offset + head), | 225 | (void *)(offset + head), |
| @@ -273,19 +239,14 @@ more: | |||
| 273 | 239 | ||
| 274 | head += size; | 240 | head += size; |
| 275 | 241 | ||
| 276 | if (offset + head >= header->data_offset + header->data_size) | 242 | if (offset + head >= self->header.data_offset + self->header.data_size) |
| 277 | goto done; | 243 | goto done; |
| 278 | 244 | ||
| 279 | if (offset + head < (unsigned long)input_stat.st_size) | 245 | if (offset + head < self->size) |
| 280 | goto more; | 246 | goto more; |
| 281 | 247 | ||
| 282 | done: | 248 | done: |
| 283 | err = 0; | 249 | err = 0; |
| 284 | out_close: | 250 | out_err: |
| 285 | close(input); | ||
| 286 | |||
| 287 | return err; | 251 | return err; |
| 288 | out_delete: | ||
| 289 | perf_header__delete(header); | ||
| 290 | goto out_close; | ||
| 291 | } | 252 | } |
diff --git a/tools/perf/util/data_map.h b/tools/perf/util/data_map.h deleted file mode 100644 index 3180ff7e3633..000000000000 --- a/tools/perf/util/data_map.h +++ /dev/null | |||
| @@ -1,32 +0,0 @@ | |||
| 1 | #ifndef __PERF_DATAMAP_H | ||
| 2 | #define __PERF_DATAMAP_H | ||
| 3 | |||
| 4 | #include "event.h" | ||
| 5 | #include "header.h" | ||
| 6 | |||
| 7 | typedef int (*event_type_handler_t)(event_t *); | ||
| 8 | |||
| 9 | struct perf_file_handler { | ||
| 10 | event_type_handler_t process_sample_event; | ||
| 11 | event_type_handler_t process_mmap_event; | ||
| 12 | event_type_handler_t process_comm_event; | ||
| 13 | event_type_handler_t process_fork_event; | ||
| 14 | event_type_handler_t process_exit_event; | ||
| 15 | event_type_handler_t process_lost_event; | ||
| 16 | event_type_handler_t process_read_event; | ||
| 17 | event_type_handler_t process_throttle_event; | ||
| 18 | event_type_handler_t process_unthrottle_event; | ||
| 19 | int (*sample_type_check)(u64 sample_type); | ||
| 20 | unsigned long total_unknown; | ||
| 21 | }; | ||
| 22 | |||
| 23 | void register_perf_file_handler(struct perf_file_handler *handler); | ||
| 24 | int mmap_dispatch_perf_file(struct perf_header **pheader, | ||
| 25 | const char *input_name, | ||
| 26 | int force, | ||
| 27 | int full_paths, | ||
| 28 | int *cwdlen, | ||
| 29 | char **cwd); | ||
| 30 | int perf_header__read_build_ids(int input, off_t offset, off_t file_size); | ||
| 31 | |||
| 32 | #endif | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 414b89d1bde9..bb0fd6da2d56 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
| @@ -1,11 +1,16 @@ | |||
| 1 | #include <linux/types.h> | 1 | #include <linux/types.h> |
| 2 | #include "event.h" | 2 | #include "event.h" |
| 3 | #include "debug.h" | 3 | #include "debug.h" |
| 4 | #include "session.h" | ||
| 5 | #include "sort.h" | ||
| 4 | #include "string.h" | 6 | #include "string.h" |
| 7 | #include "strlist.h" | ||
| 5 | #include "thread.h" | 8 | #include "thread.h" |
| 6 | 9 | ||
| 7 | static pid_t event__synthesize_comm(pid_t pid, int full, | 10 | static pid_t event__synthesize_comm(pid_t pid, int full, |
| 8 | int (*process)(event_t *event)) | 11 | int (*process)(event_t *event, |
| 12 | struct perf_session *session), | ||
| 13 | struct perf_session *session) | ||
| 9 | { | 14 | { |
| 10 | event_t ev; | 15 | event_t ev; |
| 11 | char filename[PATH_MAX]; | 16 | char filename[PATH_MAX]; |
| @@ -54,7 +59,7 @@ out_race: | |||
| 54 | if (!full) { | 59 | if (!full) { |
| 55 | ev.comm.tid = pid; | 60 | ev.comm.tid = pid; |
| 56 | 61 | ||
| 57 | process(&ev); | 62 | process(&ev, session); |
| 58 | goto out_fclose; | 63 | goto out_fclose; |
| 59 | } | 64 | } |
| 60 | 65 | ||
| @@ -72,7 +77,7 @@ out_race: | |||
| 72 | 77 | ||
| 73 | ev.comm.tid = pid; | 78 | ev.comm.tid = pid; |
| 74 | 79 | ||
| 75 | process(&ev); | 80 | process(&ev, session); |
| 76 | } | 81 | } |
| 77 | closedir(tasks); | 82 | closedir(tasks); |
| 78 | 83 | ||
| @@ -86,7 +91,9 @@ out_failure: | |||
| 86 | } | 91 | } |
| 87 | 92 | ||
| 88 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | 93 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, |
| 89 | int (*process)(event_t *event)) | 94 | int (*process)(event_t *event, |
| 95 | struct perf_session *session), | ||
| 96 | struct perf_session *session) | ||
| 90 | { | 97 | { |
| 91 | char filename[PATH_MAX]; | 98 | char filename[PATH_MAX]; |
| 92 | FILE *fp; | 99 | FILE *fp; |
| @@ -141,7 +148,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
| 141 | ev.mmap.pid = tgid; | 148 | ev.mmap.pid = tgid; |
| 142 | ev.mmap.tid = pid; | 149 | ev.mmap.tid = pid; |
| 143 | 150 | ||
| 144 | process(&ev); | 151 | process(&ev, session); |
| 145 | } | 152 | } |
| 146 | } | 153 | } |
| 147 | 154 | ||
| @@ -149,15 +156,20 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
| 149 | return 0; | 156 | return 0; |
| 150 | } | 157 | } |
| 151 | 158 | ||
| 152 | int event__synthesize_thread(pid_t pid, int (*process)(event_t *event)) | 159 | int event__synthesize_thread(pid_t pid, |
| 160 | int (*process)(event_t *event, | ||
| 161 | struct perf_session *session), | ||
| 162 | struct perf_session *session) | ||
| 153 | { | 163 | { |
| 154 | pid_t tgid = event__synthesize_comm(pid, 1, process); | 164 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); |
| 155 | if (tgid == -1) | 165 | if (tgid == -1) |
| 156 | return -1; | 166 | return -1; |
| 157 | return event__synthesize_mmap_events(pid, tgid, process); | 167 | return event__synthesize_mmap_events(pid, tgid, process, session); |
| 158 | } | 168 | } |
| 159 | 169 | ||
| 160 | void event__synthesize_threads(int (*process)(event_t *event)) | 170 | void event__synthesize_threads(int (*process)(event_t *event, |
| 171 | struct perf_session *session), | ||
| 172 | struct perf_session *session) | ||
| 161 | { | 173 | { |
| 162 | DIR *proc; | 174 | DIR *proc; |
| 163 | struct dirent dirent, *next; | 175 | struct dirent dirent, *next; |
| @@ -171,24 +183,47 @@ void event__synthesize_threads(int (*process)(event_t *event)) | |||
| 171 | if (*end) /* only interested in proper numerical dirents */ | 183 | if (*end) /* only interested in proper numerical dirents */ |
| 172 | continue; | 184 | continue; |
| 173 | 185 | ||
| 174 | event__synthesize_thread(pid, process); | 186 | event__synthesize_thread(pid, process, session); |
| 175 | } | 187 | } |
| 176 | 188 | ||
| 177 | closedir(proc); | 189 | closedir(proc); |
| 178 | } | 190 | } |
| 179 | 191 | ||
| 180 | char *event__cwd; | 192 | static void thread__comm_adjust(struct thread *self) |
| 181 | int event__cwdlen; | 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; | ||
| 182 | 214 | ||
| 183 | struct events_stats event__stats; | 215 | thread__comm_adjust(self); |
| 184 | 216 | ||
| 185 | int event__process_comm(event_t *self) | 217 | return 0; |
| 218 | } | ||
| 219 | |||
| 220 | int event__process_comm(event_t *self, struct perf_session *session) | ||
| 186 | { | 221 | { |
| 187 | struct thread *thread = threads__findnew(self->comm.pid); | 222 | struct thread *thread = perf_session__findnew(session, self->comm.pid); |
| 188 | 223 | ||
| 189 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); | 224 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); |
| 190 | 225 | ||
| 191 | if (thread == NULL || thread__set_comm(thread, self->comm.comm)) { | 226 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { |
| 192 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 227 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
| 193 | return -1; | 228 | return -1; |
| 194 | } | 229 | } |
| @@ -196,18 +231,18 @@ int event__process_comm(event_t *self) | |||
| 196 | return 0; | 231 | return 0; |
| 197 | } | 232 | } |
| 198 | 233 | ||
| 199 | int event__process_lost(event_t *self) | 234 | int event__process_lost(event_t *self, struct perf_session *session) |
| 200 | { | 235 | { |
| 201 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); | 236 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); |
| 202 | event__stats.lost += self->lost.lost; | 237 | session->events_stats.lost += self->lost.lost; |
| 203 | return 0; | 238 | return 0; |
| 204 | } | 239 | } |
| 205 | 240 | ||
| 206 | int event__process_mmap(event_t *self) | 241 | int event__process_mmap(event_t *self, struct perf_session *session) |
| 207 | { | 242 | { |
| 208 | struct thread *thread = threads__findnew(self->mmap.pid); | 243 | struct thread *thread = perf_session__findnew(session, self->mmap.pid); |
| 209 | struct map *map = map__new(&self->mmap, MAP__FUNCTION, | 244 | struct map *map = map__new(&self->mmap, MAP__FUNCTION, |
| 210 | event__cwd, event__cwdlen); | 245 | session->cwd, session->cwdlen); |
| 211 | 246 | ||
| 212 | dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", | 247 | dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", |
| 213 | self->mmap.pid, self->mmap.tid, | 248 | self->mmap.pid, self->mmap.tid, |
| @@ -224,10 +259,10 @@ int event__process_mmap(event_t *self) | |||
| 224 | return 0; | 259 | return 0; |
| 225 | } | 260 | } |
| 226 | 261 | ||
| 227 | int event__process_task(event_t *self) | 262 | int event__process_task(event_t *self, struct perf_session *session) |
| 228 | { | 263 | { |
| 229 | struct thread *thread = threads__findnew(self->fork.pid); | 264 | struct thread *thread = perf_session__findnew(session, self->fork.pid); |
| 230 | struct thread *parent = threads__findnew(self->fork.ppid); | 265 | struct thread *parent = perf_session__findnew(session, self->fork.ppid); |
| 231 | 266 | ||
| 232 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | 267 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, |
| 233 | self->fork.ppid, self->fork.ptid); | 268 | self->fork.ppid, self->fork.ptid); |
| @@ -249,18 +284,20 @@ int event__process_task(event_t *self) | |||
| 249 | return 0; | 284 | return 0; |
| 250 | } | 285 | } |
| 251 | 286 | ||
| 252 | void thread__find_addr_location(struct thread *self, u8 cpumode, | 287 | void thread__find_addr_location(struct thread *self, |
| 288 | struct perf_session *session, u8 cpumode, | ||
| 253 | enum map_type type, u64 addr, | 289 | enum map_type type, u64 addr, |
| 254 | struct addr_location *al, | 290 | struct addr_location *al, |
| 255 | symbol_filter_t filter) | 291 | symbol_filter_t filter) |
| 256 | { | 292 | { |
| 257 | struct thread *thread = al->thread = self; | 293 | struct map_groups *mg = &self->mg; |
| 258 | 294 | ||
| 295 | al->thread = self; | ||
| 259 | al->addr = addr; | 296 | al->addr = addr; |
| 260 | 297 | ||
| 261 | if (cpumode & PERF_RECORD_MISC_KERNEL) { | 298 | if (cpumode & PERF_RECORD_MISC_KERNEL) { |
| 262 | al->level = 'k'; | 299 | al->level = 'k'; |
| 263 | thread = kthread; | 300 | mg = &session->kmaps; |
| 264 | } else if (cpumode & PERF_RECORD_MISC_USER) | 301 | } else if (cpumode & PERF_RECORD_MISC_USER) |
| 265 | al->level = '.'; | 302 | al->level = '.'; |
| 266 | else { | 303 | else { |
| @@ -270,7 +307,7 @@ void thread__find_addr_location(struct thread *self, u8 cpumode, | |||
| 270 | return; | 307 | return; |
| 271 | } | 308 | } |
| 272 | try_again: | 309 | try_again: |
| 273 | al->map = thread__find_map(thread, type, al->addr); | 310 | al->map = map_groups__find(mg, type, al->addr); |
| 274 | if (al->map == NULL) { | 311 | if (al->map == NULL) { |
| 275 | /* | 312 | /* |
| 276 | * If this is outside of all known maps, and is a negative | 313 | * If this is outside of all known maps, and is a negative |
| @@ -281,32 +318,139 @@ try_again: | |||
| 281 | * "[vdso]" dso, but for now lets use the old trick of looking | 318 | * "[vdso]" dso, but for now lets use the old trick of looking |
| 282 | * in the whole kernel symbol list. | 319 | * in the whole kernel symbol list. |
| 283 | */ | 320 | */ |
| 284 | if ((long long)al->addr < 0 && thread != kthread) { | 321 | if ((long long)al->addr < 0 && mg != &session->kmaps) { |
| 285 | thread = kthread; | 322 | mg = &session->kmaps; |
| 286 | goto try_again; | 323 | goto try_again; |
| 287 | } | 324 | } |
| 288 | al->sym = NULL; | 325 | al->sym = NULL; |
| 289 | } else { | 326 | } else { |
| 290 | al->addr = al->map->map_ip(al->map, al->addr); | 327 | al->addr = al->map->map_ip(al->map, al->addr); |
| 291 | al->sym = map__find_symbol(al->map, al->addr, filter); | 328 | al->sym = map__find_symbol(al->map, session, al->addr, filter); |
| 292 | } | 329 | } |
| 293 | } | 330 | } |
| 294 | 331 | ||
| 295 | int event__preprocess_sample(const event_t *self, struct addr_location *al, | 332 | static void dso__calc_col_width(struct dso *self) |
| 296 | symbol_filter_t filter) | 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) | ||
| 297 | { | 347 | { |
| 298 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 348 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
| 299 | struct thread *thread = threads__findnew(self->ip.pid); | 349 | struct thread *thread = perf_session__findnew(session, self->ip.pid); |
| 300 | 350 | ||
| 301 | if (thread == NULL) | 351 | if (thread == NULL) |
| 302 | return -1; | 352 | return -1; |
| 303 | 353 | ||
| 354 | if (symbol_conf.comm_list && | ||
| 355 | !strlist__has_entry(symbol_conf.comm_list, thread->comm)) | ||
| 356 | goto out_filtered; | ||
| 357 | |||
| 304 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 358 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
| 305 | 359 | ||
| 306 | thread__find_addr_location(thread, cpumode, MAP__FUNCTION, | 360 | thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION, |
| 307 | self->ip.ip, al, filter); | 361 | self->ip.ip, al, filter); |
| 308 | dump_printf(" ...... dso: %s\n", | 362 | dump_printf(" ...... dso: %s\n", |
| 309 | al->map ? al->map->dso->long_name : | 363 | al->map ? al->map->dso->long_name : |
| 310 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 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 | |||
| 311 | return 0; | 455 | return 0; |
| 312 | } | 456 | } |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index a4cc8105cf67..690a96d0467c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
| @@ -56,11 +56,25 @@ struct read_event { | |||
| 56 | u64 id; | 56 | u64 id; |
| 57 | }; | 57 | }; |
| 58 | 58 | ||
| 59 | struct sample_event{ | 59 | struct sample_event { |
| 60 | struct perf_event_header header; | 60 | struct perf_event_header header; |
| 61 | u64 array[]; | 61 | u64 array[]; |
| 62 | }; | 62 | }; |
| 63 | 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 | |||
| 64 | #define BUILD_ID_SIZE 20 | 78 | #define BUILD_ID_SIZE 20 |
| 65 | 79 | ||
| 66 | struct build_id_event { | 80 | struct build_id_event { |
| @@ -81,18 +95,19 @@ typedef union event_union { | |||
| 81 | } event_t; | 95 | } event_t; |
| 82 | 96 | ||
| 83 | struct events_stats { | 97 | struct events_stats { |
| 84 | unsigned long total; | 98 | u64 total; |
| 85 | unsigned long lost; | 99 | u64 lost; |
| 86 | }; | 100 | }; |
| 87 | 101 | ||
| 88 | void event__print_totals(void); | 102 | void event__print_totals(void); |
| 89 | 103 | ||
| 90 | enum map_type { | 104 | enum map_type { |
| 91 | MAP__FUNCTION = 0, | 105 | MAP__FUNCTION = 0, |
| 92 | 106 | MAP__VARIABLE, | |
| 93 | MAP__NR_TYPES, | ||
| 94 | }; | 107 | }; |
| 95 | 108 | ||
| 109 | #define MAP__NR_TYPES (MAP__VARIABLE + 1) | ||
| 110 | |||
| 96 | struct map { | 111 | struct map { |
| 97 | union { | 112 | union { |
| 98 | struct rb_node rb_node; | 113 | struct rb_node rb_node; |
| @@ -134,26 +149,35 @@ void map__delete(struct map *self); | |||
| 134 | struct map *map__clone(struct map *self); | 149 | struct map *map__clone(struct map *self); |
| 135 | int map__overlap(struct map *l, struct map *r); | 150 | int map__overlap(struct map *l, struct map *r); |
| 136 | size_t map__fprintf(struct map *self, FILE *fp); | 151 | size_t map__fprintf(struct map *self, FILE *fp); |
| 137 | struct symbol *map__find_symbol(struct map *self, u64 addr, | 152 | |
| 138 | symbol_filter_t filter); | 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); | ||
| 139 | void map__fixup_start(struct map *self); | 162 | void map__fixup_start(struct map *self); |
| 140 | void map__fixup_end(struct map *self); | 163 | void map__fixup_end(struct map *self); |
| 141 | 164 | ||
| 142 | int event__synthesize_thread(pid_t pid, int (*process)(event_t *event)); | 165 | int event__synthesize_thread(pid_t pid, |
| 143 | void event__synthesize_threads(int (*process)(event_t *event)); | 166 | int (*process)(event_t *event, |
| 144 | 167 | struct perf_session *session), | |
| 145 | extern char *event__cwd; | 168 | struct perf_session *session); |
| 146 | extern int event__cwdlen; | 169 | void event__synthesize_threads(int (*process)(event_t *event, |
| 147 | extern struct events_stats event__stats; | 170 | struct perf_session *session), |
| 148 | extern unsigned long event__total[PERF_RECORD_MAX]; | 171 | struct perf_session *session); |
| 149 | 172 | ||
| 150 | int event__process_comm(event_t *self); | 173 | int event__process_comm(event_t *self, struct perf_session *session); |
| 151 | int event__process_lost(event_t *self); | 174 | int event__process_lost(event_t *self, struct perf_session *session); |
| 152 | int event__process_mmap(event_t *self); | 175 | int event__process_mmap(event_t *self, struct perf_session *session); |
| 153 | int event__process_task(event_t *self); | 176 | int event__process_task(event_t *self, struct perf_session *session); |
| 154 | 177 | ||
| 155 | struct addr_location; | 178 | struct addr_location; |
| 156 | int event__preprocess_sample(const event_t *self, struct addr_location *al, | 179 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
| 157 | symbol_filter_t filter); | 180 | struct addr_location *al, symbol_filter_t filter); |
| 181 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data); | ||
| 158 | 182 | ||
| 159 | #endif /* __PERF_RECORD_H */ | 183 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4805e6dfd23c..8a0bca55106f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -8,8 +8,8 @@ | |||
| 8 | #include "header.h" | 8 | #include "header.h" |
| 9 | #include "../perf.h" | 9 | #include "../perf.h" |
| 10 | #include "trace-event.h" | 10 | #include "trace-event.h" |
| 11 | #include "session.h" | ||
| 11 | #include "symbol.h" | 12 | #include "symbol.h" |
| 12 | #include "data_map.h" | ||
| 13 | #include "debug.h" | 13 | #include "debug.h" |
| 14 | 14 | ||
| 15 | /* | 15 | /* |
| @@ -58,35 +58,19 @@ int perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | |||
| 58 | return 0; | 58 | return 0; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | /* | 61 | int perf_header__init(struct perf_header *self) |
| 62 | * Create new perf.data header: | ||
| 63 | */ | ||
| 64 | struct perf_header *perf_header__new(void) | ||
| 65 | { | 62 | { |
| 66 | struct perf_header *self = zalloc(sizeof(*self)); | 63 | self->size = 1; |
| 67 | 64 | self->attr = malloc(sizeof(void *)); | |
| 68 | if (self != NULL) { | 65 | return self->attr == NULL ? -ENOMEM : 0; |
| 69 | self->size = 1; | ||
| 70 | self->attr = malloc(sizeof(void *)); | ||
| 71 | |||
| 72 | if (self->attr == NULL) { | ||
| 73 | free(self); | ||
| 74 | self = NULL; | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | return self; | ||
| 79 | } | 66 | } |
| 80 | 67 | ||
| 81 | void perf_header__delete(struct perf_header *self) | 68 | void perf_header__exit(struct perf_header *self) |
| 82 | { | 69 | { |
| 83 | int i; | 70 | int i; |
| 84 | |||
| 85 | for (i = 0; i < self->attrs; ++i) | 71 | for (i = 0; i < self->attrs; ++i) |
| 86 | perf_header_attr__delete(self->attr[i]); | 72 | perf_header_attr__delete(self->attr[i]); |
| 87 | |||
| 88 | free(self->attr); | 73 | free(self->attr); |
| 89 | free(self); | ||
| 90 | } | 74 | } |
| 91 | 75 | ||
| 92 | int perf_header__add_attr(struct perf_header *self, | 76 | int perf_header__add_attr(struct perf_header *self, |
| @@ -187,7 +171,9 @@ static int do_write(int fd, const void *buf, size_t size) | |||
| 187 | 171 | ||
| 188 | static int __dsos__write_buildid_table(struct list_head *head, int fd) | 172 | static int __dsos__write_buildid_table(struct list_head *head, int fd) |
| 189 | { | 173 | { |
| 174 | #define NAME_ALIGN 64 | ||
| 190 | struct dso *pos; | 175 | struct dso *pos; |
| 176 | static const char zero_buf[NAME_ALIGN]; | ||
| 191 | 177 | ||
| 192 | list_for_each_entry(pos, head, node) { | 178 | list_for_each_entry(pos, head, node) { |
| 193 | int err; | 179 | int err; |
| @@ -197,14 +183,17 @@ static int __dsos__write_buildid_table(struct list_head *head, int fd) | |||
| 197 | if (!pos->has_build_id) | 183 | if (!pos->has_build_id) |
| 198 | continue; | 184 | continue; |
| 199 | len = pos->long_name_len + 1; | 185 | len = pos->long_name_len + 1; |
| 200 | len = ALIGN(len, 64); | 186 | len = ALIGN(len, NAME_ALIGN); |
| 201 | memset(&b, 0, sizeof(b)); | 187 | memset(&b, 0, sizeof(b)); |
| 202 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | 188 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); |
| 203 | b.header.size = sizeof(b) + len; | 189 | b.header.size = sizeof(b) + len; |
| 204 | err = do_write(fd, &b, sizeof(b)); | 190 | err = do_write(fd, &b, sizeof(b)); |
| 205 | if (err < 0) | 191 | if (err < 0) |
| 206 | return err; | 192 | return err; |
| 207 | err = do_write(fd, pos->long_name, len); | 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); | ||
| 208 | if (err < 0) | 197 | if (err < 0) |
| 209 | return err; | 198 | return err; |
| 210 | } | 199 | } |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index d1dbe2b79c42..d118d05d3abe 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
| @@ -55,8 +55,8 @@ struct perf_header { | |||
| 55 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | 55 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); |
| 56 | }; | 56 | }; |
| 57 | 57 | ||
| 58 | struct perf_header *perf_header__new(void); | 58 | int perf_header__init(struct perf_header *self); |
| 59 | void perf_header__delete(struct perf_header *self); | 59 | void perf_header__exit(struct perf_header *self); |
| 60 | 60 | ||
| 61 | int perf_header__read(struct perf_header *self, int fd); | 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); | 62 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 0ebf6ee16caa..e8daf5ca6fd2 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -1,9 +1,7 @@ | |||
| 1 | #include "hist.h" | 1 | #include "hist.h" |
| 2 | 2 | #include "session.h" | |
| 3 | struct rb_root hist; | 3 | #include "sort.h" |
| 4 | struct rb_root collapse_hists; | 4 | #include <math.h> |
| 5 | struct rb_root output_hists; | ||
| 6 | int callchain; | ||
| 7 | 5 | ||
| 8 | struct callchain_param callchain_param = { | 6 | struct callchain_param callchain_param = { |
| 9 | .mode = CHAIN_GRAPH_REL, | 7 | .mode = CHAIN_GRAPH_REL, |
| @@ -14,11 +12,12 @@ struct callchain_param callchain_param = { | |||
| 14 | * histogram, sorted on item, collects counts | 12 | * histogram, sorted on item, collects counts |
| 15 | */ | 13 | */ |
| 16 | 14 | ||
| 17 | struct hist_entry *__hist_entry__add(struct addr_location *al, | 15 | struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, |
| 18 | struct symbol *sym_parent, | 16 | struct addr_location *al, |
| 19 | u64 count, bool *hit) | 17 | struct symbol *sym_parent, |
| 18 | u64 count, bool *hit) | ||
| 20 | { | 19 | { |
| 21 | struct rb_node **p = &hist.rb_node; | 20 | struct rb_node **p = &self->hists.rb_node; |
| 22 | struct rb_node *parent = NULL; | 21 | struct rb_node *parent = NULL; |
| 23 | struct hist_entry *he; | 22 | struct hist_entry *he; |
| 24 | struct hist_entry entry = { | 23 | struct hist_entry entry = { |
| @@ -54,7 +53,7 @@ struct hist_entry *__hist_entry__add(struct addr_location *al, | |||
| 54 | return NULL; | 53 | return NULL; |
| 55 | *he = entry; | 54 | *he = entry; |
| 56 | rb_link_node(&he->rb_node, parent, p); | 55 | rb_link_node(&he->rb_node, parent, p); |
| 57 | rb_insert_color(&he->rb_node, &hist); | 56 | rb_insert_color(&he->rb_node, &self->hists); |
| 58 | *hit = false; | 57 | *hit = false; |
| 59 | return he; | 58 | return he; |
| 60 | } | 59 | } |
| @@ -102,9 +101,9 @@ void hist_entry__free(struct hist_entry *he) | |||
| 102 | * collapse the histogram | 101 | * collapse the histogram |
| 103 | */ | 102 | */ |
| 104 | 103 | ||
| 105 | void collapse__insert_entry(struct hist_entry *he) | 104 | static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) |
| 106 | { | 105 | { |
| 107 | struct rb_node **p = &collapse_hists.rb_node; | 106 | struct rb_node **p = &root->rb_node; |
| 108 | struct rb_node *parent = NULL; | 107 | struct rb_node *parent = NULL; |
| 109 | struct hist_entry *iter; | 108 | struct hist_entry *iter; |
| 110 | int64_t cmp; | 109 | int64_t cmp; |
| @@ -128,38 +127,45 @@ void collapse__insert_entry(struct hist_entry *he) | |||
| 128 | } | 127 | } |
| 129 | 128 | ||
| 130 | rb_link_node(&he->rb_node, parent, p); | 129 | rb_link_node(&he->rb_node, parent, p); |
| 131 | rb_insert_color(&he->rb_node, &collapse_hists); | 130 | rb_insert_color(&he->rb_node, root); |
| 132 | } | 131 | } |
| 133 | 132 | ||
| 134 | void collapse__resort(void) | 133 | void perf_session__collapse_resort(struct perf_session *self) |
| 135 | { | 134 | { |
| 135 | struct rb_root tmp; | ||
| 136 | struct rb_node *next; | 136 | struct rb_node *next; |
| 137 | struct hist_entry *n; | 137 | struct hist_entry *n; |
| 138 | 138 | ||
| 139 | if (!sort__need_collapse) | 139 | if (!sort__need_collapse) |
| 140 | return; | 140 | return; |
| 141 | 141 | ||
| 142 | next = rb_first(&hist); | 142 | tmp = RB_ROOT; |
| 143 | next = rb_first(&self->hists); | ||
| 144 | |||
| 143 | while (next) { | 145 | while (next) { |
| 144 | n = rb_entry(next, struct hist_entry, rb_node); | 146 | n = rb_entry(next, struct hist_entry, rb_node); |
| 145 | next = rb_next(&n->rb_node); | 147 | next = rb_next(&n->rb_node); |
| 146 | 148 | ||
| 147 | rb_erase(&n->rb_node, &hist); | 149 | rb_erase(&n->rb_node, &self->hists); |
| 148 | collapse__insert_entry(n); | 150 | collapse__insert_entry(&tmp, n); |
| 149 | } | 151 | } |
| 152 | |||
| 153 | self->hists = tmp; | ||
| 150 | } | 154 | } |
| 151 | 155 | ||
| 152 | /* | 156 | /* |
| 153 | * reverse the map, sort on count. | 157 | * reverse the map, sort on count. |
| 154 | */ | 158 | */ |
| 155 | 159 | ||
| 156 | void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) | 160 | static void perf_session__insert_output_hist_entry(struct rb_root *root, |
| 161 | struct hist_entry *he, | ||
| 162 | u64 min_callchain_hits) | ||
| 157 | { | 163 | { |
| 158 | struct rb_node **p = &output_hists.rb_node; | 164 | struct rb_node **p = &root->rb_node; |
| 159 | struct rb_node *parent = NULL; | 165 | struct rb_node *parent = NULL; |
| 160 | struct hist_entry *iter; | 166 | struct hist_entry *iter; |
| 161 | 167 | ||
| 162 | if (callchain) | 168 | if (symbol_conf.use_callchain) |
| 163 | callchain_param.sort(&he->sorted_chain, &he->callchain, | 169 | callchain_param.sort(&he->sorted_chain, &he->callchain, |
| 164 | min_callchain_hits, &callchain_param); | 170 | min_callchain_hits, &callchain_param); |
| 165 | 171 | ||
| @@ -174,29 +180,483 @@ void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) | |||
| 174 | } | 180 | } |
| 175 | 181 | ||
| 176 | rb_link_node(&he->rb_node, parent, p); | 182 | rb_link_node(&he->rb_node, parent, p); |
| 177 | rb_insert_color(&he->rb_node, &output_hists); | 183 | rb_insert_color(&he->rb_node, root); |
| 178 | } | 184 | } |
| 179 | 185 | ||
| 180 | void output__resort(u64 total_samples) | 186 | void perf_session__output_resort(struct perf_session *self, u64 total_samples) |
| 181 | { | 187 | { |
| 188 | struct rb_root tmp; | ||
| 182 | struct rb_node *next; | 189 | struct rb_node *next; |
| 183 | struct hist_entry *n; | 190 | struct hist_entry *n; |
| 184 | struct rb_root *tree = &hist; | ||
| 185 | u64 min_callchain_hits; | 191 | u64 min_callchain_hits; |
| 186 | 192 | ||
| 187 | min_callchain_hits = | 193 | min_callchain_hits = |
| 188 | total_samples * (callchain_param.min_percent / 100); | 194 | total_samples * (callchain_param.min_percent / 100); |
| 189 | 195 | ||
| 190 | if (sort__need_collapse) | 196 | tmp = RB_ROOT; |
| 191 | tree = &collapse_hists; | 197 | next = rb_first(&self->hists); |
| 192 | |||
| 193 | next = rb_first(tree); | ||
| 194 | 198 | ||
| 195 | while (next) { | 199 | while (next) { |
| 196 | n = rb_entry(next, struct hist_entry, rb_node); | 200 | n = rb_entry(next, struct hist_entry, rb_node); |
| 197 | next = rb_next(&n->rb_node); | 201 | next = rb_next(&n->rb_node); |
| 198 | 202 | ||
| 199 | rb_erase(&n->rb_node, tree); | 203 | rb_erase(&n->rb_node, &self->hists); |
| 200 | output__insert_entry(n, min_callchain_hits); | 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); | ||
| 201 | } | 657 | } |
| 658 | |||
| 659 | free(rem_sq_bracket); | ||
| 660 | |||
| 661 | return ret; | ||
| 202 | } | 662 | } |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 3020db0c9292..e5f99b24048b 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -1,50 +1,27 @@ | |||
| 1 | #ifndef __PERF_HIST_H | 1 | #ifndef __PERF_HIST_H |
| 2 | #define __PERF_HIST_H | 2 | #define __PERF_HIST_H |
| 3 | #include "../builtin.h" | ||
| 4 | 3 | ||
| 5 | #include "util.h" | 4 | #include <linux/types.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" | 5 | #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 | 6 | ||
| 24 | #include "thread.h" | ||
| 25 | #include "sort.h" | ||
| 26 | |||
| 27 | extern struct rb_root hist; | ||
| 28 | extern struct rb_root collapse_hists; | ||
| 29 | extern struct rb_root output_hists; | ||
| 30 | extern int callchain; | ||
| 31 | extern struct callchain_param callchain_param; | 7 | extern struct callchain_param callchain_param; |
| 32 | extern unsigned long total; | ||
| 33 | extern unsigned long total_mmap; | ||
| 34 | extern unsigned long total_comm; | ||
| 35 | extern unsigned long total_fork; | ||
| 36 | extern unsigned long total_unknown; | ||
| 37 | extern unsigned long total_lost; | ||
| 38 | 8 | ||
| 39 | struct hist_entry *__hist_entry__add(struct addr_location *al, | 9 | struct perf_session; |
| 40 | struct symbol *parent, | 10 | struct hist_entry; |
| 41 | u64 count, bool *hit); | 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); | ||
| 42 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | 18 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); |
| 43 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | 19 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); |
| 44 | extern void hist_entry__free(struct hist_entry *); | 20 | void hist_entry__free(struct hist_entry *); |
| 45 | extern void collapse__insert_entry(struct hist_entry *); | ||
| 46 | extern void collapse__resort(void); | ||
| 47 | extern void output__insert_entry(struct hist_entry *, u64); | ||
| 48 | extern void output__resort(u64); | ||
| 49 | 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); | ||
| 50 | #endif /* __PERF_HIST_H */ | 27 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 69f94fe9db20..c4d55a0da2ea 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -104,43 +104,70 @@ void map__fixup_end(struct map *self) | |||
| 104 | 104 | ||
| 105 | #define DSO__DELETED "(deleted)" | 105 | #define DSO__DELETED "(deleted)" |
| 106 | 106 | ||
| 107 | struct symbol *map__find_symbol(struct map *self, u64 addr, | 107 | int map__load(struct map *self, struct perf_session *session, |
| 108 | symbol_filter_t filter) | 108 | symbol_filter_t filter) |
| 109 | { | 109 | { |
| 110 | if (!dso__loaded(self->dso, self->type)) { | 110 | const char *name = self->dso->long_name; |
| 111 | int nr = dso__load(self->dso, self, filter); | 111 | int nr; |
| 112 | 112 | ||
| 113 | if (nr < 0) { | 113 | if (dso__loaded(self->dso, self->type)) |
| 114 | if (self->dso->has_build_id) { | 114 | return 0; |
| 115 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 115 | |
| 116 | 116 | nr = dso__load(self->dso, self, session, filter); | |
| 117 | build_id__sprintf(self->dso->build_id, | 117 | if (nr < 0) { |
| 118 | sizeof(self->dso->build_id), | 118 | if (self->dso->has_build_id) { |
| 119 | sbuild_id); | 119 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
| 120 | pr_warning("%s with build id %s not found", | 120 | |
| 121 | self->dso->long_name, sbuild_id); | 121 | build_id__sprintf(self->dso->build_id, |
| 122 | } else | 122 | sizeof(self->dso->build_id), |
| 123 | pr_warning("Failed to open %s", | 123 | sbuild_id); |
| 124 | self->dso->long_name); | 124 | pr_warning("%s with build id %s not found", |
| 125 | pr_warning(", continuing without symbols\n"); | 125 | name, sbuild_id); |
| 126 | return NULL; | 126 | } else |
| 127 | } else if (nr == 0) { | 127 | pr_warning("Failed to open %s", name); |
| 128 | const char *name = self->dso->long_name; | 128 | |
| 129 | const size_t len = strlen(name); | 129 | pr_warning(", continuing without symbols\n"); |
| 130 | const size_t real_len = len - sizeof(DSO__DELETED); | 130 | return -1; |
| 131 | 131 | } else if (nr == 0) { | |
| 132 | if (len > sizeof(DSO__DELETED) && | 132 | const size_t len = strlen(name); |
| 133 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | 133 | const size_t real_len = len - sizeof(DSO__DELETED); |
| 134 | pr_warning("%.*s was updated, restart the long running apps that use it!\n", | 134 | |
| 135 | (int)real_len, name); | 135 | if (len > sizeof(DSO__DELETED) && |
| 136 | } else { | 136 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { |
| 137 | pr_warning("no symbols found in %s, maybe install a debug package?\n", name); | 137 | pr_warning("%.*s was updated, restart the long " |
| 138 | } | 138 | "running apps that use it!\n", |
| 139 | return NULL; | 139 | (int)real_len, name); |
| 140 | } else { | ||
| 141 | pr_warning("no symbols found in %s, maybe install " | ||
| 142 | "a debug package?\n", name); | ||
| 140 | } | 143 | } |
| 144 | |||
| 145 | return -1; | ||
| 141 | } | 146 | } |
| 142 | 147 | ||
| 143 | return self->dso->find_symbol(self->dso, self->type, addr); | 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); | ||
| 144 | } | 171 | } |
| 145 | 172 | ||
| 146 | struct map *map__clone(struct map *self) | 173 | struct map *map__clone(struct map *self) |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 9e5dbd66d34d..e5bc0fb016b2 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -197,7 +197,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
| 197 | if (id == config) { | 197 | if (id == config) { |
| 198 | closedir(evt_dir); | 198 | closedir(evt_dir); |
| 199 | closedir(sys_dir); | 199 | closedir(sys_dir); |
| 200 | path = zalloc(sizeof(path)); | 200 | path = zalloc(sizeof(*path)); |
| 201 | path->system = malloc(MAX_EVENT_LENGTH); | 201 | path->system = malloc(MAX_EVENT_LENGTH); |
| 202 | if (!path->system) { | 202 | if (!path->system) { |
| 203 | free(path); | 203 | free(path); |
| @@ -467,7 +467,6 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
| 467 | while ((evt_ent = readdir(evt_dir))) { | 467 | while ((evt_ent = readdir(evt_dir))) { |
| 468 | char event_opt[MAX_EVOPT_LEN + 1]; | 468 | char event_opt[MAX_EVOPT_LEN + 1]; |
| 469 | int len; | 469 | int len; |
| 470 | unsigned int rem = MAX_EVOPT_LEN; | ||
| 471 | 470 | ||
| 472 | if (!strcmp(evt_ent->d_name, ".") | 471 | if (!strcmp(evt_ent->d_name, ".") |
| 473 | || !strcmp(evt_ent->d_name, "..") | 472 | || !strcmp(evt_ent->d_name, "..") |
| @@ -475,20 +474,12 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
| 475 | || !strcmp(evt_ent->d_name, "filter")) | 474 | || !strcmp(evt_ent->d_name, "filter")) |
| 476 | continue; | 475 | continue; |
| 477 | 476 | ||
| 478 | 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, |
| 479 | evt_ent->d_name); | 478 | evt_ent->d_name, flags ? ":" : "", |
| 479 | flags ?: ""); | ||
| 480 | if (len < 0) | 480 | if (len < 0) |
| 481 | return EVT_FAILED; | 481 | return EVT_FAILED; |
| 482 | 482 | ||
| 483 | rem -= len; | ||
| 484 | if (flags) { | ||
| 485 | if (rem < strlen(flags) + 1) | ||
| 486 | return EVT_FAILED; | ||
| 487 | |||
| 488 | strcat(event_opt, ":"); | ||
| 489 | strcat(event_opt, flags); | ||
| 490 | } | ||
| 491 | |||
| 492 | if (parse_events(NULL, event_opt, 0)) | 483 | if (parse_events(NULL, event_opt, 0)) |
| 493 | return EVT_FAILED; | 484 | return EVT_FAILED; |
| 494 | } | 485 | } |
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/probe-event.c b/tools/perf/util/probe-event.c index cd7fbda5e2a5..29465d440043 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -48,6 +48,9 @@ | |||
| 48 | 48 | ||
| 49 | /* If there is no space to write, returns -E2BIG. */ | 49 | /* If there is no space to write, returns -E2BIG. */ |
| 50 | static int e_snprintf(char *str, size_t size, const char *format, ...) | 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, ...) | ||
| 51 | { | 54 | { |
| 52 | int ret; | 55 | int ret; |
| 53 | va_list ap; | 56 | va_list ap; |
| @@ -59,6 +62,18 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) | |||
| 59 | return ret; | 62 | return ret; |
| 60 | } | 63 | } |
| 61 | 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 | |||
| 62 | /* Parse probepoint definition. */ | 77 | /* Parse probepoint definition. */ |
| 63 | static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | 78 | static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) |
| 64 | { | 79 | { |
| @@ -66,10 +81,26 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 66 | char c, nc = 0; | 81 | char c, nc = 0; |
| 67 | /* | 82 | /* |
| 68 | * <Syntax> | 83 | * <Syntax> |
| 69 | * perf probe SRC:LN | 84 | * perf probe [EVENT=]SRC:LN |
| 70 | * perf probe FUNC[+OFFS|%return][@SRC] | 85 | * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC] |
| 86 | * | ||
| 87 | * TODO:Group name support | ||
| 71 | */ | 88 | */ |
| 72 | 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 | |||
| 73 | ptr = strpbrk(arg, ":+@%"); | 104 | ptr = strpbrk(arg, ":+@%"); |
| 74 | if (ptr) { | 105 | if (ptr) { |
| 75 | nc = *ptr; | 106 | nc = *ptr; |
| @@ -147,10 +178,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 147 | } | 178 | } |
| 148 | 179 | ||
| 149 | /* Parse perf-probe event definition */ | 180 | /* Parse perf-probe event definition */ |
| 150 | int parse_perf_probe_event(const char *str, struct probe_point *pp) | 181 | void parse_perf_probe_event(const char *str, struct probe_point *pp, |
| 182 | bool *need_dwarf) | ||
| 151 | { | 183 | { |
| 152 | char **argv; | 184 | char **argv; |
| 153 | int argc, i, need_dwarf = 0; | 185 | int argc, i; |
| 186 | |||
| 187 | *need_dwarf = false; | ||
| 154 | 188 | ||
| 155 | argv = argv_split(str, &argc); | 189 | argv = argv_split(str, &argc); |
| 156 | if (!argv) | 190 | if (!argv) |
| @@ -161,7 +195,7 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp) | |||
| 161 | /* Parse probe point */ | 195 | /* Parse probe point */ |
| 162 | parse_perf_probe_probepoint(argv[0], pp); | 196 | parse_perf_probe_probepoint(argv[0], pp); |
| 163 | if (pp->file || pp->line) | 197 | if (pp->file || pp->line) |
| 164 | need_dwarf = 1; | 198 | *need_dwarf = true; |
| 165 | 199 | ||
| 166 | /* Copy arguments and ensure return probe has no C argument */ | 200 | /* Copy arguments and ensure return probe has no C argument */ |
| 167 | pp->nr_args = argc - 1; | 201 | pp->nr_args = argc - 1; |
| @@ -174,17 +208,15 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp) | |||
| 174 | if (pp->retprobe) | 208 | if (pp->retprobe) |
| 175 | semantic_error("You can't specify local" | 209 | semantic_error("You can't specify local" |
| 176 | " variable for kretprobe"); | 210 | " variable for kretprobe"); |
| 177 | need_dwarf = 1; | 211 | *need_dwarf = true; |
| 178 | } | 212 | } |
| 179 | } | 213 | } |
| 180 | 214 | ||
| 181 | argv_free(argv); | 215 | argv_free(argv); |
| 182 | return need_dwarf; | ||
| 183 | } | 216 | } |
| 184 | 217 | ||
| 185 | /* Parse kprobe_events event into struct probe_point */ | 218 | /* Parse kprobe_events event into struct probe_point */ |
| 186 | void parse_trace_kprobe_event(const char *str, char **group, char **event, | 219 | void parse_trace_kprobe_event(const char *str, struct probe_point *pp) |
| 187 | struct probe_point *pp) | ||
| 188 | { | 220 | { |
| 189 | char pr; | 221 | char pr; |
| 190 | char *p; | 222 | char *p; |
| @@ -200,18 +232,17 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event, | |||
| 200 | 232 | ||
| 201 | /* Scan event and group name. */ | 233 | /* Scan event and group name. */ |
| 202 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", | 234 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", |
| 203 | &pr, (float *)(void *)group, (float *)(void *)event); | 235 | &pr, (float *)(void *)&pp->group, |
| 236 | (float *)(void *)&pp->event); | ||
| 204 | if (ret != 3) | 237 | if (ret != 3) |
| 205 | semantic_error("Failed to parse event name: %s", argv[0]); | 238 | semantic_error("Failed to parse event name: %s", argv[0]); |
| 206 | pr_debug("Group:%s Event:%s probe:%c\n", *group, *event, pr); | 239 | pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); |
| 207 | |||
| 208 | if (!pp) | ||
| 209 | goto end; | ||
| 210 | 240 | ||
| 211 | pp->retprobe = (pr == 'r'); | 241 | pp->retprobe = (pr == 'r'); |
| 212 | 242 | ||
| 213 | /* Scan function name and offset */ | 243 | /* Scan function name and offset */ |
| 214 | ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, &pp->offset); | 244 | ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, |
| 245 | &pp->offset); | ||
| 215 | if (ret == 1) | 246 | if (ret == 1) |
| 216 | pp->offset = 0; | 247 | pp->offset = 0; |
| 217 | 248 | ||
| @@ -230,15 +261,15 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event, | |||
| 230 | die("Failed to copy argument."); | 261 | die("Failed to copy argument."); |
| 231 | } | 262 | } |
| 232 | 263 | ||
| 233 | end: | ||
| 234 | argv_free(argv); | 264 | argv_free(argv); |
| 235 | } | 265 | } |
| 236 | 266 | ||
| 237 | int synthesize_perf_probe_event(struct probe_point *pp) | 267 | /* Synthesize only probe point (not argument) */ |
| 268 | int synthesize_perf_probe_point(struct probe_point *pp) | ||
| 238 | { | 269 | { |
| 239 | char *buf; | 270 | char *buf; |
| 240 | char offs[64] = "", line[64] = ""; | 271 | char offs[64] = "", line[64] = ""; |
| 241 | int i, len, ret; | 272 | int ret; |
| 242 | 273 | ||
| 243 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | 274 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); |
| 244 | if (!buf) | 275 | if (!buf) |
| @@ -258,11 +289,25 @@ int synthesize_perf_probe_event(struct probe_point *pp) | |||
| 258 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, | 289 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, |
| 259 | offs, pp->retprobe ? "%return" : "", line); | 290 | offs, pp->retprobe ? "%return" : "", line); |
| 260 | else | 291 | else |
| 261 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->file, line); | 292 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); |
| 262 | if (ret <= 0) | 293 | if (ret <= 0) { |
| 263 | goto error; | 294 | error: |
| 264 | len = ret; | 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; | ||
| 265 | 309 | ||
| 310 | buf = pp->probes[0]; | ||
| 266 | for (i = 0; i < pp->nr_args; i++) { | 311 | for (i = 0; i < pp->nr_args; i++) { |
| 267 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | 312 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", |
| 268 | pp->args[i]); | 313 | pp->args[i]); |
| @@ -275,6 +320,7 @@ int synthesize_perf_probe_event(struct probe_point *pp) | |||
| 275 | return pp->found; | 320 | return pp->found; |
| 276 | error: | 321 | error: |
| 277 | free(pp->probes[0]); | 322 | free(pp->probes[0]); |
| 323 | pp->probes[0] = NULL; | ||
| 278 | 324 | ||
| 279 | return ret; | 325 | return ret; |
| 280 | } | 326 | } |
| @@ -304,6 +350,7 @@ int synthesize_trace_kprobe_event(struct probe_point *pp) | |||
| 304 | return pp->found; | 350 | return pp->found; |
| 305 | error: | 351 | error: |
| 306 | free(pp->probes[0]); | 352 | free(pp->probes[0]); |
| 353 | pp->probes[0] = NULL; | ||
| 307 | 354 | ||
| 308 | return ret; | 355 | return ret; |
| 309 | } | 356 | } |
| @@ -363,6 +410,10 @@ static void clear_probe_point(struct probe_point *pp) | |||
| 363 | { | 410 | { |
| 364 | int i; | 411 | int i; |
| 365 | 412 | ||
| 413 | if (pp->event) | ||
| 414 | free(pp->event); | ||
| 415 | if (pp->group) | ||
| 416 | free(pp->group); | ||
| 366 | if (pp->function) | 417 | if (pp->function) |
| 367 | free(pp->function); | 418 | free(pp->function); |
| 368 | if (pp->file) | 419 | if (pp->file) |
| @@ -373,15 +424,33 @@ static void clear_probe_point(struct probe_point *pp) | |||
| 373 | free(pp->args); | 424 | free(pp->args); |
| 374 | for (i = 0; i < pp->found; i++) | 425 | for (i = 0; i < pp->found; i++) |
| 375 | free(pp->probes[i]); | 426 | free(pp->probes[i]); |
| 376 | memset(pp, 0, sizeof(pp)); | 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"); | ||
| 377 | } | 448 | } |
| 378 | 449 | ||
| 379 | /* List up current perf-probe events */ | 450 | /* List up current perf-probe events */ |
| 380 | void show_perf_probe_events(void) | 451 | void show_perf_probe_events(void) |
| 381 | { | 452 | { |
| 382 | unsigned int i; | ||
| 383 | int fd; | 453 | int fd; |
| 384 | char *group, *event; | ||
| 385 | struct probe_point pp; | 454 | struct probe_point pp; |
| 386 | struct strlist *rawlist; | 455 | struct strlist *rawlist; |
| 387 | struct str_node *ent; | 456 | struct str_node *ent; |
| @@ -390,13 +459,12 @@ void show_perf_probe_events(void) | |||
| 390 | rawlist = get_trace_kprobe_event_rawlist(fd); | 459 | rawlist = get_trace_kprobe_event_rawlist(fd); |
| 391 | close(fd); | 460 | close(fd); |
| 392 | 461 | ||
| 393 | for (i = 0; i < strlist__nr_entries(rawlist); i++) { | 462 | strlist__for_each(ent, rawlist) { |
| 394 | ent = strlist__entry(rawlist, i); | 463 | parse_trace_kprobe_event(ent->s, &pp); |
| 395 | parse_trace_kprobe_event(ent->s, &group, &event, &pp); | 464 | /* Synthesize only event probe point */ |
| 396 | synthesize_perf_probe_event(&pp); | 465 | synthesize_perf_probe_point(&pp); |
| 397 | printf("[%s:%s]\t%s\n", group, event, pp.probes[0]); | 466 | /* Show an event */ |
| 398 | free(group); | 467 | show_perf_probe_event(pp.event, pp.probes[0], &pp); |
| 399 | free(event); | ||
| 400 | clear_probe_point(&pp); | 468 | clear_probe_point(&pp); |
| 401 | } | 469 | } |
| 402 | 470 | ||
| @@ -404,21 +472,27 @@ void show_perf_probe_events(void) | |||
| 404 | } | 472 | } |
| 405 | 473 | ||
| 406 | /* Get current perf-probe event names */ | 474 | /* Get current perf-probe event names */ |
| 407 | static struct strlist *get_perf_event_names(int fd) | 475 | static struct strlist *get_perf_event_names(int fd, bool include_group) |
| 408 | { | 476 | { |
| 409 | unsigned int i; | 477 | char buf[128]; |
| 410 | char *group, *event; | ||
| 411 | struct strlist *sl, *rawlist; | 478 | struct strlist *sl, *rawlist; |
| 412 | struct str_node *ent; | 479 | struct str_node *ent; |
| 480 | struct probe_point pp; | ||
| 413 | 481 | ||
| 482 | memset(&pp, 0, sizeof(pp)); | ||
| 414 | rawlist = get_trace_kprobe_event_rawlist(fd); | 483 | rawlist = get_trace_kprobe_event_rawlist(fd); |
| 415 | 484 | ||
| 416 | sl = strlist__new(false, NULL); | 485 | sl = strlist__new(true, NULL); |
| 417 | for (i = 0; i < strlist__nr_entries(rawlist); i++) { | 486 | strlist__for_each(ent, rawlist) { |
| 418 | ent = strlist__entry(rawlist, i); | 487 | parse_trace_kprobe_event(ent->s, &pp); |
| 419 | parse_trace_kprobe_event(ent->s, &group, &event, NULL); | 488 | if (include_group) { |
| 420 | strlist__add(sl, event); | 489 | if (e_snprintf(buf, 128, "%s:%s", pp.group, |
| 421 | free(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); | ||
| 422 | } | 496 | } |
| 423 | 497 | ||
| 424 | strlist__delete(rawlist); | 498 | strlist__delete(rawlist); |
| @@ -426,24 +500,36 @@ static struct strlist *get_perf_event_names(int fd) | |||
| 426 | return sl; | 500 | return sl; |
| 427 | } | 501 | } |
| 428 | 502 | ||
| 429 | static int write_trace_kprobe_event(int fd, const char *buf) | 503 | static void write_trace_kprobe_event(int fd, const char *buf) |
| 430 | { | 504 | { |
| 431 | int ret; | 505 | int ret; |
| 432 | 506 | ||
| 507 | pr_debug("Writing event: %s\n", buf); | ||
| 433 | ret = write(fd, buf, strlen(buf)); | 508 | ret = write(fd, buf, strlen(buf)); |
| 434 | if (ret <= 0) | 509 | if (ret <= 0) |
| 435 | die("Failed to create event."); | 510 | die("Failed to write event: %s", strerror(errno)); |
| 436 | else | ||
| 437 | printf("Added new event: %s\n", buf); | ||
| 438 | |||
| 439 | return ret; | ||
| 440 | } | 511 | } |
| 441 | 512 | ||
| 442 | static void get_new_event_name(char *buf, size_t len, const char *base, | 513 | static void get_new_event_name(char *buf, size_t len, const char *base, |
| 443 | struct strlist *namelist) | 514 | struct strlist *namelist, bool allow_suffix) |
| 444 | { | 515 | { |
| 445 | int i, ret; | 516 | int i, ret; |
| 446 | for (i = 0; i < MAX_EVENT_INDEX; i++) { | 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++) { | ||
| 447 | ret = e_snprintf(buf, len, "%s_%d", base, i); | 533 | ret = e_snprintf(buf, len, "%s_%d", base, i); |
| 448 | if (ret < 0) | 534 | if (ret < 0) |
| 449 | die("snprintf() failed: %s", strerror(-ret)); | 535 | die("snprintf() failed: %s", strerror(-ret)); |
| @@ -454,31 +540,138 @@ static void get_new_event_name(char *buf, size_t len, const char *base, | |||
| 454 | die("Too many events are on the same function."); | 540 | die("Too many events are on the same function."); |
| 455 | } | 541 | } |
| 456 | 542 | ||
| 457 | void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) | 543 | void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, |
| 544 | bool force_add) | ||
| 458 | { | 545 | { |
| 459 | int i, j, fd; | 546 | int i, j, fd; |
| 460 | struct probe_point *pp; | 547 | struct probe_point *pp; |
| 461 | char buf[MAX_CMDLEN]; | 548 | char buf[MAX_CMDLEN]; |
| 462 | char event[64]; | 549 | char event[64]; |
| 463 | struct strlist *namelist; | 550 | struct strlist *namelist; |
| 551 | bool allow_suffix; | ||
| 464 | 552 | ||
| 465 | fd = open_kprobe_events(O_RDWR, O_APPEND); | 553 | fd = open_kprobe_events(O_RDWR, O_APPEND); |
| 466 | /* Get current event names */ | 554 | /* Get current event names */ |
| 467 | namelist = get_perf_event_names(fd); | 555 | namelist = get_perf_event_names(fd, false); |
| 468 | 556 | ||
| 469 | for (j = 0; j < nr_probes; j++) { | 557 | for (j = 0; j < nr_probes; j++) { |
| 470 | pp = 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; | ||
| 471 | for (i = 0; i < pp->found; i++) { | 566 | for (i = 0; i < pp->found; i++) { |
| 472 | /* Get an unused new event name */ | 567 | /* Get an unused new event name */ |
| 473 | get_new_event_name(event, 64, pp->function, namelist); | 568 | get_new_event_name(event, 64, pp->event, namelist, |
| 569 | allow_suffix); | ||
| 474 | snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", | 570 | snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", |
| 475 | pp->retprobe ? 'r' : 'p', | 571 | pp->retprobe ? 'r' : 'p', |
| 476 | PERFPROBE_GROUP, event, | 572 | pp->group, event, |
| 477 | pp->probes[i]); | 573 | pp->probes[i]); |
| 478 | write_trace_kprobe_event(fd, buf); | 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); | ||
| 479 | /* Add added event name to namelist */ | 579 | /* Add added event name to namelist */ |
| 480 | strlist__add(namelist, event); | 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); | ||
| 481 | } | 638 | } |
| 482 | } | 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); | ||
| 483 | close(fd); | 675 | close(fd); |
| 484 | } | 676 | } |
| 677 | |||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 0c6fe56fe38a..7f1d499118c0 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
| @@ -1,15 +1,19 @@ | |||
| 1 | #ifndef _PROBE_EVENT_H | 1 | #ifndef _PROBE_EVENT_H |
| 2 | #define _PROBE_EVENT_H | 2 | #define _PROBE_EVENT_H |
| 3 | 3 | ||
| 4 | #include <stdbool.h> | ||
| 4 | #include "probe-finder.h" | 5 | #include "probe-finder.h" |
| 5 | #include "strlist.h" | 6 | #include "strlist.h" |
| 6 | 7 | ||
| 7 | extern int parse_perf_probe_event(const char *str, struct probe_point *pp); | 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); | ||
| 8 | extern int synthesize_perf_probe_event(struct probe_point *pp); | 11 | extern int synthesize_perf_probe_event(struct probe_point *pp); |
| 9 | extern void parse_trace_kprobe_event(const char *str, char **group, | 12 | extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); |
| 10 | char **event, struct probe_point *pp); | ||
| 11 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); | 13 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); |
| 12 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); | 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); | ||
| 13 | extern void show_perf_probe_events(void); | 17 | extern void show_perf_probe_events(void); |
| 14 | 18 | ||
| 15 | /* Maximum index number of event-name postfix */ | 19 | /* Maximum index number of event-name postfix */ |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 293cdfc1b8ca..4b852c0d16a5 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -106,7 +106,7 @@ static int strtailcmp(const char *s1, const char *s2) | |||
| 106 | { | 106 | { |
| 107 | int i1 = strlen(s1); | 107 | int i1 = strlen(s1); |
| 108 | int i2 = strlen(s2); | 108 | int i2 = strlen(s2); |
| 109 | while (--i1 > 0 && --i2 > 0) { | 109 | while (--i1 >= 0 && --i2 >= 0) { |
| 110 | if (s1[i1] != s2[i2]) | 110 | if (s1[i1] != s2[i2]) |
| 111 | return s1[i1] - s2[i2]; | 111 | return s1[i1] - s2[i2]; |
| 112 | } | 112 | } |
| @@ -687,10 +687,8 @@ int find_probepoint(int fd, struct probe_point *pp) | |||
| 687 | struct probe_finder pf = {.pp = pp}; | 687 | struct probe_finder pf = {.pp = pp}; |
| 688 | 688 | ||
| 689 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); | 689 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); |
| 690 | if (ret != DW_DLV_OK) { | 690 | if (ret != DW_DLV_OK) |
| 691 | pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO.\n"); | ||
| 692 | return -ENOENT; | 691 | return -ENOENT; |
| 693 | } | ||
| 694 | 692 | ||
| 695 | pp->found = 0; | 693 | pp->found = 0; |
| 696 | while (++cu_number) { | 694 | while (++cu_number) { |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index bdebca6697d2..a4086aaddb73 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | #ifndef _PROBE_FINDER_H | 1 | #ifndef _PROBE_FINDER_H |
| 2 | #define _PROBE_FINDER_H | 2 | #define _PROBE_FINDER_H |
| 3 | 3 | ||
| 4 | #define MAX_PATH_LEN 256 | 4 | #define MAX_PATH_LEN 256 |
| 5 | #define MAX_PROBE_BUFFER 1024 | 5 | #define MAX_PROBE_BUFFER 1024 |
| 6 | #define MAX_PROBES 128 | 6 | #define MAX_PROBES 128 |
| 7 | 7 | ||
| 8 | static inline int is_c_varname(const char *name) | 8 | static inline int is_c_varname(const char *name) |
| 9 | { | 9 | { |
| @@ -12,45 +12,53 @@ static inline int is_c_varname(const char *name) | |||
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | struct probe_point { | 14 | struct probe_point { |
| 15 | char *event; /* Event name */ | ||
| 16 | char *group; /* Event group */ | ||
| 17 | |||
| 15 | /* Inputs */ | 18 | /* Inputs */ |
| 16 | char *file; /* File name */ | 19 | char *file; /* File name */ |
| 17 | int line; /* Line number */ | 20 | int line; /* Line number */ |
| 18 | 21 | ||
| 19 | char *function; /* Function name */ | 22 | char *function; /* Function name */ |
| 20 | int offset; /* Offset bytes */ | 23 | int offset; /* Offset bytes */ |
| 21 | 24 | ||
| 22 | int nr_args; /* Number of arguments */ | 25 | int nr_args; /* Number of arguments */ |
| 23 | char **args; /* Arguments */ | 26 | char **args; /* Arguments */ |
| 24 | 27 | ||
| 25 | int retprobe; /* Return probe */ | 28 | int retprobe; /* Return probe */ |
| 26 | 29 | ||
| 27 | /* Output */ | 30 | /* Output */ |
| 28 | int found; /* Number of found probe points */ | 31 | int found; /* Number of found probe points */ |
| 29 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ | 32 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ |
| 30 | }; | 33 | }; |
| 31 | 34 | ||
| 32 | #ifndef NO_LIBDWARF | 35 | #ifndef NO_LIBDWARF |
| 33 | extern int find_probepoint(int fd, struct probe_point *pp); | 36 | extern int find_probepoint(int fd, struct probe_point *pp); |
| 34 | 37 | ||
| 35 | #include <libdwarf/dwarf.h> | 38 | /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ |
| 36 | #include <libdwarf/libdwarf.h> | 39 | #ifndef _MIPS_SZLONG |
| 40 | # define _MIPS_SZLONG 0 | ||
| 41 | #endif | ||
| 42 | |||
| 43 | #include <dwarf.h> | ||
| 44 | #include <libdwarf.h> | ||
| 37 | 45 | ||
| 38 | struct probe_finder { | 46 | struct probe_finder { |
| 39 | struct probe_point *pp; /* Target probe point */ | 47 | struct probe_point *pp; /* Target probe point */ |
| 40 | 48 | ||
| 41 | /* For function searching */ | 49 | /* For function searching */ |
| 42 | Dwarf_Addr addr; /* Address */ | 50 | Dwarf_Addr addr; /* Address */ |
| 43 | Dwarf_Unsigned fno; /* File number */ | 51 | Dwarf_Unsigned fno; /* File number */ |
| 44 | Dwarf_Unsigned lno; /* Line number */ | 52 | Dwarf_Unsigned lno; /* Line number */ |
| 45 | Dwarf_Off inl_offs; /* Inline offset */ | 53 | Dwarf_Off inl_offs; /* Inline offset */ |
| 46 | Dwarf_Die cu_die; /* Current CU */ | 54 | Dwarf_Die cu_die; /* Current CU */ |
| 47 | 55 | ||
| 48 | /* For variable searching */ | 56 | /* For variable searching */ |
| 49 | Dwarf_Addr cu_base; /* Current CU base address */ | 57 | Dwarf_Addr cu_base; /* Current CU base address */ |
| 50 | Dwarf_Locdesc fbloc; /* Location of Current Frame Base */ | 58 | Dwarf_Locdesc fbloc; /* Location of Current Frame Base */ |
| 51 | const char *var; /* Current variable name */ | 59 | const char *var; /* Current variable name */ |
| 52 | char *buf; /* Current output buffer */ | 60 | char *buf; /* Current output buffer */ |
| 53 | int len; /* Length of output buffer */ | 61 | int len; /* Length of output buffer */ |
| 54 | }; | 62 | }; |
| 55 | #endif /* NO_LIBDWARF */ | 63 | #endif /* NO_LIBDWARF */ |
| 56 | 64 | ||
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/sort.c b/tools/perf/util/sort.c index b490354d1b23..cb0f327de9e8 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -288,3 +288,29 @@ int sort_dimension__add(const char *tok) | |||
| 288 | 288 | ||
| 289 | return -ESRCH; | 289 | return -ESRCH; |
| 290 | } | 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 index 333e664ff45f..753f9ea99fb0 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
| @@ -49,9 +49,13 @@ struct hist_entry { | |||
| 49 | struct symbol *sym; | 49 | struct symbol *sym; |
| 50 | u64 ip; | 50 | u64 ip; |
| 51 | char level; | 51 | char level; |
| 52 | struct symbol *parent; | 52 | struct symbol *parent; |
| 53 | struct callchain_node callchain; | 53 | struct callchain_node callchain; |
| 54 | struct rb_root sorted_chain; | 54 | union { |
| 55 | unsigned long position; | ||
| 56 | struct hist_entry *pair; | ||
| 57 | struct rb_root sorted_chain; | ||
| 58 | }; | ||
| 55 | }; | 59 | }; |
| 56 | 60 | ||
| 57 | enum sort_type { | 61 | enum sort_type { |
| @@ -81,6 +85,8 @@ struct sort_entry { | |||
| 81 | extern struct sort_entry sort_thread; | 85 | extern struct sort_entry sort_thread; |
| 82 | extern struct list_head hist_entry__sort_list; | 86 | extern struct list_head hist_entry__sort_list; |
| 83 | 87 | ||
| 88 | void setup_sorting(const char * const usagestr[], const struct option *opts); | ||
| 89 | |||
| 84 | extern int repsep_fprintf(FILE *fp, const char *fmt, ...); | 90 | extern int repsep_fprintf(FILE *fp, const char *fmt, ...); |
| 85 | extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); | 91 | extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); |
| 86 | extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); | 92 | extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); |
| @@ -95,5 +101,7 @@ extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | |||
| 95 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | 101 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); |
| 96 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | 102 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); |
| 97 | extern int sort_dimension__add(const char *); | 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); | ||
| 98 | 106 | ||
| 99 | #endif /* __PERF_SORT_H */ | 107 | #endif /* __PERF_SORT_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index f24a8cc933d5..5352d7dccc61 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
| @@ -226,3 +226,28 @@ fail: | |||
| 226 | argv_free(argv); | 226 | argv_free(argv); |
| 227 | return NULL; | 227 | return NULL; |
| 228 | } | 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 bfecec265a1a..02ede58c54b4 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
| @@ -1,6 +1,7 @@ | |||
| 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,6 +9,7 @@ char *strxfrchar(char *s, char from, char to); | |||
| 8 | s64 perf_atoll(const char *str); | 9 | s64 perf_atoll(const char *str); |
| 9 | char **argv_split(const char *str, int *argcp); | 10 | char **argv_split(const char *str, int *argcp); |
| 10 | void argv_free(char **argv); | 11 | void argv_free(char **argv); |
| 12 | bool strglobmatch(const char *str, const char *pat); | ||
| 11 | 13 | ||
| 12 | #define _STR(x) #x | 14 | #define _STR(x) #x |
| 13 | #define STR(x) _STR(x) | 15 | #define STR(x) _STR(x) |
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 cb4659306d7b..3ba839007d2c 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.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 /* __PERF_STRLIST_H */ | 78 | #endif /* __PERF_STRLIST_H */ |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index fffcb937cdcb..ab92763edb03 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -1,5 +1,7 @@ | |||
| 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" |
| 5 | #include "thread.h" | 7 | #include "thread.h" |
| @@ -29,33 +31,50 @@ enum dso_origin { | |||
| 29 | }; | 31 | }; |
| 30 | 32 | ||
| 31 | static void dsos__add(struct list_head *head, struct dso *dso); | 33 | static void dsos__add(struct list_head *head, struct dso *dso); |
| 32 | static struct map *thread__find_map_by_name(struct thread *self, char *name); | ||
| 33 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 34 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
| 34 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); | ||
| 35 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 35 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
| 36 | struct thread *thread, symbol_filter_t filter); | 36 | struct perf_session *session, symbol_filter_t filter); |
| 37 | unsigned int symbol__priv_size; | ||
| 38 | static int vmlinux_path__nr_entries; | 37 | static int vmlinux_path__nr_entries; |
| 39 | static char **vmlinux_path; | 38 | static char **vmlinux_path; |
| 40 | 39 | ||
| 41 | static struct symbol_conf symbol_conf__defaults = { | 40 | struct symbol_conf symbol_conf = { |
| 41 | .exclude_other = true, | ||
| 42 | .use_modules = true, | 42 | .use_modules = true, |
| 43 | .try_vmlinux_path = true, | 43 | .try_vmlinux_path = true, |
| 44 | }; | 44 | }; |
| 45 | 45 | ||
| 46 | static struct thread kthread_mem; | ||
| 47 | struct thread *kthread = &kthread_mem; | ||
| 48 | |||
| 49 | bool dso__loaded(const struct dso *self, enum map_type type) | 46 | bool dso__loaded(const struct dso *self, enum map_type type) |
| 50 | { | 47 | { |
| 51 | return self->loaded & (1 << type); | 48 | return self->loaded & (1 << type); |
| 52 | } | 49 | } |
| 53 | 50 | ||
| 51 | bool dso__sorted_by_name(const struct dso *self, enum map_type type) | ||
| 52 | { | ||
| 53 | return self->sorted_by_name & (1 << type); | ||
| 54 | } | ||
| 55 | |||
| 54 | static void dso__set_loaded(struct dso *self, enum map_type type) | 56 | static void dso__set_loaded(struct dso *self, enum map_type type) |
| 55 | { | 57 | { |
| 56 | self->loaded |= (1 << type); | 58 | self->loaded |= (1 << type); |
| 57 | } | 59 | } |
| 58 | 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 | |||
| 59 | static void symbols__fixup_end(struct rb_root *self) | 78 | static void symbols__fixup_end(struct rb_root *self) |
| 60 | { | 79 | { |
| 61 | struct rb_node *nd, *prevnd = rb_first(self); | 80 | struct rb_node *nd, *prevnd = rb_first(self); |
| @@ -79,7 +98,7 @@ static void symbols__fixup_end(struct rb_root *self) | |||
| 79 | curr->end = roundup(curr->start, 4096); | 98 | curr->end = roundup(curr->start, 4096); |
| 80 | } | 99 | } |
| 81 | 100 | ||
| 82 | static void __thread__fixup_maps_end(struct thread *self, enum map_type type) | 101 | static void __map_groups__fixup_end(struct map_groups *self, enum map_type type) |
| 83 | { | 102 | { |
| 84 | struct map *prev, *curr; | 103 | struct map *prev, *curr; |
| 85 | struct rb_node *nd, *prevnd = rb_first(&self->maps[type]); | 104 | struct rb_node *nd, *prevnd = rb_first(&self->maps[type]); |
| @@ -102,23 +121,23 @@ static void __thread__fixup_maps_end(struct thread *self, enum map_type type) | |||
| 102 | curr->end = ~0UL; | 121 | curr->end = ~0UL; |
| 103 | } | 122 | } |
| 104 | 123 | ||
| 105 | static void thread__fixup_maps_end(struct thread *self) | 124 | static void map_groups__fixup_end(struct map_groups *self) |
| 106 | { | 125 | { |
| 107 | int i; | 126 | int i; |
| 108 | for (i = 0; i < MAP__NR_TYPES; ++i) | 127 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 109 | __thread__fixup_maps_end(self, i); | 128 | __map_groups__fixup_end(self, i); |
| 110 | } | 129 | } |
| 111 | 130 | ||
| 112 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) | 131 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) |
| 113 | { | 132 | { |
| 114 | size_t namelen = strlen(name) + 1; | 133 | size_t namelen = strlen(name) + 1; |
| 115 | struct symbol *self = zalloc(symbol__priv_size + | 134 | struct symbol *self = zalloc(symbol_conf.priv_size + |
| 116 | sizeof(*self) + namelen); | 135 | sizeof(*self) + namelen); |
| 117 | if (self == NULL) | 136 | if (self == NULL) |
| 118 | return NULL; | 137 | return NULL; |
| 119 | 138 | ||
| 120 | if (symbol__priv_size) | 139 | if (symbol_conf.priv_size) |
| 121 | self = ((void *)self) + symbol__priv_size; | 140 | self = ((void *)self) + symbol_conf.priv_size; |
| 122 | 141 | ||
| 123 | self->start = start; | 142 | self->start = start; |
| 124 | self->end = len ? start + len - 1 : start; | 143 | self->end = len ? start + len - 1 : start; |
| @@ -132,7 +151,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) | |||
| 132 | 151 | ||
| 133 | static void symbol__delete(struct symbol *self) | 152 | static void symbol__delete(struct symbol *self) |
| 134 | { | 153 | { |
| 135 | free(((void *)self) - symbol__priv_size); | 154 | free(((void *)self) - symbol_conf.priv_size); |
| 136 | } | 155 | } |
| 137 | 156 | ||
| 138 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | 157 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) |
| @@ -164,11 +183,11 @@ struct dso *dso__new(const char *name) | |||
| 164 | dso__set_long_name(self, self->name); | 183 | dso__set_long_name(self, self->name); |
| 165 | self->short_name = self->name; | 184 | self->short_name = self->name; |
| 166 | for (i = 0; i < MAP__NR_TYPES; ++i) | 185 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 167 | self->symbols[i] = RB_ROOT; | 186 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; |
| 168 | self->find_symbol = dso__find_symbol; | ||
| 169 | self->slen_calculated = 0; | 187 | self->slen_calculated = 0; |
| 170 | self->origin = DSO__ORIG_NOT_FOUND; | 188 | self->origin = DSO__ORIG_NOT_FOUND; |
| 171 | self->loaded = 0; | 189 | self->loaded = 0; |
| 190 | self->sorted_by_name = 0; | ||
| 172 | self->has_build_id = 0; | 191 | self->has_build_id = 0; |
| 173 | } | 192 | } |
| 174 | 193 | ||
| @@ -246,11 +265,85 @@ static struct symbol *symbols__find(struct rb_root *self, u64 ip) | |||
| 246 | return NULL; | 265 | return NULL; |
| 247 | } | 266 | } |
| 248 | 267 | ||
| 249 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr) | 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) | ||
| 274 | { | ||
| 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; | ||
| 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 | { | ||
| 293 | struct rb_node *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) | ||
| 250 | { | 330 | { |
| 251 | return symbols__find(&self->symbols[type], addr); | 331 | return symbols__find(&self->symbols[type], addr); |
| 252 | } | 332 | } |
| 253 | 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 | |||
| 254 | int build_id__sprintf(u8 *self, int len, char *bf) | 347 | int build_id__sprintf(u8 *self, int len, char *bf) |
| 255 | { | 348 | { |
| 256 | char *bid = bf; | 349 | char *bid = bf; |
| @@ -327,10 +420,7 @@ static int dso__load_all_kallsyms(struct dso *self, struct map *map) | |||
| 327 | continue; | 420 | continue; |
| 328 | 421 | ||
| 329 | symbol_type = toupper(line[len]); | 422 | symbol_type = toupper(line[len]); |
| 330 | /* | 423 | if (!symbol_type__is_a(symbol_type, map->type)) |
| 331 | * We're interested only in code ('T'ext) | ||
| 332 | */ | ||
| 333 | if (symbol_type != 'T' && symbol_type != 'W') | ||
| 334 | continue; | 424 | continue; |
| 335 | 425 | ||
| 336 | symbol_name = line + len + 2; | 426 | symbol_name = line + len + 2; |
| @@ -364,8 +454,8 @@ out_failure: | |||
| 364 | * kernel range is broken in several maps, named [kernel].N, as we don't have | 454 | * kernel range is broken in several maps, named [kernel].N, as we don't have |
| 365 | * the original ELF section names vmlinux have. | 455 | * the original ELF section names vmlinux have. |
| 366 | */ | 456 | */ |
| 367 | static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread *thread, | 457 | static int dso__split_kallsyms(struct dso *self, struct map *map, |
| 368 | symbol_filter_t filter) | 458 | struct perf_session *session, symbol_filter_t filter) |
| 369 | { | 459 | { |
| 370 | struct map *curr_map = map; | 460 | struct map *curr_map = map; |
| 371 | struct symbol *pos; | 461 | struct symbol *pos; |
| @@ -382,13 +472,13 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread | |||
| 382 | 472 | ||
| 383 | module = strchr(pos->name, '\t'); | 473 | module = strchr(pos->name, '\t'); |
| 384 | if (module) { | 474 | if (module) { |
| 385 | if (!thread->use_modules) | 475 | if (!symbol_conf.use_modules) |
| 386 | goto discard_symbol; | 476 | goto discard_symbol; |
| 387 | 477 | ||
| 388 | *module++ = '\0'; | 478 | *module++ = '\0'; |
| 389 | 479 | ||
| 390 | if (strcmp(self->name, module)) { | 480 | if (strcmp(self->name, module)) { |
| 391 | curr_map = thread__find_map_by_name(thread, module); | 481 | curr_map = map_groups__find_by_name(&session->kmaps, map->type, module); |
| 392 | if (curr_map == NULL) { | 482 | if (curr_map == NULL) { |
| 393 | pr_debug("/proc/{kallsyms,modules} " | 483 | pr_debug("/proc/{kallsyms,modules} " |
| 394 | "inconsistency!\n"); | 484 | "inconsistency!\n"); |
| @@ -419,7 +509,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread | |||
| 419 | } | 509 | } |
| 420 | 510 | ||
| 421 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; | 511 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; |
| 422 | __thread__insert_map(thread, curr_map); | 512 | map_groups__insert(&session->kmaps, curr_map); |
| 423 | ++kernel_range; | 513 | ++kernel_range; |
| 424 | } | 514 | } |
| 425 | 515 | ||
| @@ -440,7 +530,7 @@ discard_symbol: rb_erase(&pos->rb_node, root); | |||
| 440 | 530 | ||
| 441 | 531 | ||
| 442 | static int dso__load_kallsyms(struct dso *self, struct map *map, | 532 | static int dso__load_kallsyms(struct dso *self, struct map *map, |
| 443 | struct thread *thread, symbol_filter_t filter) | 533 | struct perf_session *session, symbol_filter_t filter) |
| 444 | { | 534 | { |
| 445 | if (dso__load_all_kallsyms(self, map) < 0) | 535 | if (dso__load_all_kallsyms(self, map) < 0) |
| 446 | return -1; | 536 | return -1; |
| @@ -448,14 +538,7 @@ static int dso__load_kallsyms(struct dso *self, struct map *map, | |||
| 448 | symbols__fixup_end(&self->symbols[map->type]); | 538 | symbols__fixup_end(&self->symbols[map->type]); |
| 449 | self->origin = DSO__ORIG_KERNEL; | 539 | self->origin = DSO__ORIG_KERNEL; |
| 450 | 540 | ||
| 451 | return dso__split_kallsyms(self, map, thread, filter); | 541 | return dso__split_kallsyms(self, map, session, filter); |
| 452 | } | ||
| 453 | |||
| 454 | size_t kernel_maps__fprintf(FILE *fp) | ||
| 455 | { | ||
| 456 | size_t printed = fprintf(fp, "Kernel maps:\n"); | ||
| 457 | printed += thread__fprintf_maps(kthread, fp); | ||
| 458 | return printed + fprintf(fp, "END kernel maps\n"); | ||
| 459 | } | 542 | } |
| 460 | 543 | ||
| 461 | static int dso__load_perf_map(struct dso *self, struct map *map, | 544 | static int dso__load_perf_map(struct dso *self, struct map *map, |
| @@ -544,6 +627,13 @@ static inline int elf_sym__is_function(const GElf_Sym *sym) | |||
| 544 | sym->st_shndx != SHN_UNDEF; | 627 | sym->st_shndx != SHN_UNDEF; |
| 545 | } | 628 | } |
| 546 | 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 | |||
| 547 | static inline int elf_sym__is_label(const GElf_Sym *sym) | 637 | static inline int elf_sym__is_label(const GElf_Sym *sym) |
| 548 | { | 638 | { |
| 549 | return elf_sym__type(sym) == STT_NOTYPE && | 639 | return elf_sym__type(sym) == STT_NOTYPE && |
| @@ -564,6 +654,12 @@ static inline int elf_sec__is_text(const GElf_Shdr *shdr, | |||
| 564 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | 654 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; |
| 565 | } | 655 | } |
| 566 | 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 | |||
| 567 | static inline const char *elf_sym__name(const GElf_Sym *sym, | 663 | static inline const char *elf_sym__name(const GElf_Sym *sym, |
| 568 | const Elf_Data *symstrs) | 664 | const Elf_Data *symstrs) |
| 569 | { | 665 | { |
| @@ -744,8 +840,32 @@ out: | |||
| 744 | return 0; | 840 | return 0; |
| 745 | } | 841 | } |
| 746 | 842 | ||
| 843 | static bool elf_sym__is_a(GElf_Sym *self, enum map_type type) | ||
| 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 | |||
| 747 | static int dso__load_sym(struct dso *self, struct map *map, | 867 | static int dso__load_sym(struct dso *self, struct map *map, |
| 748 | struct thread *thread, const char *name, int fd, | 868 | struct perf_session *session, const char *name, int fd, |
| 749 | symbol_filter_t filter, int kernel, int kmodule) | 869 | symbol_filter_t filter, int kernel, int kmodule) |
| 750 | { | 870 | { |
| 751 | struct map *curr_map = map; | 871 | struct map *curr_map = map; |
| @@ -818,7 +938,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
| 818 | int is_label = elf_sym__is_label(&sym); | 938 | int is_label = elf_sym__is_label(&sym); |
| 819 | const char *section_name; | 939 | const char *section_name; |
| 820 | 940 | ||
| 821 | if (!is_label && !elf_sym__is_function(&sym)) | 941 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
| 822 | continue; | 942 | continue; |
| 823 | 943 | ||
| 824 | sec = elf_getscn(elf, sym.st_shndx); | 944 | sec = elf_getscn(elf, sym.st_shndx); |
| @@ -827,7 +947,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
| 827 | 947 | ||
| 828 | gelf_getshdr(sec, &shdr); | 948 | gelf_getshdr(sec, &shdr); |
| 829 | 949 | ||
| 830 | if (is_label && !elf_sec__is_text(&shdr, secstrs)) | 950 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) |
| 831 | continue; | 951 | continue; |
| 832 | 952 | ||
| 833 | elf_name = elf_sym__name(&sym, symstrs); | 953 | elf_name = elf_sym__name(&sym, symstrs); |
| @@ -849,7 +969,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
| 849 | snprintf(dso_name, sizeof(dso_name), | 969 | snprintf(dso_name, sizeof(dso_name), |
| 850 | "%s%s", self->short_name, section_name); | 970 | "%s%s", self->short_name, section_name); |
| 851 | 971 | ||
| 852 | curr_map = thread__find_map_by_name(thread, dso_name); | 972 | curr_map = map_groups__find_by_name(&session->kmaps, map->type, dso_name); |
| 853 | if (curr_map == NULL) { | 973 | if (curr_map == NULL) { |
| 854 | u64 start = sym.st_value; | 974 | u64 start = sym.st_value; |
| 855 | 975 | ||
| @@ -868,7 +988,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
| 868 | curr_map->map_ip = identity__map_ip; | 988 | curr_map->map_ip = identity__map_ip; |
| 869 | curr_map->unmap_ip = identity__map_ip; | 989 | curr_map->unmap_ip = identity__map_ip; |
| 870 | curr_dso->origin = DSO__ORIG_KERNEL; | 990 | curr_dso->origin = DSO__ORIG_KERNEL; |
| 871 | __thread__insert_map(kthread, curr_map); | 991 | map_groups__insert(&session->kmaps, curr_map); |
| 872 | dsos__add(&dsos__kernel, curr_dso); | 992 | dsos__add(&dsos__kernel, curr_dso); |
| 873 | } else | 993 | } else |
| 874 | curr_dso = curr_map->dso; | 994 | curr_dso = curr_map->dso; |
| @@ -938,8 +1058,9 @@ static bool __dsos__read_build_ids(struct list_head *head) | |||
| 938 | 1058 | ||
| 939 | bool dsos__read_build_ids(void) | 1059 | bool dsos__read_build_ids(void) |
| 940 | { | 1060 | { |
| 941 | return __dsos__read_build_ids(&dsos__kernel) || | 1061 | bool kbuildids = __dsos__read_build_ids(&dsos__kernel), |
| 942 | __dsos__read_build_ids(&dsos__user); | 1062 | ubuildids = __dsos__read_build_ids(&dsos__user); |
| 1063 | return kbuildids || ubuildids; | ||
| 943 | } | 1064 | } |
| 944 | 1065 | ||
| 945 | /* | 1066 | /* |
| @@ -1082,7 +1203,8 @@ char dso__symtab_origin(const struct dso *self) | |||
| 1082 | return origin[self->origin]; | 1203 | return origin[self->origin]; |
| 1083 | } | 1204 | } |
| 1084 | 1205 | ||
| 1085 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | 1206 | int dso__load(struct dso *self, struct map *map, struct perf_session *session, |
| 1207 | symbol_filter_t filter) | ||
| 1086 | { | 1208 | { |
| 1087 | int size = PATH_MAX; | 1209 | int size = PATH_MAX; |
| 1088 | char *name; | 1210 | char *name; |
| @@ -1093,7 +1215,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
| 1093 | dso__set_loaded(self, map->type); | 1215 | dso__set_loaded(self, map->type); |
| 1094 | 1216 | ||
| 1095 | if (self->kernel) | 1217 | if (self->kernel) |
| 1096 | return dso__load_kernel_sym(self, map, kthread, filter); | 1218 | return dso__load_kernel_sym(self, map, session, filter); |
| 1097 | 1219 | ||
| 1098 | name = malloc(size); | 1220 | name = malloc(size); |
| 1099 | if (!name) | 1221 | if (!name) |
| @@ -1179,11 +1301,12 @@ out: | |||
| 1179 | return ret; | 1301 | return ret; |
| 1180 | } | 1302 | } |
| 1181 | 1303 | ||
| 1182 | static struct map *thread__find_map_by_name(struct thread *self, char *name) | 1304 | struct map *map_groups__find_by_name(struct map_groups *self, |
| 1305 | enum map_type type, const char *name) | ||
| 1183 | { | 1306 | { |
| 1184 | struct rb_node *nd; | 1307 | struct rb_node *nd; |
| 1185 | 1308 | ||
| 1186 | for (nd = rb_first(&self->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { | 1309 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { |
| 1187 | struct map *map = rb_entry(nd, struct map, rb_node); | 1310 | struct map *map = rb_entry(nd, struct map, rb_node); |
| 1188 | 1311 | ||
| 1189 | if (map->dso && strcmp(map->dso->name, name) == 0) | 1312 | if (map->dso && strcmp(map->dso->name, name) == 0) |
| @@ -1193,7 +1316,7 @@ static struct map *thread__find_map_by_name(struct thread *self, char *name) | |||
| 1193 | return NULL; | 1316 | return NULL; |
| 1194 | } | 1317 | } |
| 1195 | 1318 | ||
| 1196 | static int dsos__set_modules_path_dir(char *dirname) | 1319 | static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname) |
| 1197 | { | 1320 | { |
| 1198 | struct dirent *dent; | 1321 | struct dirent *dent; |
| 1199 | DIR *dir = opendir(dirname); | 1322 | DIR *dir = opendir(dirname); |
| @@ -1213,7 +1336,7 @@ static int dsos__set_modules_path_dir(char *dirname) | |||
| 1213 | 1336 | ||
| 1214 | snprintf(path, sizeof(path), "%s/%s", | 1337 | snprintf(path, sizeof(path), "%s/%s", |
| 1215 | dirname, dent->d_name); | 1338 | dirname, dent->d_name); |
| 1216 | if (dsos__set_modules_path_dir(path) < 0) | 1339 | if (perf_session__set_modules_path_dir(self, path) < 0) |
| 1217 | goto failure; | 1340 | goto failure; |
| 1218 | } else { | 1341 | } else { |
| 1219 | char *dot = strrchr(dent->d_name, '.'), | 1342 | char *dot = strrchr(dent->d_name, '.'), |
| @@ -1227,7 +1350,7 @@ static int dsos__set_modules_path_dir(char *dirname) | |||
| 1227 | (int)(dot - dent->d_name), dent->d_name); | 1350 | (int)(dot - dent->d_name), dent->d_name); |
| 1228 | 1351 | ||
| 1229 | strxfrchar(dso_name, '-', '_'); | 1352 | strxfrchar(dso_name, '-', '_'); |
| 1230 | map = thread__find_map_by_name(kthread, dso_name); | 1353 | map = map_groups__find_by_name(&self->kmaps, MAP__FUNCTION, dso_name); |
| 1231 | if (map == NULL) | 1354 | if (map == NULL) |
| 1232 | continue; | 1355 | continue; |
| 1233 | 1356 | ||
| @@ -1247,7 +1370,7 @@ failure: | |||
| 1247 | return -1; | 1370 | return -1; |
| 1248 | } | 1371 | } |
| 1249 | 1372 | ||
| 1250 | static int dsos__set_modules_path(void) | 1373 | static int perf_session__set_modules_path(struct perf_session *self) |
| 1251 | { | 1374 | { |
| 1252 | struct utsname uts; | 1375 | struct utsname uts; |
| 1253 | char modules_path[PATH_MAX]; | 1376 | char modules_path[PATH_MAX]; |
| @@ -1258,7 +1381,7 @@ static int dsos__set_modules_path(void) | |||
| 1258 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", | 1381 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", |
| 1259 | uts.release); | 1382 | uts.release); |
| 1260 | 1383 | ||
| 1261 | return dsos__set_modules_path_dir(modules_path); | 1384 | return perf_session__set_modules_path_dir(self, modules_path); |
| 1262 | } | 1385 | } |
| 1263 | 1386 | ||
| 1264 | /* | 1387 | /* |
| @@ -1280,7 +1403,7 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | |||
| 1280 | return self; | 1403 | return self; |
| 1281 | } | 1404 | } |
| 1282 | 1405 | ||
| 1283 | static int thread__create_module_maps(struct thread *self) | 1406 | static int perf_session__create_module_maps(struct perf_session *self) |
| 1284 | { | 1407 | { |
| 1285 | char *line = NULL; | 1408 | char *line = NULL; |
| 1286 | size_t n; | 1409 | size_t n; |
| @@ -1337,14 +1460,14 @@ static int thread__create_module_maps(struct thread *self) | |||
| 1337 | dso->has_build_id = true; | 1460 | dso->has_build_id = true; |
| 1338 | 1461 | ||
| 1339 | dso->origin = DSO__ORIG_KMODULE; | 1462 | dso->origin = DSO__ORIG_KMODULE; |
| 1340 | __thread__insert_map(self, map); | 1463 | map_groups__insert(&self->kmaps, map); |
| 1341 | dsos__add(&dsos__kernel, dso); | 1464 | dsos__add(&dsos__kernel, dso); |
| 1342 | } | 1465 | } |
| 1343 | 1466 | ||
| 1344 | free(line); | 1467 | free(line); |
| 1345 | fclose(file); | 1468 | fclose(file); |
| 1346 | 1469 | ||
| 1347 | return dsos__set_modules_path(); | 1470 | return perf_session__set_modules_path(self); |
| 1348 | 1471 | ||
| 1349 | out_delete_line: | 1472 | out_delete_line: |
| 1350 | free(line); | 1473 | free(line); |
| @@ -1352,7 +1475,8 @@ out_failure: | |||
| 1352 | return -1; | 1475 | return -1; |
| 1353 | } | 1476 | } |
| 1354 | 1477 | ||
| 1355 | static int dso__load_vmlinux(struct dso *self, struct map *map, struct thread *thread, | 1478 | static int dso__load_vmlinux(struct dso *self, struct map *map, |
| 1479 | struct perf_session *session, | ||
| 1356 | const char *vmlinux, symbol_filter_t filter) | 1480 | const char *vmlinux, symbol_filter_t filter) |
| 1357 | { | 1481 | { |
| 1358 | int err = -1, fd; | 1482 | int err = -1, fd; |
| @@ -1386,14 +1510,14 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, struct thread *t | |||
| 1386 | return -1; | 1510 | return -1; |
| 1387 | 1511 | ||
| 1388 | dso__set_loaded(self, map->type); | 1512 | dso__set_loaded(self, map->type); |
| 1389 | err = dso__load_sym(self, map, thread, self->long_name, fd, filter, 1, 0); | 1513 | err = dso__load_sym(self, map, session, self->long_name, fd, filter, 1, 0); |
| 1390 | close(fd); | 1514 | close(fd); |
| 1391 | 1515 | ||
| 1392 | return err; | 1516 | return err; |
| 1393 | } | 1517 | } |
| 1394 | 1518 | ||
| 1395 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 1519 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
| 1396 | struct thread *thread, symbol_filter_t filter) | 1520 | struct perf_session *session, symbol_filter_t filter) |
| 1397 | { | 1521 | { |
| 1398 | int err; | 1522 | int err; |
| 1399 | bool is_kallsyms; | 1523 | bool is_kallsyms; |
| @@ -1403,7 +1527,7 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, | |||
| 1403 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | 1527 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", |
| 1404 | vmlinux_path__nr_entries); | 1528 | vmlinux_path__nr_entries); |
| 1405 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | 1529 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { |
| 1406 | err = dso__load_vmlinux(self, map, thread, | 1530 | err = dso__load_vmlinux(self, map, session, |
| 1407 | vmlinux_path[i], filter); | 1531 | vmlinux_path[i], filter); |
| 1408 | if (err > 0) { | 1532 | if (err > 0) { |
| 1409 | pr_debug("Using %s for symbols\n", | 1533 | pr_debug("Using %s for symbols\n", |
| @@ -1419,12 +1543,12 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, | |||
| 1419 | if (is_kallsyms) | 1543 | if (is_kallsyms) |
| 1420 | goto do_kallsyms; | 1544 | goto do_kallsyms; |
| 1421 | 1545 | ||
| 1422 | err = dso__load_vmlinux(self, map, thread, self->long_name, filter); | 1546 | err = dso__load_vmlinux(self, map, session, self->long_name, filter); |
| 1423 | if (err <= 0) { | 1547 | if (err <= 0) { |
| 1424 | pr_info("The file %s cannot be used, " | 1548 | pr_info("The file %s cannot be used, " |
| 1425 | "trying to use /proc/kallsyms...", self->long_name); | 1549 | "trying to use /proc/kallsyms...", self->long_name); |
| 1426 | do_kallsyms: | 1550 | do_kallsyms: |
| 1427 | err = dso__load_kallsyms(self, map, thread, filter); | 1551 | err = dso__load_kallsyms(self, map, session, filter); |
| 1428 | if (err > 0 && !is_kallsyms) | 1552 | if (err > 0 && !is_kallsyms) |
| 1429 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); | 1553 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); |
| 1430 | } | 1554 | } |
| @@ -1507,42 +1631,59 @@ size_t dsos__fprintf_buildid(FILE *fp) | |||
| 1507 | __dsos__fprintf_buildid(&dsos__user, fp)); | 1631 | __dsos__fprintf_buildid(&dsos__user, fp)); |
| 1508 | } | 1632 | } |
| 1509 | 1633 | ||
| 1510 | static int thread__create_kernel_map(struct thread *self, const char *vmlinux) | 1634 | static struct dso *dsos__create_kernel( const char *vmlinux) |
| 1511 | { | 1635 | { |
| 1512 | struct map *kmap; | ||
| 1513 | struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]"); | 1636 | struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]"); |
| 1514 | 1637 | ||
| 1515 | if (kernel == NULL) | 1638 | if (kernel == NULL) |
| 1516 | return -1; | 1639 | return NULL; |
| 1517 | |||
| 1518 | kmap = map__new2(0, kernel, MAP__FUNCTION); | ||
| 1519 | if (kmap == NULL) | ||
| 1520 | goto out_delete_kernel_dso; | ||
| 1521 | 1640 | ||
| 1522 | kmap->map_ip = kmap->unmap_ip = identity__map_ip; | ||
| 1523 | kernel->short_name = "[kernel]"; | 1641 | kernel->short_name = "[kernel]"; |
| 1524 | kernel->kernel = 1; | 1642 | kernel->kernel = 1; |
| 1525 | 1643 | ||
| 1526 | vdso = dso__new("[vdso]"); | 1644 | vdso = dso__new("[vdso]"); |
| 1527 | if (vdso == NULL) | 1645 | if (vdso == NULL) |
| 1528 | goto out_delete_kernel_map; | 1646 | goto out_delete_kernel_dso; |
| 1529 | dso__set_loaded(vdso, MAP__FUNCTION); | 1647 | dso__set_loaded(vdso, MAP__FUNCTION); |
| 1530 | 1648 | ||
| 1531 | if (sysfs__read_build_id("/sys/kernel/notes", kernel->build_id, | 1649 | if (sysfs__read_build_id("/sys/kernel/notes", kernel->build_id, |
| 1532 | sizeof(kernel->build_id)) == 0) | 1650 | sizeof(kernel->build_id)) == 0) |
| 1533 | kernel->has_build_id = true; | 1651 | kernel->has_build_id = true; |
| 1534 | 1652 | ||
| 1535 | __thread__insert_map(self, kmap); | ||
| 1536 | dsos__add(&dsos__kernel, kernel); | 1653 | dsos__add(&dsos__kernel, kernel); |
| 1537 | dsos__add(&dsos__user, vdso); | 1654 | dsos__add(&dsos__user, vdso); |
| 1538 | 1655 | ||
| 1539 | return 0; | 1656 | return kernel; |
| 1540 | 1657 | ||
| 1541 | out_delete_kernel_map: | ||
| 1542 | map__delete(kmap); | ||
| 1543 | out_delete_kernel_dso: | 1658 | out_delete_kernel_dso: |
| 1544 | dso__delete(kernel); | 1659 | dso__delete(kernel); |
| 1545 | return -1; | 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) | ||
| 1669 | return -1; | ||
| 1670 | |||
| 1671 | functions = map__new2(0, kernel, MAP__FUNCTION); | ||
| 1672 | if (functions == NULL) | ||
| 1673 | return -1; | ||
| 1674 | |||
| 1675 | variables = map__new2(0, kernel, MAP__VARIABLE); | ||
| 1676 | if (variables == NULL) { | ||
| 1677 | map__delete(functions); | ||
| 1678 | return -1; | ||
| 1679 | } | ||
| 1680 | |||
| 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); | ||
| 1685 | |||
| 1686 | return 0; | ||
| 1546 | } | 1687 | } |
| 1547 | 1688 | ||
| 1548 | static void vmlinux_path__exit(void) | 1689 | static void vmlinux_path__exit(void) |
| @@ -1600,29 +1741,69 @@ out_fail: | |||
| 1600 | return -1; | 1741 | return -1; |
| 1601 | } | 1742 | } |
| 1602 | 1743 | ||
| 1603 | int symbol__init(struct symbol_conf *conf) | 1744 | static int setup_list(struct strlist **list, const char *list_str, |
| 1745 | const char *list_name) | ||
| 1604 | { | 1746 | { |
| 1605 | const struct symbol_conf *pconf = conf ?: &symbol_conf__defaults; | 1747 | if (list_str == NULL) |
| 1748 | return 0; | ||
| 1606 | 1749 | ||
| 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) | ||
| 1759 | { | ||
| 1607 | elf_version(EV_CURRENT); | 1760 | elf_version(EV_CURRENT); |
| 1608 | symbol__priv_size = pconf->priv_size; | 1761 | if (symbol_conf.sort_by_name) |
| 1609 | thread__init(kthread, 0); | 1762 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - |
| 1763 | sizeof(struct symbol)); | ||
| 1610 | 1764 | ||
| 1611 | if (pconf->try_vmlinux_path && vmlinux_path__init() < 0) | 1765 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) |
| 1612 | return -1; | 1766 | return -1; |
| 1613 | 1767 | ||
| 1614 | if (thread__create_kernel_map(kthread, pconf->vmlinux_name) < 0) { | 1768 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { |
| 1615 | vmlinux_path__exit(); | 1769 | pr_err("'.' is the only non valid --field-separator argument\n"); |
| 1616 | return -1; | 1770 | return -1; |
| 1617 | } | 1771 | } |
| 1618 | 1772 | ||
| 1619 | kthread->use_modules = pconf->use_modules; | 1773 | if (setup_list(&symbol_conf.dso_list, |
| 1620 | if (pconf->use_modules && thread__create_module_maps(kthread) < 0) | 1774 | symbol_conf.dso_list_str, "dso") < 0) |
| 1621 | pr_debug("Failed to load list of modules in use, " | 1775 | return -1; |
| 1622 | "continuing...\n"); | 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); | ||
| 1623 | /* | 1804 | /* |
| 1624 | * Now that we have all the maps created, just set the ->end of them: | 1805 | * Now that we have all the maps created, just set the ->end of them: |
| 1625 | */ | 1806 | */ |
| 1626 | thread__fixup_maps_end(kthread); | 1807 | map_groups__fixup_end(&self->kmaps); |
| 1627 | return 0; | 1808 | return 0; |
| 1628 | } | 1809 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 17003efa0b39..8aded2356f79 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -49,18 +49,32 @@ struct symbol { | |||
| 49 | char name[0]; | 49 | char name[0]; |
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| 52 | struct strlist; | ||
| 53 | |||
| 52 | struct symbol_conf { | 54 | struct symbol_conf { |
| 53 | unsigned short priv_size; | 55 | unsigned short priv_size; |
| 54 | bool try_vmlinux_path, | 56 | bool try_vmlinux_path, |
| 55 | use_modules; | 57 | use_modules, |
| 56 | const char *vmlinux_name; | 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; | ||
| 57 | }; | 71 | }; |
| 58 | 72 | ||
| 59 | extern unsigned int symbol__priv_size; | 73 | extern struct symbol_conf symbol_conf; |
| 60 | 74 | ||
| 61 | static inline void *symbol__priv(struct symbol *self) | 75 | static inline void *symbol__priv(struct symbol *self) |
| 62 | { | 76 | { |
| 63 | return ((void *)self) - symbol__priv_size; | 77 | return ((void *)self) - symbol_conf.priv_size; |
| 64 | } | 78 | } |
| 65 | 79 | ||
| 66 | struct addr_location { | 80 | struct addr_location { |
| @@ -69,18 +83,19 @@ struct addr_location { | |||
| 69 | struct symbol *sym; | 83 | struct symbol *sym; |
| 70 | u64 addr; | 84 | u64 addr; |
| 71 | char level; | 85 | char level; |
| 86 | bool filtered; | ||
| 72 | }; | 87 | }; |
| 73 | 88 | ||
| 74 | struct dso { | 89 | struct dso { |
| 75 | struct list_head node; | 90 | struct list_head node; |
| 76 | struct rb_root symbols[MAP__NR_TYPES]; | 91 | struct rb_root symbols[MAP__NR_TYPES]; |
| 77 | struct symbol *(*find_symbol)(struct dso *self, | 92 | struct rb_root symbol_names[MAP__NR_TYPES]; |
| 78 | enum map_type type, u64 addr); | ||
| 79 | u8 adjust_symbols:1; | 93 | u8 adjust_symbols:1; |
| 80 | u8 slen_calculated:1; | 94 | u8 slen_calculated:1; |
| 81 | u8 has_build_id:1; | 95 | u8 has_build_id:1; |
| 82 | u8 kernel:1; | 96 | u8 kernel:1; |
| 83 | unsigned char origin; | 97 | unsigned char origin; |
| 98 | u8 sorted_by_name; | ||
| 84 | u8 loaded; | 99 | u8 loaded; |
| 85 | u8 build_id[BUILD_ID_SIZE]; | 100 | u8 build_id[BUILD_ID_SIZE]; |
| 86 | u16 long_name_len; | 101 | u16 long_name_len; |
| @@ -93,9 +108,15 @@ struct dso *dso__new(const char *name); | |||
| 93 | void dso__delete(struct dso *self); | 108 | void dso__delete(struct dso *self); |
| 94 | 109 | ||
| 95 | bool dso__loaded(const struct dso *self, enum map_type type); | 110 | bool dso__loaded(const struct dso *self, enum map_type type); |
| 111 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); | ||
| 112 | |||
| 113 | void dso__sort_by_name(struct dso *self, enum map_type type); | ||
| 114 | |||
| 115 | struct perf_session; | ||
| 96 | 116 | ||
| 97 | struct dso *dsos__findnew(const char *name); | 117 | struct dso *dsos__findnew(const char *name); |
| 98 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); | 118 | int dso__load(struct dso *self, struct map *map, struct perf_session *session, |
| 119 | symbol_filter_t filter); | ||
| 99 | void dsos__fprintf(FILE *fp); | 120 | void dsos__fprintf(FILE *fp); |
| 100 | size_t dsos__fprintf_buildid(FILE *fp); | 121 | size_t dsos__fprintf_buildid(FILE *fp); |
| 101 | 122 | ||
| @@ -103,18 +124,18 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp); | |||
| 103 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); | 124 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); |
| 104 | char dso__symtab_origin(const struct dso *self); | 125 | char dso__symtab_origin(const struct dso *self); |
| 105 | void dso__set_build_id(struct dso *self, void *build_id); | 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); | ||
| 106 | 130 | ||
| 107 | int filename__read_build_id(const char *filename, void *bf, size_t size); | 131 | int filename__read_build_id(const char *filename, void *bf, size_t size); |
| 108 | int sysfs__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); |
| 109 | bool dsos__read_build_ids(void); | 133 | bool dsos__read_build_ids(void); |
| 110 | int build_id__sprintf(u8 *self, int len, char *bf); | 134 | int build_id__sprintf(u8 *self, int len, char *bf); |
| 111 | 135 | ||
| 112 | size_t kernel_maps__fprintf(FILE *fp); | 136 | int symbol__init(void); |
| 113 | 137 | int perf_session__create_kernel_maps(struct perf_session *self); | |
| 114 | int symbol__init(struct symbol_conf *conf); | ||
| 115 | 138 | ||
| 116 | struct thread; | ||
| 117 | struct thread *kthread; | ||
| 118 | extern struct list_head dsos__user, dsos__kernel; | 139 | extern struct list_head dsos__user, dsos__kernel; |
| 119 | extern struct dso *vdso; | 140 | extern struct dso *vdso; |
| 120 | #endif /* __PERF_SYMBOL */ | 141 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 603f5610861b..4a08dcf50b68 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -2,18 +2,14 @@ | |||
| 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 | ||
| 9 | static struct rb_root threads; | 10 | void map_groups__init(struct map_groups *self) |
| 10 | static struct thread *last_match; | ||
| 11 | |||
| 12 | void thread__init(struct thread *self, pid_t pid) | ||
| 13 | { | 11 | { |
| 14 | int i; | 12 | int i; |
| 15 | self->pid = pid; | ||
| 16 | self->comm = NULL; | ||
| 17 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 13 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
| 18 | self->maps[i] = RB_ROOT; | 14 | self->maps[i] = RB_ROOT; |
| 19 | INIT_LIST_HEAD(&self->removed_maps[i]); | 15 | INIT_LIST_HEAD(&self->removed_maps[i]); |
| @@ -25,7 +21,8 @@ static struct thread *thread__new(pid_t pid) | |||
| 25 | struct thread *self = zalloc(sizeof(*self)); | 21 | struct thread *self = zalloc(sizeof(*self)); |
| 26 | 22 | ||
| 27 | if (self != NULL) { | 23 | if (self != NULL) { |
| 28 | thread__init(self, pid); | 24 | map_groups__init(&self->mg); |
| 25 | self->pid = pid; | ||
| 29 | self->comm = malloc(32); | 26 | self->comm = malloc(32); |
| 30 | if (self->comm) | 27 | if (self->comm) |
| 31 | snprintf(self->comm, 32, ":%d", self->pid); | 28 | snprintf(self->comm, 32, ":%d", self->pid); |
| @@ -55,10 +52,11 @@ int thread__comm_len(struct thread *self) | |||
| 55 | 52 | ||
| 56 | static const char *map_type__name[MAP__NR_TYPES] = { | 53 | static const char *map_type__name[MAP__NR_TYPES] = { |
| 57 | [MAP__FUNCTION] = "Functions", | 54 | [MAP__FUNCTION] = "Functions", |
| 55 | [MAP__VARIABLE] = "Variables", | ||
| 58 | }; | 56 | }; |
| 59 | 57 | ||
| 60 | static size_t __thread__fprintf_maps(struct thread *self, | 58 | static size_t __map_groups__fprintf_maps(struct map_groups *self, |
| 61 | enum map_type type, FILE *fp) | 59 | enum map_type type, FILE *fp) |
| 62 | { | 60 | { |
| 63 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); | 61 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); |
| 64 | struct rb_node *nd; | 62 | struct rb_node *nd; |
| @@ -76,16 +74,16 @@ static size_t __thread__fprintf_maps(struct thread *self, | |||
| 76 | return printed; | 74 | return printed; |
| 77 | } | 75 | } |
| 78 | 76 | ||
| 79 | size_t thread__fprintf_maps(struct thread *self, FILE *fp) | 77 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp) |
| 80 | { | 78 | { |
| 81 | size_t printed = 0, i; | 79 | size_t printed = 0, i; |
| 82 | for (i = 0; i < MAP__NR_TYPES; ++i) | 80 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 83 | printed += __thread__fprintf_maps(self, i, fp); | 81 | printed += __map_groups__fprintf_maps(self, i, fp); |
| 84 | return printed; | 82 | return printed; |
| 85 | } | 83 | } |
| 86 | 84 | ||
| 87 | static size_t __thread__fprintf_removed_maps(struct thread *self, | 85 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, |
| 88 | enum map_type type, FILE *fp) | 86 | enum map_type type, FILE *fp) |
| 89 | { | 87 | { |
| 90 | struct map *pos; | 88 | struct map *pos; |
| 91 | size_t printed = 0; | 89 | size_t printed = 0; |
| @@ -101,25 +99,30 @@ static size_t __thread__fprintf_removed_maps(struct thread *self, | |||
| 101 | return printed; | 99 | return printed; |
| 102 | } | 100 | } |
| 103 | 101 | ||
| 104 | static size_t thread__fprintf_removed_maps(struct thread *self, FILE *fp) | 102 | static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp) |
| 105 | { | 103 | { |
| 106 | size_t printed = 0, i; | 104 | size_t printed = 0, i; |
| 107 | for (i = 0; i < MAP__NR_TYPES; ++i) | 105 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 108 | printed += __thread__fprintf_removed_maps(self, i, fp); | 106 | printed += __map_groups__fprintf_removed_maps(self, i, fp); |
| 109 | return printed; | 107 | return printed; |
| 110 | } | 108 | } |
| 111 | 109 | ||
| 112 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 110 | static size_t map_groups__fprintf(struct map_groups *self, FILE *fp) |
| 113 | { | 111 | { |
| 114 | size_t printed = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); | 112 | size_t printed = map_groups__fprintf_maps(self, fp); |
| 115 | printed += thread__fprintf_removed_maps(self, fp); | ||
| 116 | printed += fprintf(fp, "Removed maps:\n"); | 113 | printed += fprintf(fp, "Removed maps:\n"); |
| 117 | return printed + thread__fprintf_removed_maps(self, fp); | 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); | ||
| 118 | } | 121 | } |
| 119 | 122 | ||
| 120 | struct thread *threads__findnew(pid_t pid) | 123 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) |
| 121 | { | 124 | { |
| 122 | struct rb_node **p = &threads.rb_node; | 125 | struct rb_node **p = &self->threads.rb_node; |
| 123 | struct rb_node *parent = NULL; | 126 | struct rb_node *parent = NULL; |
| 124 | struct thread *th; | 127 | struct thread *th; |
| 125 | 128 | ||
| @@ -128,15 +131,15 @@ struct thread *threads__findnew(pid_t pid) | |||
| 128 | * so most of the time we dont have to look up | 131 | * so most of the time we dont have to look up |
| 129 | * the full rbtree: | 132 | * the full rbtree: |
| 130 | */ | 133 | */ |
| 131 | if (last_match && last_match->pid == pid) | 134 | if (self->last_match && self->last_match->pid == pid) |
| 132 | return last_match; | 135 | return self->last_match; |
| 133 | 136 | ||
| 134 | while (*p != NULL) { | 137 | while (*p != NULL) { |
| 135 | parent = *p; | 138 | parent = *p; |
| 136 | th = rb_entry(parent, struct thread, rb_node); | 139 | th = rb_entry(parent, struct thread, rb_node); |
| 137 | 140 | ||
| 138 | if (th->pid == pid) { | 141 | if (th->pid == pid) { |
| 139 | last_match = th; | 142 | self->last_match = th; |
| 140 | return th; | 143 | return th; |
| 141 | } | 144 | } |
| 142 | 145 | ||
| @@ -149,26 +152,15 @@ struct thread *threads__findnew(pid_t pid) | |||
| 149 | th = thread__new(pid); | 152 | th = thread__new(pid); |
| 150 | if (th != NULL) { | 153 | if (th != NULL) { |
| 151 | rb_link_node(&th->rb_node, parent, p); | 154 | rb_link_node(&th->rb_node, parent, p); |
| 152 | rb_insert_color(&th->rb_node, &threads); | 155 | rb_insert_color(&th->rb_node, &self->threads); |
| 153 | last_match = th; | 156 | self->last_match = th; |
| 154 | } | 157 | } |
| 155 | 158 | ||
| 156 | return th; | 159 | return th; |
| 157 | } | 160 | } |
| 158 | 161 | ||
| 159 | struct thread *register_idle_thread(void) | 162 | static void map_groups__remove_overlappings(struct map_groups *self, |
| 160 | { | 163 | struct map *map) |
| 161 | struct thread *thread = threads__findnew(0); | ||
| 162 | |||
| 163 | if (!thread || thread__set_comm(thread, "swapper")) { | ||
| 164 | fprintf(stderr, "problem inserting idle task.\n"); | ||
| 165 | exit(-1); | ||
| 166 | } | ||
| 167 | |||
| 168 | return thread; | ||
| 169 | } | ||
| 170 | |||
| 171 | static void thread__remove_overlappings(struct thread *self, struct map *map) | ||
| 172 | { | 164 | { |
| 173 | struct rb_root *root = &self->maps[map->type]; | 165 | struct rb_root *root = &self->maps[map->type]; |
| 174 | struct rb_node *next = rb_first(root); | 166 | struct rb_node *next = rb_first(root); |
| @@ -238,12 +230,15 @@ struct map *maps__find(struct rb_root *maps, u64 ip) | |||
| 238 | 230 | ||
| 239 | void thread__insert_map(struct thread *self, struct map *map) | 231 | void thread__insert_map(struct thread *self, struct map *map) |
| 240 | { | 232 | { |
| 241 | thread__remove_overlappings(self, map); | 233 | map_groups__remove_overlappings(&self->mg, map); |
| 242 | maps__insert(&self->maps[map->type], map); | 234 | map_groups__insert(&self->mg, map); |
| 243 | } | 235 | } |
| 244 | 236 | ||
| 245 | static int thread__clone_maps(struct thread *self, struct thread *parent, | 237 | /* |
| 246 | enum map_type type) | 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) | ||
| 247 | { | 242 | { |
| 248 | struct rb_node *nd; | 243 | struct rb_node *nd; |
| 249 | for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { | 244 | for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { |
| @@ -251,7 +246,7 @@ static int thread__clone_maps(struct thread *self, struct thread *parent, | |||
| 251 | struct map *new = map__clone(map); | 246 | struct map *new = map__clone(map); |
| 252 | if (new == NULL) | 247 | if (new == NULL) |
| 253 | return -ENOMEM; | 248 | return -ENOMEM; |
| 254 | thread__insert_map(self, new); | 249 | map_groups__insert(self, new); |
| 255 | } | 250 | } |
| 256 | return 0; | 251 | return 0; |
| 257 | } | 252 | } |
| @@ -267,17 +262,17 @@ int thread__fork(struct thread *self, struct thread *parent) | |||
| 267 | return -ENOMEM; | 262 | return -ENOMEM; |
| 268 | 263 | ||
| 269 | for (i = 0; i < MAP__NR_TYPES; ++i) | 264 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 270 | if (thread__clone_maps(self, parent, i) < 0) | 265 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) |
| 271 | return -ENOMEM; | 266 | return -ENOMEM; |
| 272 | return 0; | 267 | return 0; |
| 273 | } | 268 | } |
| 274 | 269 | ||
| 275 | size_t threads__fprintf(FILE *fp) | 270 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp) |
| 276 | { | 271 | { |
| 277 | size_t ret = 0; | 272 | size_t ret = 0; |
| 278 | struct rb_node *nd; | 273 | struct rb_node *nd; |
| 279 | 274 | ||
| 280 | for (nd = rb_first(&threads); nd; nd = rb_next(nd)) { | 275 | for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) { |
| 281 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | 276 | struct thread *pos = rb_entry(nd, struct thread, rb_node); |
| 282 | 277 | ||
| 283 | ret += thread__fprintf(pos, fp); | 278 | ret += thread__fprintf(pos, fp); |
| @@ -286,14 +281,15 @@ size_t threads__fprintf(FILE *fp) | |||
| 286 | return ret; | 281 | return ret; |
| 287 | } | 282 | } |
| 288 | 283 | ||
| 289 | struct symbol *thread__find_symbol(struct thread *self, | 284 | struct symbol *map_groups__find_symbol(struct map_groups *self, |
| 290 | enum map_type type, u64 addr, | 285 | struct perf_session *session, |
| 291 | symbol_filter_t filter) | 286 | enum map_type type, u64 addr, |
| 287 | symbol_filter_t filter) | ||
| 292 | { | 288 | { |
| 293 | struct map *map = thread__find_map(self, type, addr); | 289 | struct map *map = map_groups__find(self, type, addr); |
| 294 | 290 | ||
| 295 | if (map != NULL) | 291 | if (map != NULL) |
| 296 | return map__find_symbol(map, map->map_ip(map, addr), filter); | 292 | return map__find_symbol(map, session, map->map_ip(map, addr), filter); |
| 297 | 293 | ||
| 298 | return NULL; | 294 | return NULL; |
| 299 | } | 295 | } |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 686d6e914d9e..c206f72c8881 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -5,52 +5,66 @@ | |||
| 5 | #include <unistd.h> | 5 | #include <unistd.h> |
| 6 | #include "symbol.h" | 6 | #include "symbol.h" |
| 7 | 7 | ||
| 8 | struct thread { | 8 | struct map_groups { |
| 9 | struct rb_node rb_node; | ||
| 10 | struct rb_root maps[MAP__NR_TYPES]; | 9 | struct rb_root maps[MAP__NR_TYPES]; |
| 11 | struct list_head removed_maps[MAP__NR_TYPES]; | 10 | struct list_head removed_maps[MAP__NR_TYPES]; |
| 11 | }; | ||
| 12 | |||
| 13 | struct thread { | ||
| 14 | struct rb_node rb_node; | ||
| 15 | struct map_groups mg; | ||
| 12 | pid_t pid; | 16 | pid_t pid; |
| 13 | bool use_modules; | ||
| 14 | char shortname[3]; | 17 | char shortname[3]; |
| 15 | char *comm; | 18 | char *comm; |
| 16 | int comm_len; | 19 | int comm_len; |
| 17 | }; | 20 | }; |
| 18 | 21 | ||
| 19 | void thread__init(struct thread *self, pid_t pid); | 22 | void map_groups__init(struct map_groups *self); |
| 20 | int thread__set_comm(struct thread *self, const char *comm); | 23 | int thread__set_comm(struct thread *self, const char *comm); |
| 21 | int thread__comm_len(struct thread *self); | 24 | int thread__comm_len(struct thread *self); |
| 22 | struct thread *threads__findnew(pid_t pid); | 25 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
| 23 | struct thread *register_idle_thread(void); | ||
| 24 | void thread__insert_map(struct thread *self, struct map *map); | 26 | void thread__insert_map(struct thread *self, struct map *map); |
| 25 | int thread__fork(struct thread *self, struct thread *parent); | 27 | int thread__fork(struct thread *self, struct thread *parent); |
| 26 | size_t thread__fprintf_maps(struct thread *self, FILE *fp); | 28 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); |
| 27 | size_t threads__fprintf(FILE *fp); | 29 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); |
| 28 | 30 | ||
| 29 | void maps__insert(struct rb_root *maps, struct map *map); | 31 | void maps__insert(struct rb_root *maps, struct map *map); |
| 30 | struct map *maps__find(struct rb_root *maps, u64 addr); | 32 | struct map *maps__find(struct rb_root *maps, u64 addr); |
| 31 | 33 | ||
| 32 | static inline struct map *thread__find_map(struct thread *self, | 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, | ||
| 33 | enum map_type type, u64 addr) | 40 | enum map_type type, u64 addr) |
| 34 | { | 41 | { |
| 35 | return self ? maps__find(&self->maps[type], addr) : NULL; | 42 | return maps__find(&self->maps[type], addr); |
| 36 | } | 43 | } |
| 37 | 44 | ||
| 38 | static inline void __thread__insert_map(struct thread *self, struct map *map) | 45 | static inline struct map *thread__find_map(struct thread *self, |
| 46 | enum map_type type, u64 addr) | ||
| 39 | { | 47 | { |
| 40 | maps__insert(&self->maps[map->type], map); | 48 | return self ? map_groups__find(&self->mg, type, addr) : NULL; |
| 41 | } | 49 | } |
| 42 | 50 | ||
| 43 | void thread__find_addr_location(struct thread *self, u8 cpumode, | 51 | void thread__find_addr_location(struct thread *self, |
| 52 | struct perf_session *session, u8 cpumode, | ||
| 44 | enum map_type type, u64 addr, | 53 | enum map_type type, u64 addr, |
| 45 | struct addr_location *al, | 54 | struct addr_location *al, |
| 46 | symbol_filter_t filter); | 55 | symbol_filter_t filter); |
| 47 | struct symbol *thread__find_symbol(struct thread *self, | 56 | struct symbol *map_groups__find_symbol(struct map_groups *self, |
| 48 | enum map_type type, u64 addr, | 57 | struct perf_session *session, |
| 49 | symbol_filter_t filter); | 58 | enum map_type type, u64 addr, |
| 59 | symbol_filter_t filter); | ||
| 50 | 60 | ||
| 51 | static inline struct symbol * | 61 | static inline struct symbol * |
| 52 | thread__find_function(struct thread *self, u64 addr, symbol_filter_t filter) | 62 | map_groups__find_function(struct map_groups *self, struct perf_session *session, |
| 63 | u64 addr, symbol_filter_t filter) | ||
| 53 | { | 64 | { |
| 54 | return thread__find_symbol(self, MAP__FUNCTION, addr, filter); | 65 | return map_groups__find_symbol(self, session, MAP__FUNCTION, addr, filter); |
| 55 | } | 66 | } |
| 67 | |||
| 68 | struct map *map_groups__find_by_name(struct map_groups *self, | ||
| 69 | enum map_type type, const char *name); | ||
| 56 | #endif /* __PERF_THREAD_H */ | 70 | #endif /* __PERF_THREAD_H */ |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 0302405aa2ca..c5c32be040bf 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
| @@ -177,7 +177,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused) | |||
| 177 | func_count++; | 177 | func_count++; |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | func_list = malloc_or_die(sizeof(*func_list) * func_count + 1); | 180 | func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1)); |
| 181 | 181 | ||
| 182 | i = 0; | 182 | i = 0; |
| 183 | while (list) { | 183 | while (list) { |
| @@ -1477,7 +1477,7 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok) | |||
| 1477 | goto out_free; | 1477 | goto out_free; |
| 1478 | 1478 | ||
| 1479 | field = malloc_or_die(sizeof(*field)); | 1479 | field = malloc_or_die(sizeof(*field)); |
| 1480 | memset(field, 0, sizeof(field)); | 1480 | memset(field, 0, sizeof(*field)); |
| 1481 | 1481 | ||
| 1482 | value = arg_eval(arg); | 1482 | value = arg_eval(arg); |
| 1483 | field->value = strdup(value); | 1483 | field->value = strdup(value); |
diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/trace-event-perl.c index 51e833fd58c3..6d6d76b8a21e 100644 --- a/tools/perf/util/trace-event-perl.c +++ b/tools/perf/util/trace-event-perl.c | |||
| @@ -32,9 +32,6 @@ | |||
| 32 | 32 | ||
| 33 | void xs_init(pTHX); | 33 | void xs_init(pTHX); |
| 34 | 34 | ||
| 35 | void boot_Perf__Trace__Context(pTHX_ CV *cv); | ||
| 36 | void boot_DynaLoader(pTHX_ CV *cv); | ||
| 37 | |||
| 38 | void xs_init(pTHX) | 35 | void xs_init(pTHX) |
| 39 | { | 36 | { |
| 40 | const char *file = __FILE__; | 37 | const char *file = __FILE__; |
| @@ -270,7 +267,7 @@ int common_lock_depth(struct scripting_context *context) | |||
| 270 | } | 267 | } |
| 271 | 268 | ||
| 272 | static void perl_process_event(int cpu, void *data, | 269 | static void perl_process_event(int cpu, void *data, |
| 273 | int size __attribute((unused)), | 270 | int size __unused, |
| 274 | unsigned long long nsecs, char *comm) | 271 | unsigned long long nsecs, char *comm) |
| 275 | { | 272 | { |
| 276 | struct format_field *field; | 273 | struct format_field *field; |
| @@ -362,28 +359,46 @@ static void run_start_sub(void) | |||
| 362 | /* | 359 | /* |
| 363 | * Start trace script | 360 | * Start trace script |
| 364 | */ | 361 | */ |
| 365 | static int perl_start_script(const char *script) | 362 | static int perl_start_script(const char *script, int argc, const char **argv) |
| 366 | { | 363 | { |
| 367 | const char *command_line[2] = { "", NULL }; | 364 | const char **command_line; |
| 365 | int i, err = 0; | ||
| 368 | 366 | ||
| 367 | command_line = malloc((argc + 2) * sizeof(const char *)); | ||
| 368 | command_line[0] = ""; | ||
| 369 | command_line[1] = script; | 369 | command_line[1] = script; |
| 370 | for (i = 2; i < argc + 2; i++) | ||
| 371 | command_line[i] = argv[i - 2]; | ||
| 370 | 372 | ||
| 371 | my_perl = perl_alloc(); | 373 | my_perl = perl_alloc(); |
| 372 | perl_construct(my_perl); | 374 | perl_construct(my_perl); |
| 373 | 375 | ||
| 374 | if (perl_parse(my_perl, xs_init, 2, (char **)command_line, | 376 | if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line, |
| 375 | (char **)NULL)) | 377 | (char **)NULL)) { |
| 376 | return -1; | 378 | err = -1; |
| 379 | goto error; | ||
| 380 | } | ||
| 377 | 381 | ||
| 378 | perl_run(my_perl); | 382 | if (perl_run(my_perl)) { |
| 379 | if (SvTRUE(ERRSV)) | 383 | err = -1; |
| 380 | return -1; | 384 | goto error; |
| 385 | } | ||
| 386 | |||
| 387 | if (SvTRUE(ERRSV)) { | ||
| 388 | err = -1; | ||
| 389 | goto error; | ||
| 390 | } | ||
| 381 | 391 | ||
| 382 | run_start_sub(); | 392 | run_start_sub(); |
| 383 | 393 | ||
| 394 | free(command_line); | ||
| 384 | fprintf(stderr, "perf trace started with Perl script %s\n\n", script); | 395 | fprintf(stderr, "perf trace started with Perl script %s\n\n", script); |
| 385 | |||
| 386 | return 0; | 396 | return 0; |
| 397 | error: | ||
| 398 | perl_free(my_perl); | ||
| 399 | free(command_line); | ||
| 400 | |||
| 401 | return err; | ||
| 387 | } | 402 | } |
| 388 | 403 | ||
| 389 | /* | 404 | /* |
| @@ -573,26 +588,74 @@ struct scripting_ops perl_scripting_ops = { | |||
| 573 | .generate_script = perl_generate_script, | 588 | .generate_script = perl_generate_script, |
| 574 | }; | 589 | }; |
| 575 | 590 | ||
| 576 | #ifdef NO_LIBPERL | 591 | static void print_unsupported_msg(void) |
| 577 | void setup_perl_scripting(void) | ||
| 578 | { | 592 | { |
| 579 | fprintf(stderr, "Perl scripting not supported." | 593 | fprintf(stderr, "Perl scripting not supported." |
| 580 | " Install libperl and rebuild perf to enable it. e.g. " | 594 | " Install libperl and rebuild perf to enable it.\n" |
| 581 | "apt-get install libperl-dev (ubuntu), yum install " | 595 | "For example:\n # apt-get install libperl-dev (ubuntu)" |
| 582 | "perl-ExtUtils-Embed (Fedora), etc.\n"); | 596 | "\n # yum install perl-ExtUtils-Embed (Fedora)" |
| 597 | "\n etc.\n"); | ||
| 583 | } | 598 | } |
| 584 | #else | 599 | |
| 585 | void setup_perl_scripting(void) | 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) | ||
| 586 | { | 638 | { |
| 587 | int err; | 639 | int err; |
| 588 | err = script_spec_register("Perl", &perl_scripting_ops); | 640 | err = script_spec_register("Perl", scripting_ops); |
| 589 | if (err) | 641 | if (err) |
| 590 | die("error registering Perl script extension"); | 642 | die("error registering Perl script extension"); |
| 591 | 643 | ||
| 592 | err = script_spec_register("pl", &perl_scripting_ops); | 644 | err = script_spec_register("pl", scripting_ops); |
| 593 | if (err) | 645 | if (err) |
| 594 | die("error registering pl script extension"); | 646 | die("error registering pl script extension"); |
| 595 | 647 | ||
| 596 | scripting_context = malloc(sizeof(struct scripting_context)); | 648 | scripting_context = malloc(sizeof(struct scripting_context)); |
| 597 | } | 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 | } | ||
| 598 | #endif | 661 | #endif |
diff --git a/tools/perf/util/trace-event-perl.h b/tools/perf/util/trace-event-perl.h index 8fe0d866fe1a..e88fb26137bb 100644 --- a/tools/perf/util/trace-event-perl.h +++ b/tools/perf/util/trace-event-perl.h | |||
| @@ -34,9 +34,13 @@ typedef int INTERP; | |||
| 34 | #define dXSUB_SYS | 34 | #define dXSUB_SYS |
| 35 | #define pTHX_ | 35 | #define pTHX_ |
| 36 | static inline void newXS(const char *a, void *b, const char *c) {} | 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) {} | ||
| 37 | #else | 39 | #else |
| 38 | #include <EXTERN.h> | 40 | #include <EXTERN.h> |
| 39 | #include <perl.h> | 41 | #include <perl.h> |
| 42 | void boot_Perf__Trace__Context(pTHX_ CV *cv); | ||
| 43 | void boot_DynaLoader(pTHX_ CV *cv); | ||
| 40 | typedef PerlInterpreter * INTERP; | 44 | typedef PerlInterpreter * INTERP; |
| 41 | #endif | 45 | #endif |
| 42 | 46 | ||
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 342dfdd43f87..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 | ||
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 81698d5e6503..6ad405620c9b 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
| @@ -270,7 +270,7 @@ enum trace_flag_type { | |||
| 270 | 270 | ||
| 271 | struct scripting_ops { | 271 | struct scripting_ops { |
| 272 | const char *name; | 272 | const char *name; |
| 273 | int (*start_script) (const char *); | 273 | int (*start_script) (const char *script, int argc, const char **argv); |
| 274 | int (*stop_script) (void); | 274 | int (*stop_script) (void); |
| 275 | void (*process_event) (int cpu, void *data, int size, | 275 | void (*process_event) (int cpu, void *data, int size, |
| 276 | unsigned long long nsecs, char *comm); | 276 | unsigned long long nsecs, char *comm); |
