diff options
Diffstat (limited to 'tools/perf/util')
| -rw-r--r-- | tools/perf/util/data_map.c | 75 | ||||
| -rw-r--r-- | tools/perf/util/data_map.h | 11 | ||||
| -rw-r--r-- | tools/perf/util/event.c | 78 | ||||
| -rw-r--r-- | tools/perf/util/event.h | 24 | ||||
| -rw-r--r-- | tools/perf/util/header.c | 37 | ||||
| -rw-r--r-- | tools/perf/util/header.h | 4 | ||||
| -rw-r--r-- | tools/perf/util/map.c | 87 | ||||
| -rw-r--r-- | tools/perf/util/parse-events.c | 17 | ||||
| -rw-r--r-- | tools/perf/util/parse-options.c | 3 | ||||
| -rw-r--r-- | tools/perf/util/probe-event.c | 133 | ||||
| -rw-r--r-- | tools/perf/util/probe-event.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.c | 2 | ||||
| -rw-r--r-- | tools/perf/util/session.c | 80 | ||||
| -rw-r--r-- | tools/perf/util/session.h | 16 | ||||
| -rw-r--r-- | tools/perf/util/symbol.c | 273 | ||||
| -rw-r--r-- | tools/perf/util/symbol.h | 17 | ||||
| -rw-r--r-- | tools/perf/util/thread.c | 63 | ||||
| -rw-r--r-- | tools/perf/util/thread.h | 42 | ||||
| -rw-r--r-- | tools/perf/util/trace-event-parse.c | 4 | ||||
| -rw-r--r-- | tools/perf/util/trace-event-perl.c | 67 | ||||
| -rw-r--r-- | tools/perf/util/trace-event-perl.h | 4 | ||||
| -rw-r--r-- | tools/perf/util/trace-event-read.c | 3 |
22 files changed, 754 insertions, 287 deletions
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c index ca0bedf637c2..6d46dda53a29 100644 --- a/tools/perf/util/data_map.c +++ b/tools/perf/util/data_map.c | |||
| @@ -100,11 +100,11 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 100 | } | 100 | } |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | int perf_header__read_build_ids(int input, off_t offset, off_t size) | 103 | int perf_header__read_build_ids(int input, u64 offset, u64 size) |
| 104 | { | 104 | { |
| 105 | struct build_id_event bev; | 105 | struct build_id_event bev; |
| 106 | char filename[PATH_MAX]; | 106 | char filename[PATH_MAX]; |
| 107 | off_t limit = offset + size; | 107 | u64 limit = offset + size; |
| 108 | int err = -1; | 108 | int err = -1; |
| 109 | 109 | ||
| 110 | while (offset < limit) { | 110 | while (offset < limit) { |
| @@ -129,23 +129,16 @@ out: | |||
| 129 | return err; | 129 | return err; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | int mmap_dispatch_perf_file(struct perf_header **pheader, | 132 | int perf_session__process_events(struct perf_session *self, |
| 133 | const char *input_name, | 133 | int full_paths, int *cwdlen, char **cwd) |
| 134 | int force, | ||
| 135 | int full_paths, | ||
| 136 | int *cwdlen, | ||
| 137 | char **cwd) | ||
| 138 | { | 134 | { |
| 139 | int err; | 135 | int err; |
| 140 | struct perf_header *header; | ||
| 141 | unsigned long head, shift; | 136 | unsigned long head, shift; |
| 142 | unsigned long offset = 0; | 137 | unsigned long offset = 0; |
| 143 | struct stat input_stat; | ||
| 144 | size_t page_size; | 138 | size_t page_size; |
| 145 | u64 sample_type; | 139 | u64 sample_type; |
| 146 | event_t *event; | 140 | event_t *event; |
| 147 | uint32_t size; | 141 | uint32_t size; |
| 148 | int input; | ||
| 149 | char *buf; | 142 | char *buf; |
| 150 | 143 | ||
| 151 | if (curr_handler == NULL) { | 144 | if (curr_handler == NULL) { |
| @@ -155,56 +148,19 @@ int mmap_dispatch_perf_file(struct perf_header **pheader, | |||
| 155 | 148 | ||
| 156 | page_size = getpagesize(); | 149 | page_size = getpagesize(); |
| 157 | 150 | ||
| 158 | input = open(input_name, O_RDONLY); | 151 | head = self->header.data_offset; |
| 159 | if (input < 0) { | 152 | sample_type = perf_header__sample_type(&self->header); |
| 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 | |||
| 185 | err = -ENOMEM; | ||
| 186 | header = perf_header__new(); | ||
| 187 | if (header == NULL) | ||
| 188 | goto out_close; | ||
| 189 | |||
| 190 | err = perf_header__read(header, input); | ||
| 191 | if (err < 0) | ||
| 192 | goto out_delete; | ||
| 193 | *pheader = header; | ||
| 194 | head = header->data_offset; | ||
| 195 | |||
| 196 | sample_type = perf_header__sample_type(header); | ||
| 197 | 153 | ||
| 198 | err = -EINVAL; | 154 | err = -EINVAL; |
| 199 | if (curr_handler->sample_type_check && | 155 | if (curr_handler->sample_type_check && |
| 200 | curr_handler->sample_type_check(sample_type) < 0) | 156 | curr_handler->sample_type_check(sample_type) < 0) |
| 201 | goto out_delete; | 157 | goto out_err; |
| 202 | 158 | ||
| 203 | if (!full_paths) { | 159 | if (!full_paths) { |
| 204 | if (getcwd(__cwd, sizeof(__cwd)) == NULL) { | 160 | if (getcwd(__cwd, sizeof(__cwd)) == NULL) { |
| 205 | pr_err("failed to get the current directory\n"); | 161 | pr_err("failed to get the current directory\n"); |
| 206 | err = -errno; | 162 | err = -errno; |
| 207 | goto out_delete; | 163 | goto out_err; |
| 208 | } | 164 | } |
| 209 | *cwd = __cwd; | 165 | *cwd = __cwd; |
| 210 | *cwdlen = strlen(*cwd); | 166 | *cwdlen = strlen(*cwd); |
| @@ -219,11 +175,11 @@ int mmap_dispatch_perf_file(struct perf_header **pheader, | |||
| 219 | 175 | ||
| 220 | remap: | 176 | remap: |
| 221 | buf = mmap(NULL, page_size * mmap_window, PROT_READ, | 177 | buf = mmap(NULL, page_size * mmap_window, PROT_READ, |
| 222 | MAP_SHARED, input, offset); | 178 | MAP_SHARED, self->fd, offset); |
| 223 | if (buf == MAP_FAILED) { | 179 | if (buf == MAP_FAILED) { |
| 224 | pr_err("failed to mmap file\n"); | 180 | pr_err("failed to mmap file\n"); |
| 225 | err = -errno; | 181 | err = -errno; |
| 226 | goto out_delete; | 182 | goto out_err; |
| 227 | } | 183 | } |
| 228 | 184 | ||
| 229 | more: | 185 | more: |
| @@ -273,19 +229,14 @@ more: | |||
| 273 | 229 | ||
| 274 | head += size; | 230 | head += size; |
| 275 | 231 | ||
| 276 | if (offset + head >= header->data_offset + header->data_size) | 232 | if (offset + head >= self->header.data_offset + self->header.data_size) |
| 277 | goto done; | 233 | goto done; |
| 278 | 234 | ||
| 279 | if (offset + head < (unsigned long)input_stat.st_size) | 235 | if (offset + head < self->size) |
| 280 | goto more; | 236 | goto more; |
| 281 | 237 | ||
| 282 | done: | 238 | done: |
| 283 | err = 0; | 239 | err = 0; |
| 284 | out_close: | 240 | out_err: |
| 285 | close(input); | ||
| 286 | |||
| 287 | return err; | 241 | return err; |
| 288 | out_delete: | ||
| 289 | perf_header__delete(header); | ||
| 290 | goto out_close; | ||
| 291 | } | 242 | } |
diff --git a/tools/perf/util/data_map.h b/tools/perf/util/data_map.h index 3180ff7e3633..98c5b823388c 100644 --- a/tools/perf/util/data_map.h +++ b/tools/perf/util/data_map.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "event.h" | 4 | #include "event.h" |
| 5 | #include "header.h" | 5 | #include "header.h" |
| 6 | #include "session.h" | ||
| 6 | 7 | ||
| 7 | typedef int (*event_type_handler_t)(event_t *); | 8 | typedef int (*event_type_handler_t)(event_t *); |
| 8 | 9 | ||
| @@ -21,12 +22,8 @@ struct perf_file_handler { | |||
| 21 | }; | 22 | }; |
| 22 | 23 | ||
| 23 | void register_perf_file_handler(struct perf_file_handler *handler); | 24 | void register_perf_file_handler(struct perf_file_handler *handler); |
| 24 | int mmap_dispatch_perf_file(struct perf_header **pheader, | 25 | int perf_session__process_events(struct perf_session *self, |
| 25 | const char *input_name, | 26 | int full_paths, int *cwdlen, char **cwd); |
| 26 | int force, | 27 | int perf_header__read_build_ids(int input, u64 offset, u64 file_size); |
| 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 | 28 | ||
| 32 | #endif | 29 | #endif |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 414b89d1bde9..ba0de90cd3d4 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
| @@ -254,13 +254,14 @@ void thread__find_addr_location(struct thread *self, u8 cpumode, | |||
| 254 | struct addr_location *al, | 254 | struct addr_location *al, |
| 255 | symbol_filter_t filter) | 255 | symbol_filter_t filter) |
| 256 | { | 256 | { |
| 257 | struct thread *thread = al->thread = self; | 257 | struct map_groups *mg = &self->mg; |
| 258 | 258 | ||
| 259 | al->thread = self; | ||
| 259 | al->addr = addr; | 260 | al->addr = addr; |
| 260 | 261 | ||
| 261 | if (cpumode & PERF_RECORD_MISC_KERNEL) { | 262 | if (cpumode & PERF_RECORD_MISC_KERNEL) { |
| 262 | al->level = 'k'; | 263 | al->level = 'k'; |
| 263 | thread = kthread; | 264 | mg = kmaps; |
| 264 | } else if (cpumode & PERF_RECORD_MISC_USER) | 265 | } else if (cpumode & PERF_RECORD_MISC_USER) |
| 265 | al->level = '.'; | 266 | al->level = '.'; |
| 266 | else { | 267 | else { |
| @@ -270,7 +271,7 @@ void thread__find_addr_location(struct thread *self, u8 cpumode, | |||
| 270 | return; | 271 | return; |
| 271 | } | 272 | } |
| 272 | try_again: | 273 | try_again: |
| 273 | al->map = thread__find_map(thread, type, al->addr); | 274 | al->map = map_groups__find(mg, type, al->addr); |
| 274 | if (al->map == NULL) { | 275 | if (al->map == NULL) { |
| 275 | /* | 276 | /* |
| 276 | * If this is outside of all known maps, and is a negative | 277 | * If this is outside of all known maps, and is a negative |
| @@ -281,8 +282,8 @@ try_again: | |||
| 281 | * "[vdso]" dso, but for now lets use the old trick of looking | 282 | * "[vdso]" dso, but for now lets use the old trick of looking |
| 282 | * in the whole kernel symbol list. | 283 | * in the whole kernel symbol list. |
| 283 | */ | 284 | */ |
| 284 | if ((long long)al->addr < 0 && thread != kthread) { | 285 | if ((long long)al->addr < 0 && mg != kmaps) { |
| 285 | thread = kthread; | 286 | mg = kmaps; |
| 286 | goto try_again; | 287 | goto try_again; |
| 287 | } | 288 | } |
| 288 | al->sym = NULL; | 289 | al->sym = NULL; |
| @@ -310,3 +311,70 @@ int event__preprocess_sample(const event_t *self, struct addr_location *al, | |||
| 310 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 311 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
| 311 | return 0; | 312 | return 0; |
| 312 | } | 313 | } |
| 314 | |||
| 315 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data) | ||
| 316 | { | ||
| 317 | u64 *array = event->sample.array; | ||
| 318 | |||
| 319 | if (type & PERF_SAMPLE_IP) { | ||
| 320 | data->ip = event->ip.ip; | ||
| 321 | array++; | ||
| 322 | } | ||
| 323 | |||
| 324 | if (type & PERF_SAMPLE_TID) { | ||
| 325 | u32 *p = (u32 *)array; | ||
| 326 | data->pid = p[0]; | ||
| 327 | data->tid = p[1]; | ||
| 328 | array++; | ||
| 329 | } | ||
| 330 | |||
| 331 | if (type & PERF_SAMPLE_TIME) { | ||
| 332 | data->time = *array; | ||
| 333 | array++; | ||
| 334 | } | ||
| 335 | |||
| 336 | if (type & PERF_SAMPLE_ADDR) { | ||
| 337 | data->addr = *array; | ||
| 338 | array++; | ||
| 339 | } | ||
| 340 | |||
| 341 | if (type & PERF_SAMPLE_ID) { | ||
| 342 | data->id = *array; | ||
| 343 | array++; | ||
| 344 | } | ||
| 345 | |||
| 346 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
| 347 | data->stream_id = *array; | ||
| 348 | array++; | ||
| 349 | } | ||
| 350 | |||
| 351 | if (type & PERF_SAMPLE_CPU) { | ||
| 352 | u32 *p = (u32 *)array; | ||
| 353 | data->cpu = *p; | ||
| 354 | array++; | ||
| 355 | } | ||
| 356 | |||
| 357 | if (type & PERF_SAMPLE_PERIOD) { | ||
| 358 | data->period = *array; | ||
| 359 | array++; | ||
| 360 | } | ||
| 361 | |||
| 362 | if (type & PERF_SAMPLE_READ) { | ||
| 363 | pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); | ||
| 364 | return -1; | ||
| 365 | } | ||
| 366 | |||
| 367 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
| 368 | data->callchain = (struct ip_callchain *)array; | ||
| 369 | array += 1 + data->callchain->nr; | ||
| 370 | } | ||
| 371 | |||
| 372 | if (type & PERF_SAMPLE_RAW) { | ||
| 373 | u32 *p = (u32 *)array; | ||
| 374 | data->raw_size = *p; | ||
| 375 | p++; | ||
| 376 | data->raw_data = p; | ||
| 377 | } | ||
| 378 | |||
| 379 | return 0; | ||
| 380 | } | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index a4cc8105cf67..51a96c2effde 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 { |
| @@ -89,10 +103,11 @@ 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; |
| @@ -136,6 +151,8 @@ 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 | struct symbol *map__find_symbol(struct map *self, u64 addr, |
| 138 | symbol_filter_t filter); | 153 | symbol_filter_t filter); |
| 154 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
| 155 | symbol_filter_t filter); | ||
| 139 | void map__fixup_start(struct map *self); | 156 | void map__fixup_start(struct map *self); |
| 140 | void map__fixup_end(struct map *self); | 157 | void map__fixup_end(struct map *self); |
| 141 | 158 | ||
| @@ -155,5 +172,6 @@ int event__process_task(event_t *self); | |||
| 155 | struct addr_location; | 172 | struct addr_location; |
| 156 | int event__preprocess_sample(const event_t *self, struct addr_location *al, | 173 | int event__preprocess_sample(const event_t *self, struct addr_location *al, |
| 157 | symbol_filter_t filter); | 174 | symbol_filter_t filter); |
| 175 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data); | ||
| 158 | 176 | ||
| 159 | #endif /* __PERF_RECORD_H */ | 177 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4805e6dfd23c..f2e8d8715111 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -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/map.c b/tools/perf/util/map.c index 69f94fe9db20..76bdca640a9b 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -104,43 +104,64 @@ 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 | static int map__load(struct map *self, symbol_filter_t filter) |
| 108 | symbol_filter_t filter) | ||
| 109 | { | 108 | { |
| 110 | if (!dso__loaded(self->dso, self->type)) { | 109 | const char *name = self->dso->long_name; |
| 111 | int nr = dso__load(self->dso, self, filter); | 110 | int nr = dso__load(self->dso, self, filter); |
| 112 | 111 | ||
| 113 | if (nr < 0) { | 112 | if (nr < 0) { |
| 114 | if (self->dso->has_build_id) { | 113 | if (self->dso->has_build_id) { |
| 115 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 114 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
| 116 | 115 | ||
| 117 | build_id__sprintf(self->dso->build_id, | 116 | build_id__sprintf(self->dso->build_id, |
| 118 | sizeof(self->dso->build_id), | 117 | sizeof(self->dso->build_id), |
| 119 | sbuild_id); | 118 | sbuild_id); |
| 120 | pr_warning("%s with build id %s not found", | 119 | pr_warning("%s with build id %s not found", |
| 121 | self->dso->long_name, sbuild_id); | 120 | name, sbuild_id); |
| 122 | } else | 121 | } else |
| 123 | pr_warning("Failed to open %s", | 122 | pr_warning("Failed to open %s", name); |
| 124 | self->dso->long_name); | 123 | |
| 125 | pr_warning(", continuing without symbols\n"); | 124 | pr_warning(", continuing without symbols\n"); |
| 126 | return NULL; | 125 | return -1; |
| 127 | } else if (nr == 0) { | 126 | } else if (nr == 0) { |
| 128 | const char *name = self->dso->long_name; | 127 | const size_t len = strlen(name); |
| 129 | const size_t len = strlen(name); | 128 | const size_t real_len = len - sizeof(DSO__DELETED); |
| 130 | const size_t real_len = len - sizeof(DSO__DELETED); | 129 | |
| 131 | 130 | if (len > sizeof(DSO__DELETED) && | |
| 132 | if (len > sizeof(DSO__DELETED) && | 131 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { |
| 133 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | 132 | pr_warning("%.*s was updated, restart the long " |
| 134 | pr_warning("%.*s was updated, restart the long running apps that use it!\n", | 133 | "running apps that use it!\n", |
| 135 | (int)real_len, name); | 134 | (int)real_len, name); |
| 136 | } else { | 135 | } else { |
| 137 | pr_warning("no symbols found in %s, maybe install a debug package?\n", name); | 136 | pr_warning("no symbols found in %s, maybe install " |
| 138 | } | 137 | "a debug package?\n", name); |
| 139 | return NULL; | ||
| 140 | } | 138 | } |
| 139 | |||
| 140 | return -1; | ||
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | return self->dso->find_symbol(self->dso, self->type, addr); | 143 | return 0; |
| 144 | } | ||
| 145 | |||
| 146 | struct symbol *map__find_symbol(struct map *self, u64 addr, | ||
| 147 | symbol_filter_t filter) | ||
| 148 | { | ||
| 149 | if (!dso__loaded(self->dso, self->type) && map__load(self, filter) < 0) | ||
| 150 | return NULL; | ||
| 151 | |||
| 152 | return dso__find_symbol(self->dso, self->type, addr); | ||
| 153 | } | ||
| 154 | |||
| 155 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
| 156 | symbol_filter_t filter) | ||
| 157 | { | ||
| 158 | if (!dso__loaded(self->dso, self->type) && map__load(self, filter) < 0) | ||
| 159 | return NULL; | ||
| 160 | |||
| 161 | if (!dso__sorted_by_name(self->dso, self->type)) | ||
| 162 | dso__sort_by_name(self->dso, self->type); | ||
| 163 | |||
| 164 | return dso__find_symbol_by_name(self->dso, self->type, name); | ||
| 144 | } | 165 | } |
| 145 | 166 | ||
| 146 | struct map *map__clone(struct map *self) | 167 | 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..d14a4585bcaf 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; |
| @@ -258,7 +261,7 @@ int synthesize_perf_probe_event(struct probe_point *pp) | |||
| 258 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, | 261 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, |
| 259 | offs, pp->retprobe ? "%return" : "", line); | 262 | offs, pp->retprobe ? "%return" : "", line); |
| 260 | else | 263 | else |
| 261 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->file, line); | 264 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); |
| 262 | if (ret <= 0) | 265 | if (ret <= 0) |
| 263 | goto error; | 266 | goto error; |
| 264 | len = ret; | 267 | len = ret; |
| @@ -373,14 +376,32 @@ static void clear_probe_point(struct probe_point *pp) | |||
| 373 | free(pp->args); | 376 | free(pp->args); |
| 374 | for (i = 0; i < pp->found; i++) | 377 | for (i = 0; i < pp->found; i++) |
| 375 | free(pp->probes[i]); | 378 | free(pp->probes[i]); |
| 376 | memset(pp, 0, sizeof(pp)); | 379 | memset(pp, 0, sizeof(*pp)); |
| 380 | } | ||
| 381 | |||
| 382 | /* Show an event */ | ||
| 383 | static void show_perf_probe_event(const char *group, const char *event, | ||
| 384 | const char *place, struct probe_point *pp) | ||
| 385 | { | ||
| 386 | int i; | ||
| 387 | char buf[128]; | ||
| 388 | |||
| 389 | e_snprintf(buf, 128, "%s:%s", group, event); | ||
| 390 | printf(" %-40s (on %s", buf, place); | ||
| 391 | |||
| 392 | if (pp->nr_args > 0) { | ||
| 393 | printf(" with"); | ||
| 394 | for (i = 0; i < pp->nr_args; i++) | ||
| 395 | printf(" %s", pp->args[i]); | ||
| 396 | } | ||
| 397 | printf(")\n"); | ||
| 377 | } | 398 | } |
| 378 | 399 | ||
| 379 | /* List up current perf-probe events */ | 400 | /* List up current perf-probe events */ |
| 380 | void show_perf_probe_events(void) | 401 | void show_perf_probe_events(void) |
| 381 | { | 402 | { |
| 382 | unsigned int i; | 403 | unsigned int i; |
| 383 | int fd; | 404 | int fd, nr; |
| 384 | char *group, *event; | 405 | char *group, *event; |
| 385 | struct probe_point pp; | 406 | struct probe_point pp; |
| 386 | struct strlist *rawlist; | 407 | struct strlist *rawlist; |
| @@ -393,8 +414,13 @@ void show_perf_probe_events(void) | |||
| 393 | for (i = 0; i < strlist__nr_entries(rawlist); i++) { | 414 | for (i = 0; i < strlist__nr_entries(rawlist); i++) { |
| 394 | ent = strlist__entry(rawlist, i); | 415 | ent = strlist__entry(rawlist, i); |
| 395 | parse_trace_kprobe_event(ent->s, &group, &event, &pp); | 416 | parse_trace_kprobe_event(ent->s, &group, &event, &pp); |
| 417 | /* Synthesize only event probe point */ | ||
| 418 | nr = pp.nr_args; | ||
| 419 | pp.nr_args = 0; | ||
| 396 | synthesize_perf_probe_event(&pp); | 420 | synthesize_perf_probe_event(&pp); |
| 397 | printf("[%s:%s]\t%s\n", group, event, pp.probes[0]); | 421 | pp.nr_args = nr; |
| 422 | /* Show an event */ | ||
| 423 | show_perf_probe_event(group, event, pp.probes[0], &pp); | ||
| 398 | free(group); | 424 | free(group); |
| 399 | free(event); | 425 | free(event); |
| 400 | clear_probe_point(&pp); | 426 | clear_probe_point(&pp); |
| @@ -404,21 +430,28 @@ void show_perf_probe_events(void) | |||
| 404 | } | 430 | } |
| 405 | 431 | ||
| 406 | /* Get current perf-probe event names */ | 432 | /* Get current perf-probe event names */ |
| 407 | static struct strlist *get_perf_event_names(int fd) | 433 | static struct strlist *get_perf_event_names(int fd, bool include_group) |
| 408 | { | 434 | { |
| 409 | unsigned int i; | 435 | unsigned int i; |
| 410 | char *group, *event; | 436 | char *group, *event; |
| 437 | char buf[128]; | ||
| 411 | struct strlist *sl, *rawlist; | 438 | struct strlist *sl, *rawlist; |
| 412 | struct str_node *ent; | 439 | struct str_node *ent; |
| 413 | 440 | ||
| 414 | rawlist = get_trace_kprobe_event_rawlist(fd); | 441 | rawlist = get_trace_kprobe_event_rawlist(fd); |
| 415 | 442 | ||
| 416 | sl = strlist__new(false, NULL); | 443 | sl = strlist__new(true, NULL); |
| 417 | for (i = 0; i < strlist__nr_entries(rawlist); i++) { | 444 | for (i = 0; i < strlist__nr_entries(rawlist); i++) { |
| 418 | ent = strlist__entry(rawlist, i); | 445 | ent = strlist__entry(rawlist, i); |
| 419 | parse_trace_kprobe_event(ent->s, &group, &event, NULL); | 446 | parse_trace_kprobe_event(ent->s, &group, &event, NULL); |
| 420 | strlist__add(sl, event); | 447 | if (include_group) { |
| 448 | if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) | ||
| 449 | die("Failed to copy group:event name."); | ||
| 450 | strlist__add(sl, buf); | ||
| 451 | } else | ||
| 452 | strlist__add(sl, event); | ||
| 421 | free(group); | 453 | free(group); |
| 454 | free(event); | ||
| 422 | } | 455 | } |
| 423 | 456 | ||
| 424 | strlist__delete(rawlist); | 457 | strlist__delete(rawlist); |
| @@ -426,24 +459,30 @@ static struct strlist *get_perf_event_names(int fd) | |||
| 426 | return sl; | 459 | return sl; |
| 427 | } | 460 | } |
| 428 | 461 | ||
| 429 | static int write_trace_kprobe_event(int fd, const char *buf) | 462 | static void write_trace_kprobe_event(int fd, const char *buf) |
| 430 | { | 463 | { |
| 431 | int ret; | 464 | int ret; |
| 432 | 465 | ||
| 466 | pr_debug("Writing event: %s\n", buf); | ||
| 433 | ret = write(fd, buf, strlen(buf)); | 467 | ret = write(fd, buf, strlen(buf)); |
| 434 | if (ret <= 0) | 468 | if (ret <= 0) |
| 435 | die("Failed to create event."); | 469 | die("Failed to write event: %s", strerror(errno)); |
| 436 | else | ||
| 437 | printf("Added new event: %s\n", buf); | ||
| 438 | |||
| 439 | return ret; | ||
| 440 | } | 470 | } |
| 441 | 471 | ||
| 442 | static void get_new_event_name(char *buf, size_t len, const char *base, | 472 | static void get_new_event_name(char *buf, size_t len, const char *base, |
| 443 | struct strlist *namelist) | 473 | struct strlist *namelist) |
| 444 | { | 474 | { |
| 445 | int i, ret; | 475 | int i, ret; |
| 446 | for (i = 0; i < MAX_EVENT_INDEX; i++) { | 476 | |
| 477 | /* Try no suffix */ | ||
| 478 | ret = e_snprintf(buf, len, "%s", base); | ||
| 479 | if (ret < 0) | ||
| 480 | die("snprintf() failed: %s", strerror(-ret)); | ||
| 481 | if (!strlist__has_entry(namelist, buf)) | ||
| 482 | return; | ||
| 483 | |||
| 484 | /* Try to add suffix */ | ||
| 485 | for (i = 1; i < MAX_EVENT_INDEX; i++) { | ||
| 447 | ret = e_snprintf(buf, len, "%s_%d", base, i); | 486 | ret = e_snprintf(buf, len, "%s_%d", base, i); |
| 448 | if (ret < 0) | 487 | if (ret < 0) |
| 449 | die("snprintf() failed: %s", strerror(-ret)); | 488 | die("snprintf() failed: %s", strerror(-ret)); |
| @@ -464,7 +503,7 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) | |||
| 464 | 503 | ||
| 465 | fd = open_kprobe_events(O_RDWR, O_APPEND); | 504 | fd = open_kprobe_events(O_RDWR, O_APPEND); |
| 466 | /* Get current event names */ | 505 | /* Get current event names */ |
| 467 | namelist = get_perf_event_names(fd); | 506 | namelist = get_perf_event_names(fd, false); |
| 468 | 507 | ||
| 469 | for (j = 0; j < nr_probes; j++) { | 508 | for (j = 0; j < nr_probes; j++) { |
| 470 | pp = probes + j; | 509 | pp = probes + j; |
| @@ -476,9 +515,73 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) | |||
| 476 | PERFPROBE_GROUP, event, | 515 | PERFPROBE_GROUP, event, |
| 477 | pp->probes[i]); | 516 | pp->probes[i]); |
| 478 | write_trace_kprobe_event(fd, buf); | 517 | write_trace_kprobe_event(fd, buf); |
| 518 | printf("Added new event:\n"); | ||
| 519 | /* Get the first parameter (probe-point) */ | ||
| 520 | sscanf(pp->probes[i], "%s", buf); | ||
| 521 | show_perf_probe_event(PERFPROBE_GROUP, event, | ||
| 522 | buf, pp); | ||
| 479 | /* Add added event name to namelist */ | 523 | /* Add added event name to namelist */ |
| 480 | strlist__add(namelist, event); | 524 | strlist__add(namelist, event); |
| 481 | } | 525 | } |
| 482 | } | 526 | } |
| 527 | /* Show how to use the event. */ | ||
| 528 | printf("\nYou can now use it on all perf tools, such as:\n\n"); | ||
| 529 | printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event); | ||
| 530 | |||
| 531 | strlist__delete(namelist); | ||
| 532 | close(fd); | ||
| 533 | } | ||
| 534 | |||
| 535 | static void del_trace_kprobe_event(int fd, const char *group, | ||
| 536 | const char *event, struct strlist *namelist) | ||
| 537 | { | ||
| 538 | char buf[128]; | ||
| 539 | |||
| 540 | if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) | ||
| 541 | die("Failed to copy event."); | ||
| 542 | if (!strlist__has_entry(namelist, buf)) { | ||
| 543 | pr_warning("Warning: event \"%s\" is not found.\n", buf); | ||
| 544 | return; | ||
| 545 | } | ||
| 546 | /* Convert from perf-probe event to trace-kprobe event */ | ||
| 547 | if (e_snprintf(buf, 128, "-:%s/%s", group, event) < 0) | ||
| 548 | die("Failed to copy event."); | ||
| 549 | |||
| 550 | write_trace_kprobe_event(fd, buf); | ||
| 551 | printf("Remove event: %s:%s\n", group, event); | ||
| 552 | } | ||
| 553 | |||
| 554 | void del_trace_kprobe_events(struct strlist *dellist) | ||
| 555 | { | ||
| 556 | int fd; | ||
| 557 | unsigned int i; | ||
| 558 | const char *group, *event; | ||
| 559 | char *p, *str; | ||
| 560 | struct str_node *ent; | ||
| 561 | struct strlist *namelist; | ||
| 562 | |||
| 563 | fd = open_kprobe_events(O_RDWR, O_APPEND); | ||
| 564 | /* Get current event names */ | ||
| 565 | namelist = get_perf_event_names(fd, true); | ||
| 566 | |||
| 567 | for (i = 0; i < strlist__nr_entries(dellist); i++) { | ||
| 568 | ent = strlist__entry(dellist, i); | ||
| 569 | str = strdup(ent->s); | ||
| 570 | if (!str) | ||
| 571 | die("Failed to copy event."); | ||
| 572 | p = strchr(str, ':'); | ||
| 573 | if (p) { | ||
| 574 | group = str; | ||
| 575 | *p = '\0'; | ||
| 576 | event = p + 1; | ||
| 577 | } else { | ||
| 578 | group = PERFPROBE_GROUP; | ||
| 579 | event = str; | ||
| 580 | } | ||
| 581 | del_trace_kprobe_event(fd, group, event, namelist); | ||
| 582 | free(str); | ||
| 583 | } | ||
| 584 | strlist__delete(namelist); | ||
| 483 | close(fd); | 585 | close(fd); |
| 484 | } | 586 | } |
| 587 | |||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 0c6fe56fe38a..f752159124ae 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
| @@ -10,6 +10,7 @@ extern void parse_trace_kprobe_event(const char *str, char **group, | |||
| 10 | char **event, struct probe_point *pp); | 10 | char **event, struct probe_point *pp); |
| 11 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); | 11 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); |
| 12 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); | 12 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); |
| 13 | extern void del_trace_kprobe_events(struct strlist *dellist); | ||
| 13 | extern void show_perf_probe_events(void); | 14 | extern void show_perf_probe_events(void); |
| 14 | 15 | ||
| 15 | /* Maximum index number of event-name postfix */ | 16 | /* 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..4585f1d86792 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 | } |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c new file mode 100644 index 000000000000..707ce1cb1621 --- /dev/null +++ b/tools/perf/util/session.c | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | #include <linux/kernel.h> | ||
| 2 | |||
| 3 | #include <unistd.h> | ||
| 4 | #include <sys/types.h> | ||
| 5 | |||
| 6 | #include "session.h" | ||
| 7 | #include "util.h" | ||
| 8 | |||
| 9 | static int perf_session__open(struct perf_session *self, bool force) | ||
| 10 | { | ||
| 11 | struct stat input_stat; | ||
| 12 | |||
| 13 | self->fd = open(self->filename, O_RDONLY); | ||
| 14 | if (self->fd < 0) { | ||
| 15 | pr_err("failed to open file: %s", self->filename); | ||
| 16 | if (!strcmp(self->filename, "perf.data")) | ||
| 17 | pr_err(" (try 'perf record' first)"); | ||
| 18 | pr_err("\n"); | ||
| 19 | return -errno; | ||
| 20 | } | ||
| 21 | |||
| 22 | if (fstat(self->fd, &input_stat) < 0) | ||
| 23 | goto out_close; | ||
| 24 | |||
| 25 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
| 26 | pr_err("file %s not owned by current user or root\n", | ||
| 27 | self->filename); | ||
| 28 | goto out_close; | ||
| 29 | } | ||
| 30 | |||
| 31 | if (!input_stat.st_size) { | ||
| 32 | pr_info("zero-sized file (%s), nothing to do!\n", | ||
| 33 | self->filename); | ||
| 34 | goto out_close; | ||
| 35 | } | ||
| 36 | |||
| 37 | if (perf_header__read(&self->header, self->fd) < 0) { | ||
| 38 | pr_err("incompatible file format"); | ||
| 39 | goto out_close; | ||
| 40 | } | ||
| 41 | |||
| 42 | self->size = input_stat.st_size; | ||
| 43 | return 0; | ||
| 44 | |||
| 45 | out_close: | ||
| 46 | close(self->fd); | ||
| 47 | self->fd = -1; | ||
| 48 | return -1; | ||
| 49 | } | ||
| 50 | |||
| 51 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) | ||
| 52 | { | ||
| 53 | size_t len = strlen(filename) + 1; | ||
| 54 | struct perf_session *self = zalloc(sizeof(*self) + len); | ||
| 55 | |||
| 56 | if (self == NULL) | ||
| 57 | goto out; | ||
| 58 | |||
| 59 | if (perf_header__init(&self->header) < 0) | ||
| 60 | goto out_delete; | ||
| 61 | |||
| 62 | memcpy(self->filename, filename, len); | ||
| 63 | |||
| 64 | if (mode == O_RDONLY && perf_session__open(self, force) < 0) { | ||
| 65 | perf_session__delete(self); | ||
| 66 | self = NULL; | ||
| 67 | } | ||
| 68 | out: | ||
| 69 | return self; | ||
| 70 | out_delete: | ||
| 71 | free(self); | ||
| 72 | return NULL; | ||
| 73 | } | ||
| 74 | |||
| 75 | void perf_session__delete(struct perf_session *self) | ||
| 76 | { | ||
| 77 | perf_header__exit(&self->header); | ||
| 78 | close(self->fd); | ||
| 79 | free(self); | ||
| 80 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h new file mode 100644 index 000000000000..f3699c8c8ed4 --- /dev/null +++ b/tools/perf/util/session.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | #ifndef __PERF_SESSION_H | ||
| 2 | #define __PERF_SESSION_H | ||
| 3 | |||
| 4 | #include "header.h" | ||
| 5 | |||
| 6 | struct perf_session { | ||
| 7 | struct perf_header header; | ||
| 8 | unsigned long size; | ||
| 9 | int fd; | ||
| 10 | char filename[0]; | ||
| 11 | }; | ||
| 12 | |||
| 13 | struct perf_session *perf_session__new(const char *filename, int mode, bool force); | ||
| 14 | void perf_session__delete(struct perf_session *self); | ||
| 15 | |||
| 16 | #endif /* __PERF_SESSION_H */ | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index fffcb937cdcb..d3d9fed74f1d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -29,11 +29,9 @@ enum dso_origin { | |||
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | static void dsos__add(struct list_head *head, struct dso *dso); | 31 | 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); | 32 | 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, | 33 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
| 36 | struct thread *thread, symbol_filter_t filter); | 34 | struct map_groups *mg, symbol_filter_t filter); |
| 37 | unsigned int symbol__priv_size; | 35 | unsigned int symbol__priv_size; |
| 38 | static int vmlinux_path__nr_entries; | 36 | static int vmlinux_path__nr_entries; |
| 39 | static char **vmlinux_path; | 37 | static char **vmlinux_path; |
| @@ -43,19 +41,41 @@ static struct symbol_conf symbol_conf__defaults = { | |||
| 43 | .try_vmlinux_path = true, | 41 | .try_vmlinux_path = true, |
| 44 | }; | 42 | }; |
| 45 | 43 | ||
| 46 | static struct thread kthread_mem; | 44 | static struct map_groups kmaps_mem; |
| 47 | struct thread *kthread = &kthread_mem; | 45 | struct map_groups *kmaps = &kmaps_mem; |
| 48 | 46 | ||
| 49 | bool dso__loaded(const struct dso *self, enum map_type type) | 47 | bool dso__loaded(const struct dso *self, enum map_type type) |
| 50 | { | 48 | { |
| 51 | return self->loaded & (1 << type); | 49 | return self->loaded & (1 << type); |
| 52 | } | 50 | } |
| 53 | 51 | ||
| 52 | bool dso__sorted_by_name(const struct dso *self, enum map_type type) | ||
| 53 | { | ||
| 54 | return self->sorted_by_name & (1 << type); | ||
| 55 | } | ||
| 56 | |||
| 54 | static void dso__set_loaded(struct dso *self, enum map_type type) | 57 | static void dso__set_loaded(struct dso *self, enum map_type type) |
| 55 | { | 58 | { |
| 56 | self->loaded |= (1 << type); | 59 | self->loaded |= (1 << type); |
| 57 | } | 60 | } |
| 58 | 61 | ||
| 62 | static void dso__set_sorted_by_name(struct dso *self, enum map_type type) | ||
| 63 | { | ||
| 64 | self->sorted_by_name |= (1 << type); | ||
| 65 | } | ||
| 66 | |||
| 67 | static bool symbol_type__is_a(char symbol_type, enum map_type map_type) | ||
| 68 | { | ||
| 69 | switch (map_type) { | ||
| 70 | case MAP__FUNCTION: | ||
| 71 | return symbol_type == 'T' || symbol_type == 'W'; | ||
| 72 | case MAP__VARIABLE: | ||
| 73 | return symbol_type == 'D' || symbol_type == 'd'; | ||
| 74 | default: | ||
| 75 | return false; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 59 | static void symbols__fixup_end(struct rb_root *self) | 79 | static void symbols__fixup_end(struct rb_root *self) |
| 60 | { | 80 | { |
| 61 | struct rb_node *nd, *prevnd = rb_first(self); | 81 | struct rb_node *nd, *prevnd = rb_first(self); |
| @@ -79,7 +99,7 @@ static void symbols__fixup_end(struct rb_root *self) | |||
| 79 | curr->end = roundup(curr->start, 4096); | 99 | curr->end = roundup(curr->start, 4096); |
| 80 | } | 100 | } |
| 81 | 101 | ||
| 82 | static void __thread__fixup_maps_end(struct thread *self, enum map_type type) | 102 | static void __map_groups__fixup_end(struct map_groups *self, enum map_type type) |
| 83 | { | 103 | { |
| 84 | struct map *prev, *curr; | 104 | struct map *prev, *curr; |
| 85 | struct rb_node *nd, *prevnd = rb_first(&self->maps[type]); | 105 | struct rb_node *nd, *prevnd = rb_first(&self->maps[type]); |
| @@ -102,11 +122,11 @@ static void __thread__fixup_maps_end(struct thread *self, enum map_type type) | |||
| 102 | curr->end = ~0UL; | 122 | curr->end = ~0UL; |
| 103 | } | 123 | } |
| 104 | 124 | ||
| 105 | static void thread__fixup_maps_end(struct thread *self) | 125 | static void map_groups__fixup_end(struct map_groups *self) |
| 106 | { | 126 | { |
| 107 | int i; | 127 | int i; |
| 108 | for (i = 0; i < MAP__NR_TYPES; ++i) | 128 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 109 | __thread__fixup_maps_end(self, i); | 129 | __map_groups__fixup_end(self, i); |
| 110 | } | 130 | } |
| 111 | 131 | ||
| 112 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) | 132 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) |
| @@ -164,11 +184,11 @@ struct dso *dso__new(const char *name) | |||
| 164 | dso__set_long_name(self, self->name); | 184 | dso__set_long_name(self, self->name); |
| 165 | self->short_name = self->name; | 185 | self->short_name = self->name; |
| 166 | for (i = 0; i < MAP__NR_TYPES; ++i) | 186 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 167 | self->symbols[i] = RB_ROOT; | 187 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; |
| 168 | self->find_symbol = dso__find_symbol; | ||
| 169 | self->slen_calculated = 0; | 188 | self->slen_calculated = 0; |
| 170 | self->origin = DSO__ORIG_NOT_FOUND; | 189 | self->origin = DSO__ORIG_NOT_FOUND; |
| 171 | self->loaded = 0; | 190 | self->loaded = 0; |
| 191 | self->sorted_by_name = 0; | ||
| 172 | self->has_build_id = 0; | 192 | self->has_build_id = 0; |
| 173 | } | 193 | } |
| 174 | 194 | ||
| @@ -246,11 +266,85 @@ static struct symbol *symbols__find(struct rb_root *self, u64 ip) | |||
| 246 | return NULL; | 266 | return NULL; |
| 247 | } | 267 | } |
| 248 | 268 | ||
| 249 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr) | 269 | struct symbol_name_rb_node { |
| 270 | struct rb_node rb_node; | ||
| 271 | struct symbol sym; | ||
| 272 | }; | ||
| 273 | |||
| 274 | static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym) | ||
| 275 | { | ||
| 276 | struct rb_node **p = &self->rb_node; | ||
| 277 | struct rb_node *parent = NULL; | ||
| 278 | struct symbol_name_rb_node *symn = ((void *)sym) - sizeof(*parent), *s; | ||
| 279 | |||
| 280 | while (*p != NULL) { | ||
| 281 | parent = *p; | ||
| 282 | s = rb_entry(parent, struct symbol_name_rb_node, rb_node); | ||
| 283 | if (strcmp(sym->name, s->sym.name) < 0) | ||
| 284 | p = &(*p)->rb_left; | ||
| 285 | else | ||
| 286 | p = &(*p)->rb_right; | ||
| 287 | } | ||
| 288 | rb_link_node(&symn->rb_node, parent, p); | ||
| 289 | rb_insert_color(&symn->rb_node, self); | ||
| 290 | } | ||
| 291 | |||
| 292 | static void symbols__sort_by_name(struct rb_root *self, struct rb_root *source) | ||
| 293 | { | ||
| 294 | struct rb_node *nd; | ||
| 295 | |||
| 296 | for (nd = rb_first(source); nd; nd = rb_next(nd)) { | ||
| 297 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
| 298 | symbols__insert_by_name(self, pos); | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | static struct symbol *symbols__find_by_name(struct rb_root *self, const char *name) | ||
| 303 | { | ||
| 304 | struct rb_node *n; | ||
| 305 | |||
| 306 | if (self == NULL) | ||
| 307 | return NULL; | ||
| 308 | |||
| 309 | n = self->rb_node; | ||
| 310 | |||
| 311 | while (n) { | ||
| 312 | struct symbol_name_rb_node *s; | ||
| 313 | int cmp; | ||
| 314 | |||
| 315 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); | ||
| 316 | cmp = strcmp(name, s->sym.name); | ||
| 317 | |||
| 318 | if (cmp < 0) | ||
| 319 | n = n->rb_left; | ||
| 320 | else if (cmp > 0) | ||
| 321 | n = n->rb_right; | ||
| 322 | else | ||
| 323 | return &s->sym; | ||
| 324 | } | ||
| 325 | |||
| 326 | return NULL; | ||
| 327 | } | ||
| 328 | |||
| 329 | struct symbol *dso__find_symbol(struct dso *self, | ||
| 330 | enum map_type type, u64 addr) | ||
| 250 | { | 331 | { |
| 251 | return symbols__find(&self->symbols[type], addr); | 332 | return symbols__find(&self->symbols[type], addr); |
| 252 | } | 333 | } |
| 253 | 334 | ||
| 335 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | ||
| 336 | const char *name) | ||
| 337 | { | ||
| 338 | return symbols__find_by_name(&self->symbol_names[type], name); | ||
| 339 | } | ||
| 340 | |||
| 341 | void dso__sort_by_name(struct dso *self, enum map_type type) | ||
| 342 | { | ||
| 343 | dso__set_sorted_by_name(self, type); | ||
| 344 | return symbols__sort_by_name(&self->symbol_names[type], | ||
| 345 | &self->symbols[type]); | ||
| 346 | } | ||
| 347 | |||
| 254 | int build_id__sprintf(u8 *self, int len, char *bf) | 348 | int build_id__sprintf(u8 *self, int len, char *bf) |
| 255 | { | 349 | { |
| 256 | char *bid = bf; | 350 | char *bid = bf; |
| @@ -327,10 +421,7 @@ static int dso__load_all_kallsyms(struct dso *self, struct map *map) | |||
| 327 | continue; | 421 | continue; |
| 328 | 422 | ||
| 329 | symbol_type = toupper(line[len]); | 423 | symbol_type = toupper(line[len]); |
| 330 | /* | 424 | 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; | 425 | continue; |
| 335 | 426 | ||
| 336 | symbol_name = line + len + 2; | 427 | symbol_name = line + len + 2; |
| @@ -364,8 +455,8 @@ out_failure: | |||
| 364 | * kernel range is broken in several maps, named [kernel].N, as we don't have | 455 | * kernel range is broken in several maps, named [kernel].N, as we don't have |
| 365 | * the original ELF section names vmlinux have. | 456 | * the original ELF section names vmlinux have. |
| 366 | */ | 457 | */ |
| 367 | static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread *thread, | 458 | static int dso__split_kallsyms(struct dso *self, struct map *map, |
| 368 | symbol_filter_t filter) | 459 | struct map_groups *mg, symbol_filter_t filter) |
| 369 | { | 460 | { |
| 370 | struct map *curr_map = map; | 461 | struct map *curr_map = map; |
| 371 | struct symbol *pos; | 462 | struct symbol *pos; |
| @@ -382,13 +473,13 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread | |||
| 382 | 473 | ||
| 383 | module = strchr(pos->name, '\t'); | 474 | module = strchr(pos->name, '\t'); |
| 384 | if (module) { | 475 | if (module) { |
| 385 | if (!thread->use_modules) | 476 | if (!mg->use_modules) |
| 386 | goto discard_symbol; | 477 | goto discard_symbol; |
| 387 | 478 | ||
| 388 | *module++ = '\0'; | 479 | *module++ = '\0'; |
| 389 | 480 | ||
| 390 | if (strcmp(self->name, module)) { | 481 | if (strcmp(self->name, module)) { |
| 391 | curr_map = thread__find_map_by_name(thread, module); | 482 | curr_map = map_groups__find_by_name(mg, map->type, module); |
| 392 | if (curr_map == NULL) { | 483 | if (curr_map == NULL) { |
| 393 | pr_debug("/proc/{kallsyms,modules} " | 484 | pr_debug("/proc/{kallsyms,modules} " |
| 394 | "inconsistency!\n"); | 485 | "inconsistency!\n"); |
| @@ -419,7 +510,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread | |||
| 419 | } | 510 | } |
| 420 | 511 | ||
| 421 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; | 512 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; |
| 422 | __thread__insert_map(thread, curr_map); | 513 | map_groups__insert(mg, curr_map); |
| 423 | ++kernel_range; | 514 | ++kernel_range; |
| 424 | } | 515 | } |
| 425 | 516 | ||
| @@ -440,7 +531,7 @@ discard_symbol: rb_erase(&pos->rb_node, root); | |||
| 440 | 531 | ||
| 441 | 532 | ||
| 442 | static int dso__load_kallsyms(struct dso *self, struct map *map, | 533 | static int dso__load_kallsyms(struct dso *self, struct map *map, |
| 443 | struct thread *thread, symbol_filter_t filter) | 534 | struct map_groups *mg, symbol_filter_t filter) |
| 444 | { | 535 | { |
| 445 | if (dso__load_all_kallsyms(self, map) < 0) | 536 | if (dso__load_all_kallsyms(self, map) < 0) |
| 446 | return -1; | 537 | return -1; |
| @@ -448,13 +539,13 @@ static int dso__load_kallsyms(struct dso *self, struct map *map, | |||
| 448 | symbols__fixup_end(&self->symbols[map->type]); | 539 | symbols__fixup_end(&self->symbols[map->type]); |
| 449 | self->origin = DSO__ORIG_KERNEL; | 540 | self->origin = DSO__ORIG_KERNEL; |
| 450 | 541 | ||
| 451 | return dso__split_kallsyms(self, map, thread, filter); | 542 | return dso__split_kallsyms(self, map, mg, filter); |
| 452 | } | 543 | } |
| 453 | 544 | ||
| 454 | size_t kernel_maps__fprintf(FILE *fp) | 545 | size_t kernel_maps__fprintf(FILE *fp) |
| 455 | { | 546 | { |
| 456 | size_t printed = fprintf(fp, "Kernel maps:\n"); | 547 | size_t printed = fprintf(fp, "Kernel maps:\n"); |
| 457 | printed += thread__fprintf_maps(kthread, fp); | 548 | printed += map_groups__fprintf_maps(kmaps, fp); |
| 458 | return printed + fprintf(fp, "END kernel maps\n"); | 549 | return printed + fprintf(fp, "END kernel maps\n"); |
| 459 | } | 550 | } |
| 460 | 551 | ||
| @@ -544,6 +635,13 @@ static inline int elf_sym__is_function(const GElf_Sym *sym) | |||
| 544 | sym->st_shndx != SHN_UNDEF; | 635 | sym->st_shndx != SHN_UNDEF; |
| 545 | } | 636 | } |
| 546 | 637 | ||
| 638 | static inline bool elf_sym__is_object(const GElf_Sym *sym) | ||
| 639 | { | ||
| 640 | return elf_sym__type(sym) == STT_OBJECT && | ||
| 641 | sym->st_name != 0 && | ||
| 642 | sym->st_shndx != SHN_UNDEF; | ||
| 643 | } | ||
| 644 | |||
| 547 | static inline int elf_sym__is_label(const GElf_Sym *sym) | 645 | static inline int elf_sym__is_label(const GElf_Sym *sym) |
| 548 | { | 646 | { |
| 549 | return elf_sym__type(sym) == STT_NOTYPE && | 647 | return elf_sym__type(sym) == STT_NOTYPE && |
| @@ -564,6 +662,12 @@ static inline int elf_sec__is_text(const GElf_Shdr *shdr, | |||
| 564 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | 662 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; |
| 565 | } | 663 | } |
| 566 | 664 | ||
| 665 | static inline bool elf_sec__is_data(const GElf_Shdr *shdr, | ||
| 666 | const Elf_Data *secstrs) | ||
| 667 | { | ||
| 668 | return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; | ||
| 669 | } | ||
| 670 | |||
| 567 | static inline const char *elf_sym__name(const GElf_Sym *sym, | 671 | static inline const char *elf_sym__name(const GElf_Sym *sym, |
| 568 | const Elf_Data *symstrs) | 672 | const Elf_Data *symstrs) |
| 569 | { | 673 | { |
| @@ -744,8 +848,32 @@ out: | |||
| 744 | return 0; | 848 | return 0; |
| 745 | } | 849 | } |
| 746 | 850 | ||
| 851 | static bool elf_sym__is_a(GElf_Sym *self, enum map_type type) | ||
| 852 | { | ||
| 853 | switch (type) { | ||
| 854 | case MAP__FUNCTION: | ||
| 855 | return elf_sym__is_function(self); | ||
| 856 | case MAP__VARIABLE: | ||
| 857 | return elf_sym__is_object(self); | ||
| 858 | default: | ||
| 859 | return false; | ||
| 860 | } | ||
| 861 | } | ||
| 862 | |||
| 863 | static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type) | ||
| 864 | { | ||
| 865 | switch (type) { | ||
| 866 | case MAP__FUNCTION: | ||
| 867 | return elf_sec__is_text(self, secstrs); | ||
| 868 | case MAP__VARIABLE: | ||
| 869 | return elf_sec__is_data(self, secstrs); | ||
| 870 | default: | ||
| 871 | return false; | ||
| 872 | } | ||
| 873 | } | ||
| 874 | |||
| 747 | static int dso__load_sym(struct dso *self, struct map *map, | 875 | static int dso__load_sym(struct dso *self, struct map *map, |
| 748 | struct thread *thread, const char *name, int fd, | 876 | struct map_groups *mg, const char *name, int fd, |
| 749 | symbol_filter_t filter, int kernel, int kmodule) | 877 | symbol_filter_t filter, int kernel, int kmodule) |
| 750 | { | 878 | { |
| 751 | struct map *curr_map = map; | 879 | struct map *curr_map = map; |
| @@ -818,7 +946,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
| 818 | int is_label = elf_sym__is_label(&sym); | 946 | int is_label = elf_sym__is_label(&sym); |
| 819 | const char *section_name; | 947 | const char *section_name; |
| 820 | 948 | ||
| 821 | if (!is_label && !elf_sym__is_function(&sym)) | 949 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
| 822 | continue; | 950 | continue; |
| 823 | 951 | ||
| 824 | sec = elf_getscn(elf, sym.st_shndx); | 952 | sec = elf_getscn(elf, sym.st_shndx); |
| @@ -827,7 +955,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
| 827 | 955 | ||
| 828 | gelf_getshdr(sec, &shdr); | 956 | gelf_getshdr(sec, &shdr); |
| 829 | 957 | ||
| 830 | if (is_label && !elf_sec__is_text(&shdr, secstrs)) | 958 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) |
| 831 | continue; | 959 | continue; |
| 832 | 960 | ||
| 833 | elf_name = elf_sym__name(&sym, symstrs); | 961 | elf_name = elf_sym__name(&sym, symstrs); |
| @@ -849,7 +977,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
| 849 | snprintf(dso_name, sizeof(dso_name), | 977 | snprintf(dso_name, sizeof(dso_name), |
| 850 | "%s%s", self->short_name, section_name); | 978 | "%s%s", self->short_name, section_name); |
| 851 | 979 | ||
| 852 | curr_map = thread__find_map_by_name(thread, dso_name); | 980 | curr_map = map_groups__find_by_name(mg, map->type, dso_name); |
| 853 | if (curr_map == NULL) { | 981 | if (curr_map == NULL) { |
| 854 | u64 start = sym.st_value; | 982 | u64 start = sym.st_value; |
| 855 | 983 | ||
| @@ -868,7 +996,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
| 868 | curr_map->map_ip = identity__map_ip; | 996 | curr_map->map_ip = identity__map_ip; |
| 869 | curr_map->unmap_ip = identity__map_ip; | 997 | curr_map->unmap_ip = identity__map_ip; |
| 870 | curr_dso->origin = DSO__ORIG_KERNEL; | 998 | curr_dso->origin = DSO__ORIG_KERNEL; |
| 871 | __thread__insert_map(kthread, curr_map); | 999 | map_groups__insert(kmaps, curr_map); |
| 872 | dsos__add(&dsos__kernel, curr_dso); | 1000 | dsos__add(&dsos__kernel, curr_dso); |
| 873 | } else | 1001 | } else |
| 874 | curr_dso = curr_map->dso; | 1002 | curr_dso = curr_map->dso; |
| @@ -938,8 +1066,9 @@ static bool __dsos__read_build_ids(struct list_head *head) | |||
| 938 | 1066 | ||
| 939 | bool dsos__read_build_ids(void) | 1067 | bool dsos__read_build_ids(void) |
| 940 | { | 1068 | { |
| 941 | return __dsos__read_build_ids(&dsos__kernel) || | 1069 | bool kbuildids = __dsos__read_build_ids(&dsos__kernel), |
| 942 | __dsos__read_build_ids(&dsos__user); | 1070 | ubuildids = __dsos__read_build_ids(&dsos__user); |
| 1071 | return kbuildids || ubuildids; | ||
| 943 | } | 1072 | } |
| 944 | 1073 | ||
| 945 | /* | 1074 | /* |
| @@ -1093,7 +1222,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
| 1093 | dso__set_loaded(self, map->type); | 1222 | dso__set_loaded(self, map->type); |
| 1094 | 1223 | ||
| 1095 | if (self->kernel) | 1224 | if (self->kernel) |
| 1096 | return dso__load_kernel_sym(self, map, kthread, filter); | 1225 | return dso__load_kernel_sym(self, map, kmaps, filter); |
| 1097 | 1226 | ||
| 1098 | name = malloc(size); | 1227 | name = malloc(size); |
| 1099 | if (!name) | 1228 | if (!name) |
| @@ -1179,11 +1308,12 @@ out: | |||
| 1179 | return ret; | 1308 | return ret; |
| 1180 | } | 1309 | } |
| 1181 | 1310 | ||
| 1182 | static struct map *thread__find_map_by_name(struct thread *self, char *name) | 1311 | struct map *map_groups__find_by_name(struct map_groups *self, |
| 1312 | enum map_type type, const char *name) | ||
| 1183 | { | 1313 | { |
| 1184 | struct rb_node *nd; | 1314 | struct rb_node *nd; |
| 1185 | 1315 | ||
| 1186 | for (nd = rb_first(&self->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { | 1316 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { |
| 1187 | struct map *map = rb_entry(nd, struct map, rb_node); | 1317 | struct map *map = rb_entry(nd, struct map, rb_node); |
| 1188 | 1318 | ||
| 1189 | if (map->dso && strcmp(map->dso->name, name) == 0) | 1319 | if (map->dso && strcmp(map->dso->name, name) == 0) |
| @@ -1227,7 +1357,7 @@ static int dsos__set_modules_path_dir(char *dirname) | |||
| 1227 | (int)(dot - dent->d_name), dent->d_name); | 1357 | (int)(dot - dent->d_name), dent->d_name); |
| 1228 | 1358 | ||
| 1229 | strxfrchar(dso_name, '-', '_'); | 1359 | strxfrchar(dso_name, '-', '_'); |
| 1230 | map = thread__find_map_by_name(kthread, dso_name); | 1360 | map = map_groups__find_by_name(kmaps, MAP__FUNCTION, dso_name); |
| 1231 | if (map == NULL) | 1361 | if (map == NULL) |
| 1232 | continue; | 1362 | continue; |
| 1233 | 1363 | ||
| @@ -1280,7 +1410,7 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | |||
| 1280 | return self; | 1410 | return self; |
| 1281 | } | 1411 | } |
| 1282 | 1412 | ||
| 1283 | static int thread__create_module_maps(struct thread *self) | 1413 | static int map_groups__create_module_maps(struct map_groups *self) |
| 1284 | { | 1414 | { |
| 1285 | char *line = NULL; | 1415 | char *line = NULL; |
| 1286 | size_t n; | 1416 | size_t n; |
| @@ -1337,7 +1467,7 @@ static int thread__create_module_maps(struct thread *self) | |||
| 1337 | dso->has_build_id = true; | 1467 | dso->has_build_id = true; |
| 1338 | 1468 | ||
| 1339 | dso->origin = DSO__ORIG_KMODULE; | 1469 | dso->origin = DSO__ORIG_KMODULE; |
| 1340 | __thread__insert_map(self, map); | 1470 | map_groups__insert(self, map); |
| 1341 | dsos__add(&dsos__kernel, dso); | 1471 | dsos__add(&dsos__kernel, dso); |
| 1342 | } | 1472 | } |
| 1343 | 1473 | ||
| @@ -1352,7 +1482,8 @@ out_failure: | |||
| 1352 | return -1; | 1482 | return -1; |
| 1353 | } | 1483 | } |
| 1354 | 1484 | ||
| 1355 | static int dso__load_vmlinux(struct dso *self, struct map *map, struct thread *thread, | 1485 | static int dso__load_vmlinux(struct dso *self, struct map *map, |
| 1486 | struct map_groups *mg, | ||
| 1356 | const char *vmlinux, symbol_filter_t filter) | 1487 | const char *vmlinux, symbol_filter_t filter) |
| 1357 | { | 1488 | { |
| 1358 | int err = -1, fd; | 1489 | int err = -1, fd; |
| @@ -1386,14 +1517,14 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, struct thread *t | |||
| 1386 | return -1; | 1517 | return -1; |
| 1387 | 1518 | ||
| 1388 | dso__set_loaded(self, map->type); | 1519 | dso__set_loaded(self, map->type); |
| 1389 | err = dso__load_sym(self, map, thread, self->long_name, fd, filter, 1, 0); | 1520 | err = dso__load_sym(self, map, mg, self->long_name, fd, filter, 1, 0); |
| 1390 | close(fd); | 1521 | close(fd); |
| 1391 | 1522 | ||
| 1392 | return err; | 1523 | return err; |
| 1393 | } | 1524 | } |
| 1394 | 1525 | ||
| 1395 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 1526 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
| 1396 | struct thread *thread, symbol_filter_t filter) | 1527 | struct map_groups *mg, symbol_filter_t filter) |
| 1397 | { | 1528 | { |
| 1398 | int err; | 1529 | int err; |
| 1399 | bool is_kallsyms; | 1530 | bool is_kallsyms; |
| @@ -1403,7 +1534,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", | 1534 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", |
| 1404 | vmlinux_path__nr_entries); | 1535 | vmlinux_path__nr_entries); |
| 1405 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | 1536 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { |
| 1406 | err = dso__load_vmlinux(self, map, thread, | 1537 | err = dso__load_vmlinux(self, map, mg, |
| 1407 | vmlinux_path[i], filter); | 1538 | vmlinux_path[i], filter); |
| 1408 | if (err > 0) { | 1539 | if (err > 0) { |
| 1409 | pr_debug("Using %s for symbols\n", | 1540 | pr_debug("Using %s for symbols\n", |
| @@ -1419,12 +1550,12 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, | |||
| 1419 | if (is_kallsyms) | 1550 | if (is_kallsyms) |
| 1420 | goto do_kallsyms; | 1551 | goto do_kallsyms; |
| 1421 | 1552 | ||
| 1422 | err = dso__load_vmlinux(self, map, thread, self->long_name, filter); | 1553 | err = dso__load_vmlinux(self, map, mg, self->long_name, filter); |
| 1423 | if (err <= 0) { | 1554 | if (err <= 0) { |
| 1424 | pr_info("The file %s cannot be used, " | 1555 | pr_info("The file %s cannot be used, " |
| 1425 | "trying to use /proc/kallsyms...", self->long_name); | 1556 | "trying to use /proc/kallsyms...", self->long_name); |
| 1426 | do_kallsyms: | 1557 | do_kallsyms: |
| 1427 | err = dso__load_kallsyms(self, map, thread, filter); | 1558 | err = dso__load_kallsyms(self, map, mg, filter); |
| 1428 | if (err > 0 && !is_kallsyms) | 1559 | if (err > 0 && !is_kallsyms) |
| 1429 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); | 1560 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); |
| 1430 | } | 1561 | } |
| @@ -1507,42 +1638,59 @@ size_t dsos__fprintf_buildid(FILE *fp) | |||
| 1507 | __dsos__fprintf_buildid(&dsos__user, fp)); | 1638 | __dsos__fprintf_buildid(&dsos__user, fp)); |
| 1508 | } | 1639 | } |
| 1509 | 1640 | ||
| 1510 | static int thread__create_kernel_map(struct thread *self, const char *vmlinux) | 1641 | static struct dso *dsos__create_kernel( const char *vmlinux) |
| 1511 | { | 1642 | { |
| 1512 | struct map *kmap; | ||
| 1513 | struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]"); | 1643 | struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]"); |
| 1514 | 1644 | ||
| 1515 | if (kernel == NULL) | 1645 | if (kernel == NULL) |
| 1516 | return -1; | 1646 | return NULL; |
| 1517 | |||
| 1518 | kmap = map__new2(0, kernel, MAP__FUNCTION); | ||
| 1519 | if (kmap == NULL) | ||
| 1520 | goto out_delete_kernel_dso; | ||
| 1521 | 1647 | ||
| 1522 | kmap->map_ip = kmap->unmap_ip = identity__map_ip; | ||
| 1523 | kernel->short_name = "[kernel]"; | 1648 | kernel->short_name = "[kernel]"; |
| 1524 | kernel->kernel = 1; | 1649 | kernel->kernel = 1; |
| 1525 | 1650 | ||
| 1526 | vdso = dso__new("[vdso]"); | 1651 | vdso = dso__new("[vdso]"); |
| 1527 | if (vdso == NULL) | 1652 | if (vdso == NULL) |
| 1528 | goto out_delete_kernel_map; | 1653 | goto out_delete_kernel_dso; |
| 1529 | dso__set_loaded(vdso, MAP__FUNCTION); | 1654 | dso__set_loaded(vdso, MAP__FUNCTION); |
| 1530 | 1655 | ||
| 1531 | if (sysfs__read_build_id("/sys/kernel/notes", kernel->build_id, | 1656 | if (sysfs__read_build_id("/sys/kernel/notes", kernel->build_id, |
| 1532 | sizeof(kernel->build_id)) == 0) | 1657 | sizeof(kernel->build_id)) == 0) |
| 1533 | kernel->has_build_id = true; | 1658 | kernel->has_build_id = true; |
| 1534 | 1659 | ||
| 1535 | __thread__insert_map(self, kmap); | ||
| 1536 | dsos__add(&dsos__kernel, kernel); | 1660 | dsos__add(&dsos__kernel, kernel); |
| 1537 | dsos__add(&dsos__user, vdso); | 1661 | dsos__add(&dsos__user, vdso); |
| 1538 | 1662 | ||
| 1539 | return 0; | 1663 | return kernel; |
| 1540 | 1664 | ||
| 1541 | out_delete_kernel_map: | ||
| 1542 | map__delete(kmap); | ||
| 1543 | out_delete_kernel_dso: | 1665 | out_delete_kernel_dso: |
| 1544 | dso__delete(kernel); | 1666 | dso__delete(kernel); |
| 1545 | return -1; | 1667 | return NULL; |
| 1668 | } | ||
| 1669 | |||
| 1670 | static int map_groups__create_kernel_maps(struct map_groups *self, const char *vmlinux) | ||
| 1671 | { | ||
| 1672 | struct map *functions, *variables; | ||
| 1673 | struct dso *kernel = dsos__create_kernel(vmlinux); | ||
| 1674 | |||
| 1675 | if (kernel == NULL) | ||
| 1676 | return -1; | ||
| 1677 | |||
| 1678 | functions = map__new2(0, kernel, MAP__FUNCTION); | ||
| 1679 | if (functions == NULL) | ||
| 1680 | return -1; | ||
| 1681 | |||
| 1682 | variables = map__new2(0, kernel, MAP__VARIABLE); | ||
| 1683 | if (variables == NULL) { | ||
| 1684 | map__delete(functions); | ||
| 1685 | return -1; | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | functions->map_ip = functions->unmap_ip = | ||
| 1689 | variables->map_ip = variables->unmap_ip = identity__map_ip; | ||
| 1690 | map_groups__insert(self, functions); | ||
| 1691 | map_groups__insert(self, variables); | ||
| 1692 | |||
| 1693 | return 0; | ||
| 1546 | } | 1694 | } |
| 1547 | 1695 | ||
| 1548 | static void vmlinux_path__exit(void) | 1696 | static void vmlinux_path__exit(void) |
| @@ -1606,23 +1754,26 @@ int symbol__init(struct symbol_conf *conf) | |||
| 1606 | 1754 | ||
| 1607 | elf_version(EV_CURRENT); | 1755 | elf_version(EV_CURRENT); |
| 1608 | symbol__priv_size = pconf->priv_size; | 1756 | symbol__priv_size = pconf->priv_size; |
| 1609 | thread__init(kthread, 0); | 1757 | if (pconf->sort_by_name) |
| 1758 | symbol__priv_size += (sizeof(struct symbol_name_rb_node) - | ||
| 1759 | sizeof(struct symbol)); | ||
| 1760 | map_groups__init(kmaps); | ||
| 1610 | 1761 | ||
| 1611 | if (pconf->try_vmlinux_path && vmlinux_path__init() < 0) | 1762 | if (pconf->try_vmlinux_path && vmlinux_path__init() < 0) |
| 1612 | return -1; | 1763 | return -1; |
| 1613 | 1764 | ||
| 1614 | if (thread__create_kernel_map(kthread, pconf->vmlinux_name) < 0) { | 1765 | if (map_groups__create_kernel_maps(kmaps, pconf->vmlinux_name) < 0) { |
| 1615 | vmlinux_path__exit(); | 1766 | vmlinux_path__exit(); |
| 1616 | return -1; | 1767 | return -1; |
| 1617 | } | 1768 | } |
| 1618 | 1769 | ||
| 1619 | kthread->use_modules = pconf->use_modules; | 1770 | kmaps->use_modules = pconf->use_modules; |
| 1620 | if (pconf->use_modules && thread__create_module_maps(kthread) < 0) | 1771 | if (pconf->use_modules && map_groups__create_module_maps(kmaps) < 0) |
| 1621 | pr_debug("Failed to load list of modules in use, " | 1772 | pr_debug("Failed to load list of modules in use, " |
| 1622 | "continuing...\n"); | 1773 | "continuing...\n"); |
| 1623 | /* | 1774 | /* |
| 1624 | * Now that we have all the maps created, just set the ->end of them: | 1775 | * Now that we have all the maps created, just set the ->end of them: |
| 1625 | */ | 1776 | */ |
| 1626 | thread__fixup_maps_end(kthread); | 1777 | map_groups__fixup_end(kmaps); |
| 1627 | return 0; | 1778 | return 0; |
| 1628 | } | 1779 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 17003efa0b39..cf99f88adf39 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -52,7 +52,8 @@ struct symbol { | |||
| 52 | struct symbol_conf { | 52 | struct symbol_conf { |
| 53 | unsigned short priv_size; | 53 | unsigned short priv_size; |
| 54 | bool try_vmlinux_path, | 54 | bool try_vmlinux_path, |
| 55 | use_modules; | 55 | use_modules, |
| 56 | sort_by_name; | ||
| 56 | const char *vmlinux_name; | 57 | const char *vmlinux_name; |
| 57 | }; | 58 | }; |
| 58 | 59 | ||
| @@ -74,13 +75,13 @@ struct addr_location { | |||
| 74 | struct dso { | 75 | struct dso { |
| 75 | struct list_head node; | 76 | struct list_head node; |
| 76 | struct rb_root symbols[MAP__NR_TYPES]; | 77 | struct rb_root symbols[MAP__NR_TYPES]; |
| 77 | struct symbol *(*find_symbol)(struct dso *self, | 78 | struct rb_root symbol_names[MAP__NR_TYPES]; |
| 78 | enum map_type type, u64 addr); | ||
| 79 | u8 adjust_symbols:1; | 79 | u8 adjust_symbols:1; |
| 80 | u8 slen_calculated:1; | 80 | u8 slen_calculated:1; |
| 81 | u8 has_build_id:1; | 81 | u8 has_build_id:1; |
| 82 | u8 kernel:1; | 82 | u8 kernel:1; |
| 83 | unsigned char origin; | 83 | unsigned char origin; |
| 84 | u8 sorted_by_name; | ||
| 84 | u8 loaded; | 85 | u8 loaded; |
| 85 | u8 build_id[BUILD_ID_SIZE]; | 86 | u8 build_id[BUILD_ID_SIZE]; |
| 86 | u16 long_name_len; | 87 | u16 long_name_len; |
| @@ -93,6 +94,9 @@ struct dso *dso__new(const char *name); | |||
| 93 | void dso__delete(struct dso *self); | 94 | void dso__delete(struct dso *self); |
| 94 | 95 | ||
| 95 | bool dso__loaded(const struct dso *self, enum map_type type); | 96 | bool dso__loaded(const struct dso *self, enum map_type type); |
| 97 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); | ||
| 98 | |||
| 99 | void dso__sort_by_name(struct dso *self, enum map_type type); | ||
| 96 | 100 | ||
| 97 | struct dso *dsos__findnew(const char *name); | 101 | struct dso *dsos__findnew(const char *name); |
| 98 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); | 102 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); |
| @@ -103,6 +107,9 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp); | |||
| 103 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); | 107 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); |
| 104 | char dso__symtab_origin(const struct dso *self); | 108 | char dso__symtab_origin(const struct dso *self); |
| 105 | void dso__set_build_id(struct dso *self, void *build_id); | 109 | void dso__set_build_id(struct dso *self, void *build_id); |
| 110 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); | ||
| 111 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | ||
| 112 | const char *name); | ||
| 106 | 113 | ||
| 107 | int filename__read_build_id(const char *filename, void *bf, size_t size); | 114 | 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); | 115 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); |
| @@ -113,8 +120,8 @@ size_t kernel_maps__fprintf(FILE *fp); | |||
| 113 | 120 | ||
| 114 | int symbol__init(struct symbol_conf *conf); | 121 | int symbol__init(struct symbol_conf *conf); |
| 115 | 122 | ||
| 116 | struct thread; | 123 | struct map_groups; |
| 117 | struct thread *kthread; | 124 | struct map_groups *kmaps; |
| 118 | extern struct list_head dsos__user, dsos__kernel; | 125 | extern struct list_head dsos__user, dsos__kernel; |
| 119 | extern struct dso *vdso; | 126 | extern struct dso *vdso; |
| 120 | #endif /* __PERF_SYMBOL */ | 127 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 603f5610861b..b68a00ea4121 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -9,11 +9,9 @@ | |||
| 9 | static struct rb_root threads; | 9 | static struct rb_root threads; |
| 10 | static struct thread *last_match; | 10 | static struct thread *last_match; |
| 11 | 11 | ||
| 12 | void thread__init(struct thread *self, pid_t pid) | 12 | void map_groups__init(struct map_groups *self) |
| 13 | { | 13 | { |
| 14 | int i; | 14 | int i; |
| 15 | self->pid = pid; | ||
| 16 | self->comm = NULL; | ||
| 17 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 15 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
| 18 | self->maps[i] = RB_ROOT; | 16 | self->maps[i] = RB_ROOT; |
| 19 | INIT_LIST_HEAD(&self->removed_maps[i]); | 17 | INIT_LIST_HEAD(&self->removed_maps[i]); |
| @@ -25,7 +23,8 @@ static struct thread *thread__new(pid_t pid) | |||
| 25 | struct thread *self = zalloc(sizeof(*self)); | 23 | struct thread *self = zalloc(sizeof(*self)); |
| 26 | 24 | ||
| 27 | if (self != NULL) { | 25 | if (self != NULL) { |
| 28 | thread__init(self, pid); | 26 | map_groups__init(&self->mg); |
| 27 | self->pid = pid; | ||
| 29 | self->comm = malloc(32); | 28 | self->comm = malloc(32); |
| 30 | if (self->comm) | 29 | if (self->comm) |
| 31 | snprintf(self->comm, 32, ":%d", self->pid); | 30 | snprintf(self->comm, 32, ":%d", self->pid); |
| @@ -55,10 +54,11 @@ int thread__comm_len(struct thread *self) | |||
| 55 | 54 | ||
| 56 | static const char *map_type__name[MAP__NR_TYPES] = { | 55 | static const char *map_type__name[MAP__NR_TYPES] = { |
| 57 | [MAP__FUNCTION] = "Functions", | 56 | [MAP__FUNCTION] = "Functions", |
| 57 | [MAP__VARIABLE] = "Variables", | ||
| 58 | }; | 58 | }; |
| 59 | 59 | ||
| 60 | static size_t __thread__fprintf_maps(struct thread *self, | 60 | static size_t __map_groups__fprintf_maps(struct map_groups *self, |
| 61 | enum map_type type, FILE *fp) | 61 | enum map_type type, FILE *fp) |
| 62 | { | 62 | { |
| 63 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); | 63 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); |
| 64 | struct rb_node *nd; | 64 | struct rb_node *nd; |
| @@ -76,16 +76,16 @@ static size_t __thread__fprintf_maps(struct thread *self, | |||
| 76 | return printed; | 76 | return printed; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | size_t thread__fprintf_maps(struct thread *self, FILE *fp) | 79 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp) |
| 80 | { | 80 | { |
| 81 | size_t printed = 0, i; | 81 | size_t printed = 0, i; |
| 82 | for (i = 0; i < MAP__NR_TYPES; ++i) | 82 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 83 | printed += __thread__fprintf_maps(self, i, fp); | 83 | printed += __map_groups__fprintf_maps(self, i, fp); |
| 84 | return printed; | 84 | return printed; |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | static size_t __thread__fprintf_removed_maps(struct thread *self, | 87 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, |
| 88 | enum map_type type, FILE *fp) | 88 | enum map_type type, FILE *fp) |
| 89 | { | 89 | { |
| 90 | struct map *pos; | 90 | struct map *pos; |
| 91 | size_t printed = 0; | 91 | size_t printed = 0; |
| @@ -101,20 +101,25 @@ static size_t __thread__fprintf_removed_maps(struct thread *self, | |||
| 101 | return printed; | 101 | return printed; |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | static size_t thread__fprintf_removed_maps(struct thread *self, FILE *fp) | 104 | static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp) |
| 105 | { | 105 | { |
| 106 | size_t printed = 0, i; | 106 | size_t printed = 0, i; |
| 107 | for (i = 0; i < MAP__NR_TYPES; ++i) | 107 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 108 | printed += __thread__fprintf_removed_maps(self, i, fp); | 108 | printed += __map_groups__fprintf_removed_maps(self, i, fp); |
| 109 | return printed; | 109 | return printed; |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 112 | static size_t map_groups__fprintf(struct map_groups *self, FILE *fp) |
| 113 | { | 113 | { |
| 114 | size_t printed = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); | 114 | size_t printed = map_groups__fprintf_maps(self, fp); |
| 115 | printed += thread__fprintf_removed_maps(self, fp); | ||
| 116 | printed += fprintf(fp, "Removed maps:\n"); | 115 | printed += fprintf(fp, "Removed maps:\n"); |
| 117 | return printed + thread__fprintf_removed_maps(self, fp); | 116 | return printed + map_groups__fprintf_removed_maps(self, fp); |
| 117 | } | ||
| 118 | |||
| 119 | static size_t thread__fprintf(struct thread *self, FILE *fp) | ||
| 120 | { | ||
| 121 | return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + | ||
| 122 | map_groups__fprintf(&self->mg, fp); | ||
| 118 | } | 123 | } |
| 119 | 124 | ||
| 120 | struct thread *threads__findnew(pid_t pid) | 125 | struct thread *threads__findnew(pid_t pid) |
| @@ -168,7 +173,8 @@ struct thread *register_idle_thread(void) | |||
| 168 | return thread; | 173 | return thread; |
| 169 | } | 174 | } |
| 170 | 175 | ||
| 171 | static void thread__remove_overlappings(struct thread *self, struct map *map) | 176 | static void map_groups__remove_overlappings(struct map_groups *self, |
| 177 | struct map *map) | ||
| 172 | { | 178 | { |
| 173 | struct rb_root *root = &self->maps[map->type]; | 179 | struct rb_root *root = &self->maps[map->type]; |
| 174 | struct rb_node *next = rb_first(root); | 180 | struct rb_node *next = rb_first(root); |
| @@ -238,12 +244,15 @@ struct map *maps__find(struct rb_root *maps, u64 ip) | |||
| 238 | 244 | ||
| 239 | void thread__insert_map(struct thread *self, struct map *map) | 245 | void thread__insert_map(struct thread *self, struct map *map) |
| 240 | { | 246 | { |
| 241 | thread__remove_overlappings(self, map); | 247 | map_groups__remove_overlappings(&self->mg, map); |
| 242 | maps__insert(&self->maps[map->type], map); | 248 | map_groups__insert(&self->mg, map); |
| 243 | } | 249 | } |
| 244 | 250 | ||
| 245 | static int thread__clone_maps(struct thread *self, struct thread *parent, | 251 | /* |
| 246 | enum map_type type) | 252 | * XXX This should not really _copy_ te maps, but refcount them. |
| 253 | */ | ||
| 254 | static int map_groups__clone(struct map_groups *self, | ||
| 255 | struct map_groups *parent, enum map_type type) | ||
| 247 | { | 256 | { |
| 248 | struct rb_node *nd; | 257 | struct rb_node *nd; |
| 249 | for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { | 258 | for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { |
| @@ -251,7 +260,7 @@ static int thread__clone_maps(struct thread *self, struct thread *parent, | |||
| 251 | struct map *new = map__clone(map); | 260 | struct map *new = map__clone(map); |
| 252 | if (new == NULL) | 261 | if (new == NULL) |
| 253 | return -ENOMEM; | 262 | return -ENOMEM; |
| 254 | thread__insert_map(self, new); | 263 | map_groups__insert(self, new); |
| 255 | } | 264 | } |
| 256 | return 0; | 265 | return 0; |
| 257 | } | 266 | } |
| @@ -267,7 +276,7 @@ int thread__fork(struct thread *self, struct thread *parent) | |||
| 267 | return -ENOMEM; | 276 | return -ENOMEM; |
| 268 | 277 | ||
| 269 | for (i = 0; i < MAP__NR_TYPES; ++i) | 278 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 270 | if (thread__clone_maps(self, parent, i) < 0) | 279 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) |
| 271 | return -ENOMEM; | 280 | return -ENOMEM; |
| 272 | return 0; | 281 | return 0; |
| 273 | } | 282 | } |
| @@ -286,11 +295,11 @@ size_t threads__fprintf(FILE *fp) | |||
| 286 | return ret; | 295 | return ret; |
| 287 | } | 296 | } |
| 288 | 297 | ||
| 289 | struct symbol *thread__find_symbol(struct thread *self, | 298 | struct symbol *map_groups__find_symbol(struct map_groups *self, |
| 290 | enum map_type type, u64 addr, | 299 | enum map_type type, u64 addr, |
| 291 | symbol_filter_t filter) | 300 | symbol_filter_t filter) |
| 292 | { | 301 | { |
| 293 | struct map *map = thread__find_map(self, type, addr); | 302 | struct map *map = map_groups__find(self, type, addr); |
| 294 | 303 | ||
| 295 | if (map != NULL) | 304 | if (map != NULL) |
| 296 | return map__find_symbol(map, map->map_ip(map, addr), filter); | 305 | return map__find_symbol(map, map->map_ip(map, addr), filter); |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 686d6e914d9e..1751802a09ba 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]; |
| 12 | pid_t pid; | ||
| 13 | bool use_modules; | 11 | bool use_modules; |
| 12 | }; | ||
| 13 | |||
| 14 | struct thread { | ||
| 15 | struct rb_node rb_node; | ||
| 16 | struct map_groups mg; | ||
| 17 | pid_t pid; | ||
| 14 | char shortname[3]; | 18 | char shortname[3]; |
| 15 | char *comm; | 19 | char *comm; |
| 16 | int comm_len; | 20 | int comm_len; |
| 17 | }; | 21 | }; |
| 18 | 22 | ||
| 19 | void thread__init(struct thread *self, pid_t pid); | 23 | void map_groups__init(struct map_groups *self); |
| 20 | int thread__set_comm(struct thread *self, const char *comm); | 24 | int thread__set_comm(struct thread *self, const char *comm); |
| 21 | int thread__comm_len(struct thread *self); | 25 | int thread__comm_len(struct thread *self); |
| 22 | struct thread *threads__findnew(pid_t pid); | 26 | struct thread *threads__findnew(pid_t pid); |
| 23 | struct thread *register_idle_thread(void); | 27 | struct thread *register_idle_thread(void); |
| 24 | void thread__insert_map(struct thread *self, struct map *map); | 28 | void thread__insert_map(struct thread *self, struct map *map); |
| 25 | int thread__fork(struct thread *self, struct thread *parent); | 29 | int thread__fork(struct thread *self, struct thread *parent); |
| 26 | size_t thread__fprintf_maps(struct thread *self, FILE *fp); | 30 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); |
| 27 | size_t threads__fprintf(FILE *fp); | 31 | size_t threads__fprintf(FILE *fp); |
| 28 | 32 | ||
| 29 | void maps__insert(struct rb_root *maps, struct map *map); | 33 | void maps__insert(struct rb_root *maps, struct map *map); |
| 30 | struct map *maps__find(struct rb_root *maps, u64 addr); | 34 | struct map *maps__find(struct rb_root *maps, u64 addr); |
| 31 | 35 | ||
| 32 | static inline struct map *thread__find_map(struct thread *self, | 36 | static inline void map_groups__insert(struct map_groups *self, struct map *map) |
| 37 | { | ||
| 38 | maps__insert(&self->maps[map->type], map); | ||
| 39 | } | ||
| 40 | |||
| 41 | static inline struct map *map_groups__find(struct map_groups *self, | ||
| 33 | enum map_type type, u64 addr) | 42 | enum map_type type, u64 addr) |
| 34 | { | 43 | { |
| 35 | return self ? maps__find(&self->maps[type], addr) : NULL; | 44 | return maps__find(&self->maps[type], addr); |
| 36 | } | 45 | } |
| 37 | 46 | ||
| 38 | static inline void __thread__insert_map(struct thread *self, struct map *map) | 47 | static inline struct map *thread__find_map(struct thread *self, |
| 48 | enum map_type type, u64 addr) | ||
| 39 | { | 49 | { |
| 40 | maps__insert(&self->maps[map->type], map); | 50 | return self ? map_groups__find(&self->mg, type, addr) : NULL; |
| 41 | } | 51 | } |
| 42 | 52 | ||
| 43 | void thread__find_addr_location(struct thread *self, u8 cpumode, | 53 | void thread__find_addr_location(struct thread *self, u8 cpumode, |
| 44 | enum map_type type, u64 addr, | 54 | enum map_type type, u64 addr, |
| 45 | struct addr_location *al, | 55 | struct addr_location *al, |
| 46 | symbol_filter_t filter); | 56 | symbol_filter_t filter); |
| 47 | struct symbol *thread__find_symbol(struct thread *self, | 57 | struct symbol *map_groups__find_symbol(struct map_groups *self, |
| 48 | enum map_type type, u64 addr, | 58 | enum map_type type, u64 addr, |
| 49 | symbol_filter_t filter); | 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, u64 addr, |
| 63 | symbol_filter_t filter) | ||
| 53 | { | 64 | { |
| 54 | return thread__find_symbol(self, MAP__FUNCTION, addr, filter); | 65 | return map_groups__find_symbol(self, 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..a5ffe60db5d6 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__; |
| @@ -573,26 +570,72 @@ struct scripting_ops perl_scripting_ops = { | |||
| 573 | .generate_script = perl_generate_script, | 570 | .generate_script = perl_generate_script, |
| 574 | }; | 571 | }; |
| 575 | 572 | ||
| 576 | #ifdef NO_LIBPERL | 573 | static void print_unsupported_msg(void) |
| 577 | void setup_perl_scripting(void) | ||
| 578 | { | 574 | { |
| 579 | fprintf(stderr, "Perl scripting not supported." | 575 | fprintf(stderr, "Perl scripting not supported." |
| 580 | " Install libperl and rebuild perf to enable it. e.g. " | 576 | " Install libperl and rebuild perf to enable it.\n" |
| 581 | "apt-get install libperl-dev (ubuntu), yum install " | 577 | "For example:\n # apt-get install libperl-dev (ubuntu)" |
| 582 | "perl-ExtUtils-Embed (Fedora), etc.\n"); | 578 | "\n # yum install perl-ExtUtils-Embed (Fedora)" |
| 579 | "\n etc.\n"); | ||
| 583 | } | 580 | } |
| 584 | #else | 581 | |
| 585 | void setup_perl_scripting(void) | 582 | static int perl_start_script_unsupported(const char *script __unused) |
| 583 | { | ||
| 584 | print_unsupported_msg(); | ||
| 585 | |||
| 586 | return -1; | ||
| 587 | } | ||
| 588 | |||
| 589 | static int perl_stop_script_unsupported(void) | ||
| 590 | { | ||
| 591 | return 0; | ||
| 592 | } | ||
| 593 | |||
| 594 | static void perl_process_event_unsupported(int cpu __unused, | ||
| 595 | void *data __unused, | ||
| 596 | int size __unused, | ||
| 597 | unsigned long long nsecs __unused, | ||
| 598 | char *comm __unused) | ||
| 599 | { | ||
| 600 | } | ||
| 601 | |||
| 602 | static int perl_generate_script_unsupported(const char *outfile __unused) | ||
| 603 | { | ||
| 604 | print_unsupported_msg(); | ||
| 605 | |||
| 606 | return -1; | ||
| 607 | } | ||
| 608 | |||
| 609 | struct scripting_ops perl_scripting_unsupported_ops = { | ||
| 610 | .name = "Perl", | ||
| 611 | .start_script = perl_start_script_unsupported, | ||
| 612 | .stop_script = perl_stop_script_unsupported, | ||
| 613 | .process_event = perl_process_event_unsupported, | ||
| 614 | .generate_script = perl_generate_script_unsupported, | ||
| 615 | }; | ||
| 616 | |||
| 617 | static void register_perl_scripting(struct scripting_ops *scripting_ops) | ||
| 586 | { | 618 | { |
| 587 | int err; | 619 | int err; |
| 588 | err = script_spec_register("Perl", &perl_scripting_ops); | 620 | err = script_spec_register("Perl", scripting_ops); |
| 589 | if (err) | 621 | if (err) |
| 590 | die("error registering Perl script extension"); | 622 | die("error registering Perl script extension"); |
| 591 | 623 | ||
| 592 | err = script_spec_register("pl", &perl_scripting_ops); | 624 | err = script_spec_register("pl", scripting_ops); |
| 593 | if (err) | 625 | if (err) |
| 594 | die("error registering pl script extension"); | 626 | die("error registering pl script extension"); |
| 595 | 627 | ||
| 596 | scripting_context = malloc(sizeof(struct scripting_context)); | 628 | scripting_context = malloc(sizeof(struct scripting_context)); |
| 597 | } | 629 | } |
| 630 | |||
| 631 | #ifdef NO_LIBPERL | ||
| 632 | void setup_perl_scripting(void) | ||
| 633 | { | ||
| 634 | register_perl_scripting(&perl_scripting_unsupported_ops); | ||
| 635 | } | ||
| 636 | #else | ||
| 637 | void setup_perl_scripting(void) | ||
| 638 | { | ||
| 639 | register_perl_scripting(&perl_scripting_ops); | ||
| 640 | } | ||
| 598 | #endif | 641 | #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 | ||
