aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2013-08-07 07:38:51 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2013-08-07 16:35:33 -0400
commit8e0cf965f95edd41df11cca50b92b4cb6ea8d80a (patch)
tree08c55f2c5caaccf3c479a71fb730594164e8e23c
parent0131c4ec794a7409eafff0c79105309540aaca4d (diff)
perf symbols: Add support for reading from /proc/kcore
In the absence of vmlinux, perf tools uses kallsyms for symbols. If the user has access, now also map to /proc/kcore. The dso data_type is now set to either DSO_BINARY_TYPE__KCORE or DSO_BINARY_TYPE__GUEST_KCORE as approprite. This patch breaks the "vmlinux symtab matches kallsyms" test. That is fixed in a following patch. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Cc: David Ahern <dsahern@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Namhyung Kim <namhyung@gmail.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Link: http://lkml.kernel.org/r/1375875537-4509-8-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/util/dso.c5
-rw-r--r--tools/perf/util/dso.h8
-rw-r--r--tools/perf/util/machine.c16
-rw-r--r--tools/perf/util/map.c18
-rw-r--r--tools/perf/util/map.h13
-rw-r--r--tools/perf/util/symbol-elf.c51
-rw-r--r--tools/perf/util/symbol-minimal.c7
-rw-r--r--tools/perf/util/symbol.c227
-rw-r--r--tools/perf/util/symbol.h5
9 files changed, 347 insertions, 3 deletions
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 1955804e82ff..e3c1ff8512c8 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -95,6 +95,11 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,
95 dso->long_name); 95 dso->long_name);
96 break; 96 break;
97 97
98 case DSO_BINARY_TYPE__KCORE:
99 case DSO_BINARY_TYPE__GUEST_KCORE:
100 snprintf(file, size, "%s", dso->long_name);
101 break;
102
98 default: 103 default:
99 case DSO_BINARY_TYPE__KALLSYMS: 104 case DSO_BINARY_TYPE__KALLSYMS:
100 case DSO_BINARY_TYPE__GUEST_KALLSYMS: 105 case DSO_BINARY_TYPE__GUEST_KALLSYMS:
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 735a83751b19..b793053335d6 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -21,6 +21,8 @@ enum dso_binary_type {
21 DSO_BINARY_TYPE__SYSTEM_PATH_DSO, 21 DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
22 DSO_BINARY_TYPE__GUEST_KMODULE, 22 DSO_BINARY_TYPE__GUEST_KMODULE,
23 DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, 23 DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
24 DSO_BINARY_TYPE__KCORE,
25 DSO_BINARY_TYPE__GUEST_KCORE,
24 DSO_BINARY_TYPE__NOT_FOUND, 26 DSO_BINARY_TYPE__NOT_FOUND,
25}; 27};
26 28
@@ -155,4 +157,10 @@ static inline bool dso__is_vmlinux(struct dso *dso)
155 dso->data_type == DSO_BINARY_TYPE__GUEST_VMLINUX; 157 dso->data_type == DSO_BINARY_TYPE__GUEST_VMLINUX;
156} 158}
157 159
160static inline bool dso__is_kcore(struct dso *dso)
161{
162 return dso->data_type == DSO_BINARY_TYPE__KCORE ||
163 dso->data_type == DSO_BINARY_TYPE__GUEST_KCORE;
164}
165
158#endif /* __PERF_DSO */ 166#endif /* __PERF_DSO */
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index dc35dcffbd05..ef3b49cde04e 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -856,6 +856,18 @@ static void machine__set_kernel_mmap_len(struct machine *machine,
856 } 856 }
857} 857}
858 858
859static bool machine__uses_kcore(struct machine *machine)
860{
861 struct dso *dso;
862
863 list_for_each_entry(dso, &machine->kernel_dsos, node) {
864 if (dso__is_kcore(dso))
865 return true;
866 }
867
868 return false;
869}
870
859static int machine__process_kernel_mmap_event(struct machine *machine, 871static int machine__process_kernel_mmap_event(struct machine *machine,
860 union perf_event *event) 872 union perf_event *event)
861{ 873{
@@ -864,6 +876,10 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
864 enum dso_kernel_type kernel_type; 876 enum dso_kernel_type kernel_type;
865 bool is_kernel_mmap; 877 bool is_kernel_mmap;
866 878
879 /* If we have maps from kcore then we do not need or want any others */
880 if (machine__uses_kcore(machine))
881 return 0;
882
867 machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); 883 machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
868 if (machine__is_host(machine)) 884 if (machine__is_host(machine))
869 kernel_type = DSO_TYPE_KERNEL; 885 kernel_type = DSO_TYPE_KERNEL;
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 4d599febfb0b..9e8304ca343e 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -555,3 +555,21 @@ struct map *maps__find(struct rb_root *maps, u64 ip)
555 555
556 return NULL; 556 return NULL;
557} 557}
558
559struct map *maps__first(struct rb_root *maps)
560{
561 struct rb_node *first = rb_first(maps);
562
563 if (first)
564 return rb_entry(first, struct map, rb_node);
565 return NULL;
566}
567
568struct map *maps__next(struct map *map)
569{
570 struct rb_node *next = rb_next(&map->rb_node);
571
572 if (next)
573 return rb_entry(next, struct map, rb_node);
574 return NULL;
575}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index a887f2c9dfbb..2cc93cbf0e17 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -112,6 +112,8 @@ size_t __map_groups__fprintf_maps(struct map_groups *mg,
112void maps__insert(struct rb_root *maps, struct map *map); 112void maps__insert(struct rb_root *maps, struct map *map);
113void maps__remove(struct rb_root *maps, struct map *map); 113void maps__remove(struct rb_root *maps, struct map *map);
114struct map *maps__find(struct rb_root *maps, u64 addr); 114struct map *maps__find(struct rb_root *maps, u64 addr);
115struct map *maps__first(struct rb_root *maps);
116struct map *maps__next(struct map *map);
115void map_groups__init(struct map_groups *mg); 117void map_groups__init(struct map_groups *mg);
116void map_groups__exit(struct map_groups *mg); 118void map_groups__exit(struct map_groups *mg);
117int map_groups__clone(struct map_groups *mg, 119int map_groups__clone(struct map_groups *mg,
@@ -139,6 +141,17 @@ static inline struct map *map_groups__find(struct map_groups *mg,
139 return maps__find(&mg->maps[type], addr); 141 return maps__find(&mg->maps[type], addr);
140} 142}
141 143
144static inline struct map *map_groups__first(struct map_groups *mg,
145 enum map_type type)
146{
147 return maps__first(&mg->maps[type]);
148}
149
150static inline struct map *map_groups__next(struct map *map)
151{
152 return maps__next(map);
153}
154
142struct symbol *map_groups__find_symbol(struct map_groups *mg, 155struct symbol *map_groups__find_symbol(struct map_groups *mg,
143 enum map_type type, u64 addr, 156 enum map_type type, u64 addr,
144 struct map **mapp, 157 struct map **mapp,
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 3eaa7b486e2e..a7b9ab557380 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -951,6 +951,57 @@ out_elf_end:
951 return err; 951 return err;
952} 952}
953 953
954static int elf_read_maps(Elf *elf, bool exe, mapfn_t mapfn, void *data)
955{
956 GElf_Phdr phdr;
957 size_t i, phdrnum;
958 int err;
959 u64 sz;
960
961 if (elf_getphdrnum(elf, &phdrnum))
962 return -1;
963
964 for (i = 0; i < phdrnum; i++) {
965 if (gelf_getphdr(elf, i, &phdr) == NULL)
966 return -1;
967 if (phdr.p_type != PT_LOAD)
968 continue;
969 if (exe) {
970 if (!(phdr.p_flags & PF_X))
971 continue;
972 } else {
973 if (!(phdr.p_flags & PF_R))
974 continue;
975 }
976 sz = min(phdr.p_memsz, phdr.p_filesz);
977 if (!sz)
978 continue;
979 err = mapfn(phdr.p_vaddr, sz, phdr.p_offset, data);
980 if (err)
981 return err;
982 }
983 return 0;
984}
985
986int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
987 bool *is_64_bit)
988{
989 int err;
990 Elf *elf;
991
992 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
993 if (elf == NULL)
994 return -1;
995
996 if (is_64_bit)
997 *is_64_bit = (gelf_getclass(elf) == ELFCLASS64);
998
999 err = elf_read_maps(elf, exe, mapfn, data);
1000
1001 elf_end(elf);
1002 return err;
1003}
1004
954void symbol__elf_init(void) 1005void symbol__elf_init(void)
955{ 1006{
956 elf_version(EV_CURRENT); 1007 elf_version(EV_CURRENT);
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
index a7390cde63bc..3a802c300fc5 100644
--- a/tools/perf/util/symbol-minimal.c
+++ b/tools/perf/util/symbol-minimal.c
@@ -301,6 +301,13 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused,
301 return 0; 301 return 0;
302} 302}
303 303
304int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused,
305 mapfn_t mapfn __maybe_unused, void *data __maybe_unused,
306 bool *is_64_bit __maybe_unused)
307{
308 return -1;
309}
310
304void symbol__elf_init(void) 311void symbol__elf_init(void)
305{ 312{
306} 313}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b9056a8c1620..77f3b95bb46d 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -327,6 +327,16 @@ static struct symbol *symbols__find(struct rb_root *symbols, u64 ip)
327 return NULL; 327 return NULL;
328} 328}
329 329
330static struct symbol *symbols__first(struct rb_root *symbols)
331{
332 struct rb_node *n = rb_first(symbols);
333
334 if (n)
335 return rb_entry(n, struct symbol, rb_node);
336
337 return NULL;
338}
339
330struct symbol_name_rb_node { 340struct symbol_name_rb_node {
331 struct rb_node rb_node; 341 struct rb_node rb_node;
332 struct symbol sym; 342 struct symbol sym;
@@ -397,6 +407,11 @@ struct symbol *dso__find_symbol(struct dso *dso,
397 return symbols__find(&dso->symbols[type], addr); 407 return symbols__find(&dso->symbols[type], addr);
398} 408}
399 409
410struct symbol *dso__first_symbol(struct dso *dso, enum map_type type)
411{
412 return symbols__first(&dso->symbols[type]);
413}
414
400struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, 415struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,
401 const char *name) 416 const char *name)
402{ 417{
@@ -533,6 +548,53 @@ static int dso__load_all_kallsyms(struct dso *dso, const char *filename,
533 return kallsyms__parse(filename, &args, map__process_kallsym_symbol); 548 return kallsyms__parse(filename, &args, map__process_kallsym_symbol);
534} 549}
535 550
551static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map,
552 symbol_filter_t filter)
553{
554 struct map_groups *kmaps = map__kmap(map)->kmaps;
555 struct map *curr_map;
556 struct symbol *pos;
557 int count = 0, moved = 0;
558 struct rb_root *root = &dso->symbols[map->type];
559 struct rb_node *next = rb_first(root);
560
561 while (next) {
562 char *module;
563
564 pos = rb_entry(next, struct symbol, rb_node);
565 next = rb_next(&pos->rb_node);
566
567 module = strchr(pos->name, '\t');
568 if (module)
569 *module = '\0';
570
571 curr_map = map_groups__find(kmaps, map->type, pos->start);
572
573 if (!curr_map || (filter && filter(curr_map, pos))) {
574 rb_erase(&pos->rb_node, root);
575 symbol__delete(pos);
576 } else {
577 pos->start -= curr_map->start - curr_map->pgoff;
578 if (pos->end)
579 pos->end -= curr_map->start - curr_map->pgoff;
580 if (curr_map != map) {
581 rb_erase(&pos->rb_node, root);
582 symbols__insert(
583 &curr_map->dso->symbols[curr_map->type],
584 pos);
585 ++moved;
586 } else {
587 ++count;
588 }
589 }
590 }
591
592 /* Symbols have been adjusted */
593 dso->adjust_symbols = 1;
594
595 return count + moved;
596}
597
536/* 598/*
537 * Split the symbols into maps, making sure there are no overlaps, i.e. the 599 * Split the symbols into maps, making sure there are no overlaps, i.e. the
538 * kernel range is broken in several maps, named [kernel].N, as we don't have 600 * kernel range is broken in several maps, named [kernel].N, as we don't have
@@ -674,6 +736,161 @@ bool symbol__restricted_filename(const char *filename,
674 return restricted; 736 return restricted;
675} 737}
676 738
739struct kcore_mapfn_data {
740 struct dso *dso;
741 enum map_type type;
742 struct list_head maps;
743};
744
745static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data)
746{
747 struct kcore_mapfn_data *md = data;
748 struct map *map;
749
750 map = map__new2(start, md->dso, md->type);
751 if (map == NULL)
752 return -ENOMEM;
753
754 map->end = map->start + len;
755 map->pgoff = pgoff;
756
757 list_add(&map->node, &md->maps);
758
759 return 0;
760}
761
762/*
763 * If kallsyms is referenced by name then we look for kcore in the same
764 * directory.
765 */
766static bool kcore_filename_from_kallsyms_filename(char *kcore_filename,
767 const char *kallsyms_filename)
768{
769 char *name;
770
771 strcpy(kcore_filename, kallsyms_filename);
772 name = strrchr(kcore_filename, '/');
773 if (!name)
774 return false;
775
776 if (!strcmp(name, "/kallsyms")) {
777 strcpy(name, "/kcore");
778 return true;
779 }
780
781 return false;
782}
783
784static int dso__load_kcore(struct dso *dso, struct map *map,
785 const char *kallsyms_filename)
786{
787 struct map_groups *kmaps = map__kmap(map)->kmaps;
788 struct machine *machine = kmaps->machine;
789 struct kcore_mapfn_data md;
790 struct map *old_map, *new_map, *replacement_map = NULL;
791 bool is_64_bit;
792 int err, fd;
793 char kcore_filename[PATH_MAX];
794 struct symbol *sym;
795
796 /* This function requires that the map is the kernel map */
797 if (map != machine->vmlinux_maps[map->type])
798 return -EINVAL;
799
800 if (!kcore_filename_from_kallsyms_filename(kcore_filename,
801 kallsyms_filename))
802 return -EINVAL;
803
804 md.dso = dso;
805 md.type = map->type;
806 INIT_LIST_HEAD(&md.maps);
807
808 fd = open(kcore_filename, O_RDONLY);
809 if (fd < 0)
810 return -EINVAL;
811
812 /* Read new maps into temporary lists */
813 err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md,
814 &is_64_bit);
815 if (err)
816 goto out_err;
817
818 if (list_empty(&md.maps)) {
819 err = -EINVAL;
820 goto out_err;
821 }
822
823 /* Remove old maps */
824 old_map = map_groups__first(kmaps, map->type);
825 while (old_map) {
826 struct map *next = map_groups__next(old_map);
827
828 if (old_map != map)
829 map_groups__remove(kmaps, old_map);
830 old_map = next;
831 }
832
833 /* Find the kernel map using the first symbol */
834 sym = dso__first_symbol(dso, map->type);
835 list_for_each_entry(new_map, &md.maps, node) {
836 if (sym && sym->start >= new_map->start &&
837 sym->start < new_map->end) {
838 replacement_map = new_map;
839 break;
840 }
841 }
842
843 if (!replacement_map)
844 replacement_map = list_entry(md.maps.next, struct map, node);
845
846 /* Add new maps */
847 while (!list_empty(&md.maps)) {
848 new_map = list_entry(md.maps.next, struct map, node);
849 list_del(&new_map->node);
850 if (new_map == replacement_map) {
851 map->start = new_map->start;
852 map->end = new_map->end;
853 map->pgoff = new_map->pgoff;
854 map->map_ip = new_map->map_ip;
855 map->unmap_ip = new_map->unmap_ip;
856 map__delete(new_map);
857 /* Ensure maps are correctly ordered */
858 map_groups__remove(kmaps, map);
859 map_groups__insert(kmaps, map);
860 } else {
861 map_groups__insert(kmaps, new_map);
862 }
863 }
864
865 /*
866 * Set the data type and long name so that kcore can be read via
867 * dso__data_read_addr().
868 */
869 if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
870 dso->data_type = DSO_BINARY_TYPE__GUEST_KCORE;
871 else
872 dso->data_type = DSO_BINARY_TYPE__KCORE;
873 dso__set_long_name(dso, strdup(kcore_filename));
874
875 close(fd);
876
877 if (map->type == MAP__FUNCTION)
878 pr_debug("Using %s for kernel object code\n", kcore_filename);
879 else
880 pr_debug("Using %s for kernel data\n", kcore_filename);
881
882 return 0;
883
884out_err:
885 while (!list_empty(&md.maps)) {
886 map = list_entry(md.maps.next, struct map, node);
887 list_del(&map->node);
888 map__delete(map);
889 }
890 close(fd);
891 return -EINVAL;
892}
893
677int dso__load_kallsyms(struct dso *dso, const char *filename, 894int dso__load_kallsyms(struct dso *dso, const char *filename,
678 struct map *map, symbol_filter_t filter) 895 struct map *map, symbol_filter_t filter)
679{ 896{
@@ -691,7 +908,10 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,
691 else 908 else
692 dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS; 909 dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS;
693 910
694 return dso__split_kallsyms(dso, map, filter); 911 if (!dso__load_kcore(dso, map, filename))
912 return dso__split_kallsyms_for_kcore(dso, map, filter);
913 else
914 return dso__split_kallsyms(dso, map, filter);
695} 915}
696 916
697static int dso__load_perf_map(struct dso *dso, struct map *map, 917static int dso__load_perf_map(struct dso *dso, struct map *map,
@@ -1065,7 +1285,7 @@ do_kallsyms:
1065 pr_debug("Using %s for symbols\n", kallsyms_filename); 1285 pr_debug("Using %s for symbols\n", kallsyms_filename);
1066 free(kallsyms_allocated_filename); 1286 free(kallsyms_allocated_filename);
1067 1287
1068 if (err > 0) { 1288 if (err > 0 && !dso__is_kcore(dso)) {
1069 dso__set_long_name(dso, strdup("[kernel.kallsyms]")); 1289 dso__set_long_name(dso, strdup("[kernel.kallsyms]"));
1070 map__fixup_start(map); 1290 map__fixup_start(map);
1071 map__fixup_end(map); 1291 map__fixup_end(map);
@@ -1109,8 +1329,9 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,
1109 } 1329 }
1110 1330
1111 err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); 1331 err = dso__load_kallsyms(dso, kallsyms_filename, map, filter);
1112 if (err > 0) { 1332 if (err > 0)
1113 pr_debug("Using %s for symbols\n", kallsyms_filename); 1333 pr_debug("Using %s for symbols\n", kallsyms_filename);
1334 if (err > 0 && !dso__is_kcore(dso)) {
1114 machine__mmap_name(machine, path, sizeof(path)); 1335 machine__mmap_name(machine, path, sizeof(path));
1115 dso__set_long_name(dso, strdup(path)); 1336 dso__set_long_name(dso, strdup(path));
1116 map__fixup_start(map); 1337 map__fixup_start(map);
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 5f720dc076da..fd5b70ea2981 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -215,6 +215,7 @@ struct symbol *dso__find_symbol(struct dso *dso, enum map_type type,
215 u64 addr); 215 u64 addr);
216struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, 216struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,
217 const char *name); 217 const char *name);
218struct symbol *dso__first_symbol(struct dso *dso, enum map_type type);
218 219
219int filename__read_build_id(const char *filename, void *bf, size_t size); 220int filename__read_build_id(const char *filename, void *bf, size_t size);
220int sysfs__read_build_id(const char *filename, void *bf, size_t size); 221int sysfs__read_build_id(const char *filename, void *bf, size_t size);
@@ -247,4 +248,8 @@ void symbols__fixup_duplicate(struct rb_root *symbols);
247void symbols__fixup_end(struct rb_root *symbols); 248void symbols__fixup_end(struct rb_root *symbols);
248void __map_groups__fixup_end(struct map_groups *mg, enum map_type type); 249void __map_groups__fixup_end(struct map_groups *mg, enum map_type type);
249 250
251typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data);
252int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
253 bool *is_64_bit);
254
250#endif /* __PERF_SYMBOL */ 255#endif /* __PERF_SYMBOL */