diff options
author | Adrian Hunter <adrian.hunter@intel.com> | 2013-10-09 08:01:12 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2013-10-14 09:29:00 -0400 |
commit | afba19d9dc8eba66ea26901708cf99354c637786 (patch) | |
tree | 3b96f6610f8b776d4a4f9836205321bda4b143fd /tools/perf | |
parent | 52afdaf9f0c6a35e154ba42ac9510044e16d75ec (diff) |
perf symbols: Workaround objdump difficulties with kcore
The objdump tool fails to annotate module symbols when looking at kcore.
Workaround this by extracting object code from kcore and putting it in a
temporary file for objdump to use instead.
The temporary file is created to look like kcore but contains only the
function being disassembled.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
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 <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1381320078-16497-3-git-send-email-adrian.hunter@intel.com
[ Renamed 'index' to 'idx' to avoid shadowing string.h's 'index' in Fedora 12,
Replace local with variable length with malloc/free to fix build in Fedora 12 ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/util/annotate.c | 21 | ||||
-rw-r--r-- | tools/perf/util/symbol-elf.c | 229 | ||||
-rw-r--r-- | tools/perf/util/symbol-minimal.c | 9 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 14 |
4 files changed, 273 insertions, 0 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index d73e8008aada..882bb864cee0 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -879,6 +879,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | |||
879 | FILE *file; | 879 | FILE *file; |
880 | int err = 0; | 880 | int err = 0; |
881 | char symfs_filename[PATH_MAX]; | 881 | char symfs_filename[PATH_MAX]; |
882 | struct kcore_extract kce; | ||
883 | bool delete_extract = false; | ||
882 | 884 | ||
883 | if (filename) { | 885 | if (filename) { |
884 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | 886 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", |
@@ -940,6 +942,23 @@ fallback: | |||
940 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | 942 | pr_debug("annotating [%p] %30s : [%p] %30s\n", |
941 | dso, dso->long_name, sym, sym->name); | 943 | dso, dso->long_name, sym, sym->name); |
942 | 944 | ||
945 | if (dso__is_kcore(dso)) { | ||
946 | kce.kcore_filename = symfs_filename; | ||
947 | kce.addr = map__rip_2objdump(map, sym->start); | ||
948 | kce.offs = sym->start; | ||
949 | kce.len = sym->end + 1 - sym->start; | ||
950 | if (!kcore_extract__create(&kce)) { | ||
951 | delete_extract = true; | ||
952 | strlcpy(symfs_filename, kce.extract_filename, | ||
953 | sizeof(symfs_filename)); | ||
954 | if (free_filename) { | ||
955 | free(filename); | ||
956 | free_filename = false; | ||
957 | } | ||
958 | filename = symfs_filename; | ||
959 | } | ||
960 | } | ||
961 | |||
943 | snprintf(command, sizeof(command), | 962 | snprintf(command, sizeof(command), |
944 | "%s %s%s --start-address=0x%016" PRIx64 | 963 | "%s %s%s --start-address=0x%016" PRIx64 |
945 | " --stop-address=0x%016" PRIx64 | 964 | " --stop-address=0x%016" PRIx64 |
@@ -972,6 +991,8 @@ fallback: | |||
972 | 991 | ||
973 | pclose(file); | 992 | pclose(file); |
974 | out_free_filename: | 993 | out_free_filename: |
994 | if (delete_extract) | ||
995 | kcore_extract__delete(&kce); | ||
975 | if (free_filename) | 996 | if (free_filename) |
976 | free(filename); | 997 | free(filename); |
977 | return err; | 998 | return err; |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index c37693052800..499c71d30225 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -1018,6 +1018,235 @@ int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, | |||
1018 | return err; | 1018 | return err; |
1019 | } | 1019 | } |
1020 | 1020 | ||
1021 | static int copy_bytes(int from, off_t from_offs, int to, off_t to_offs, u64 len) | ||
1022 | { | ||
1023 | ssize_t r; | ||
1024 | size_t n; | ||
1025 | int err = -1; | ||
1026 | char *buf = malloc(page_size); | ||
1027 | |||
1028 | if (buf == NULL) | ||
1029 | return -1; | ||
1030 | |||
1031 | if (lseek(to, to_offs, SEEK_SET) != to_offs) | ||
1032 | goto out; | ||
1033 | |||
1034 | if (lseek(from, from_offs, SEEK_SET) != from_offs) | ||
1035 | goto out; | ||
1036 | |||
1037 | while (len) { | ||
1038 | n = page_size; | ||
1039 | if (len < n) | ||
1040 | n = len; | ||
1041 | /* Use read because mmap won't work on proc files */ | ||
1042 | r = read(from, buf, n); | ||
1043 | if (r < 0) | ||
1044 | goto out; | ||
1045 | if (!r) | ||
1046 | break; | ||
1047 | n = r; | ||
1048 | r = write(to, buf, n); | ||
1049 | if (r < 0) | ||
1050 | goto out; | ||
1051 | if ((size_t)r != n) | ||
1052 | goto out; | ||
1053 | len -= n; | ||
1054 | } | ||
1055 | |||
1056 | err = 0; | ||
1057 | out: | ||
1058 | free(buf); | ||
1059 | return err; | ||
1060 | } | ||
1061 | |||
1062 | struct kcore { | ||
1063 | int fd; | ||
1064 | int elfclass; | ||
1065 | Elf *elf; | ||
1066 | GElf_Ehdr ehdr; | ||
1067 | }; | ||
1068 | |||
1069 | static int kcore__open(struct kcore *kcore, const char *filename) | ||
1070 | { | ||
1071 | GElf_Ehdr *ehdr; | ||
1072 | |||
1073 | kcore->fd = open(filename, O_RDONLY); | ||
1074 | if (kcore->fd == -1) | ||
1075 | return -1; | ||
1076 | |||
1077 | kcore->elf = elf_begin(kcore->fd, ELF_C_READ, NULL); | ||
1078 | if (!kcore->elf) | ||
1079 | goto out_close; | ||
1080 | |||
1081 | kcore->elfclass = gelf_getclass(kcore->elf); | ||
1082 | if (kcore->elfclass == ELFCLASSNONE) | ||
1083 | goto out_end; | ||
1084 | |||
1085 | ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); | ||
1086 | if (!ehdr) | ||
1087 | goto out_end; | ||
1088 | |||
1089 | return 0; | ||
1090 | |||
1091 | out_end: | ||
1092 | elf_end(kcore->elf); | ||
1093 | out_close: | ||
1094 | close(kcore->fd); | ||
1095 | return -1; | ||
1096 | } | ||
1097 | |||
1098 | static int kcore__init(struct kcore *kcore, char *filename, int elfclass, | ||
1099 | bool temp) | ||
1100 | { | ||
1101 | GElf_Ehdr *ehdr; | ||
1102 | |||
1103 | kcore->elfclass = elfclass; | ||
1104 | |||
1105 | if (temp) | ||
1106 | kcore->fd = mkstemp(filename); | ||
1107 | else | ||
1108 | kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400); | ||
1109 | if (kcore->fd == -1) | ||
1110 | return -1; | ||
1111 | |||
1112 | kcore->elf = elf_begin(kcore->fd, ELF_C_WRITE, NULL); | ||
1113 | if (!kcore->elf) | ||
1114 | goto out_close; | ||
1115 | |||
1116 | if (!gelf_newehdr(kcore->elf, elfclass)) | ||
1117 | goto out_end; | ||
1118 | |||
1119 | ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); | ||
1120 | if (!ehdr) | ||
1121 | goto out_end; | ||
1122 | |||
1123 | return 0; | ||
1124 | |||
1125 | out_end: | ||
1126 | elf_end(kcore->elf); | ||
1127 | out_close: | ||
1128 | close(kcore->fd); | ||
1129 | unlink(filename); | ||
1130 | return -1; | ||
1131 | } | ||
1132 | |||
1133 | static void kcore__close(struct kcore *kcore) | ||
1134 | { | ||
1135 | elf_end(kcore->elf); | ||
1136 | close(kcore->fd); | ||
1137 | } | ||
1138 | |||
1139 | static int kcore__copy_hdr(struct kcore *from, struct kcore *to, size_t count) | ||
1140 | { | ||
1141 | GElf_Ehdr *ehdr = &to->ehdr; | ||
1142 | GElf_Ehdr *kehdr = &from->ehdr; | ||
1143 | |||
1144 | memcpy(ehdr->e_ident, kehdr->e_ident, EI_NIDENT); | ||
1145 | ehdr->e_type = kehdr->e_type; | ||
1146 | ehdr->e_machine = kehdr->e_machine; | ||
1147 | ehdr->e_version = kehdr->e_version; | ||
1148 | ehdr->e_entry = 0; | ||
1149 | ehdr->e_shoff = 0; | ||
1150 | ehdr->e_flags = kehdr->e_flags; | ||
1151 | ehdr->e_phnum = count; | ||
1152 | ehdr->e_shentsize = 0; | ||
1153 | ehdr->e_shnum = 0; | ||
1154 | ehdr->e_shstrndx = 0; | ||
1155 | |||
1156 | if (from->elfclass == ELFCLASS32) { | ||
1157 | ehdr->e_phoff = sizeof(Elf32_Ehdr); | ||
1158 | ehdr->e_ehsize = sizeof(Elf32_Ehdr); | ||
1159 | ehdr->e_phentsize = sizeof(Elf32_Phdr); | ||
1160 | } else { | ||
1161 | ehdr->e_phoff = sizeof(Elf64_Ehdr); | ||
1162 | ehdr->e_ehsize = sizeof(Elf64_Ehdr); | ||
1163 | ehdr->e_phentsize = sizeof(Elf64_Phdr); | ||
1164 | } | ||
1165 | |||
1166 | if (!gelf_update_ehdr(to->elf, ehdr)) | ||
1167 | return -1; | ||
1168 | |||
1169 | if (!gelf_newphdr(to->elf, count)) | ||
1170 | return -1; | ||
1171 | |||
1172 | return 0; | ||
1173 | } | ||
1174 | |||
1175 | static int kcore__add_phdr(struct kcore *kcore, int idx, off_t offset, | ||
1176 | u64 addr, u64 len) | ||
1177 | { | ||
1178 | GElf_Phdr gphdr; | ||
1179 | GElf_Phdr *phdr; | ||
1180 | |||
1181 | phdr = gelf_getphdr(kcore->elf, idx, &gphdr); | ||
1182 | if (!phdr) | ||
1183 | return -1; | ||
1184 | |||
1185 | phdr->p_type = PT_LOAD; | ||
1186 | phdr->p_flags = PF_R | PF_W | PF_X; | ||
1187 | phdr->p_offset = offset; | ||
1188 | phdr->p_vaddr = addr; | ||
1189 | phdr->p_paddr = 0; | ||
1190 | phdr->p_filesz = len; | ||
1191 | phdr->p_memsz = len; | ||
1192 | phdr->p_align = page_size; | ||
1193 | |||
1194 | if (!gelf_update_phdr(kcore->elf, idx, phdr)) | ||
1195 | return -1; | ||
1196 | |||
1197 | return 0; | ||
1198 | } | ||
1199 | |||
1200 | static off_t kcore__write(struct kcore *kcore) | ||
1201 | { | ||
1202 | return elf_update(kcore->elf, ELF_C_WRITE); | ||
1203 | } | ||
1204 | |||
1205 | int kcore_extract__create(struct kcore_extract *kce) | ||
1206 | { | ||
1207 | struct kcore kcore; | ||
1208 | struct kcore extract; | ||
1209 | size_t count = 1; | ||
1210 | int idx = 0, err = -1; | ||
1211 | off_t offset = page_size, sz; | ||
1212 | |||
1213 | if (kcore__open(&kcore, kce->kcore_filename)) | ||
1214 | return -1; | ||
1215 | |||
1216 | strcpy(kce->extract_filename, PERF_KCORE_EXTRACT); | ||
1217 | if (kcore__init(&extract, kce->extract_filename, kcore.elfclass, true)) | ||
1218 | goto out_kcore_close; | ||
1219 | |||
1220 | if (kcore__copy_hdr(&kcore, &extract, count)) | ||
1221 | goto out_extract_close; | ||
1222 | |||
1223 | if (kcore__add_phdr(&extract, idx, offset, kce->addr, kce->len)) | ||
1224 | goto out_extract_close; | ||
1225 | |||
1226 | sz = kcore__write(&extract); | ||
1227 | if (sz < 0 || sz > offset) | ||
1228 | goto out_extract_close; | ||
1229 | |||
1230 | if (copy_bytes(kcore.fd, kce->offs, extract.fd, offset, kce->len)) | ||
1231 | goto out_extract_close; | ||
1232 | |||
1233 | err = 0; | ||
1234 | |||
1235 | out_extract_close: | ||
1236 | kcore__close(&extract); | ||
1237 | if (err) | ||
1238 | unlink(kce->extract_filename); | ||
1239 | out_kcore_close: | ||
1240 | kcore__close(&kcore); | ||
1241 | |||
1242 | return err; | ||
1243 | } | ||
1244 | |||
1245 | void kcore_extract__delete(struct kcore_extract *kce) | ||
1246 | { | ||
1247 | unlink(kce->extract_filename); | ||
1248 | } | ||
1249 | |||
1021 | void symbol__elf_init(void) | 1250 | void symbol__elf_init(void) |
1022 | { | 1251 | { |
1023 | elf_version(EV_CURRENT); | 1252 | elf_version(EV_CURRENT); |
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 3a802c300fc5..928556d2508f 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c | |||
@@ -308,6 +308,15 @@ int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused, | |||
308 | return -1; | 308 | return -1; |
309 | } | 309 | } |
310 | 310 | ||
311 | int kcore_extract__create(struct kcore_extract *kce __maybe_unused) | ||
312 | { | ||
313 | return -1; | ||
314 | } | ||
315 | |||
316 | void kcore_extract__delete(struct kcore_extract *kce __maybe_unused) | ||
317 | { | ||
318 | } | ||
319 | |||
311 | void symbol__elf_init(void) | 320 | void symbol__elf_init(void) |
312 | { | 321 | { |
313 | } | 322 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 2d3eb43ab9f9..fb107e13e925 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -256,4 +256,18 @@ typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data); | |||
256 | int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, | 256 | int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, |
257 | bool *is_64_bit); | 257 | bool *is_64_bit); |
258 | 258 | ||
259 | #define PERF_KCORE_EXTRACT "/tmp/perf-kcore-XXXXXX" | ||
260 | |||
261 | struct kcore_extract { | ||
262 | char *kcore_filename; | ||
263 | u64 addr; | ||
264 | u64 offs; | ||
265 | u64 len; | ||
266 | char extract_filename[sizeof(PERF_KCORE_EXTRACT)]; | ||
267 | int fd; | ||
268 | }; | ||
269 | |||
270 | int kcore_extract__create(struct kcore_extract *kce); | ||
271 | void kcore_extract__delete(struct kcore_extract *kce); | ||
272 | |||
259 | #endif /* __PERF_SYMBOL */ | 273 | #endif /* __PERF_SYMBOL */ |