aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2009-12-27 18:37:06 -0500
committerIngo Molnar <mingo@elte.hu>2009-12-28 03:03:36 -0500
commit4cf40131a5cf4918e83b3756e58a1fc9e984f8ef (patch)
tree88df943f704ce3a52660f975195a0b4b1b91243d /tools/perf/util
parent55aa640f54280da25046acd2075842d464f451e6 (diff)
perf record: Introduce a symtab cache
Now a cache will be created in a ~/.debug debuginfo like hierarchy, so that at the end of a 'perf record' session all the binaries (with build-ids) involved get collected and indexed by their build-ids, so that perf report can find them. This is interesting when developing software where you want to do a 'perf diff' with the previous build and opens avenues for lots more interesting tools, like a 'perf diff --graph' that takes more than two binaries into account. Tunables for collecting just the symtabs can be added if one doesn't want to have the full binary, but having the full binary allows things like 'perf rerecord' or other tools that can re-run the tests by having access to the exact binary in some perf.data file, so it may well be interesting to keep the full binary there. Space consumption is minimised by trying to use hard links, a 'perf cache' tool to manage the space used, a la ccache is required to purge older entries. With this in place it will be possible also to introduce new commands, 'perf archive' and 'perf restore' (or some more suitable and future proof names) to create a cpio/tar file with the perf data and the files in the cache that _had_ perf hits of interest. There are more aspects to polish, like finding the right vmlinux file to cache, etc, but this is enough for a first step. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1261957026-15580-10-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/header.c82
-rw-r--r--tools/perf/util/symbol.c17
-rw-r--r--tools/perf/util/symbol.h2
-rw-r--r--tools/perf/util/util.c69
-rw-r--r--tools/perf/util/util.h3
5 files changed, 164 insertions, 9 deletions
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 8a0bca55106f..df237c3a041b 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -169,20 +169,23 @@ static int do_write(int fd, const void *buf, size_t size)
169 return 0; 169 return 0;
170} 170}
171 171
172#define dsos__for_each_with_build_id(pos, head) \
173 list_for_each_entry(pos, head, node) \
174 if (!pos->has_build_id) \
175 continue; \
176 else
177
172static int __dsos__write_buildid_table(struct list_head *head, int fd) 178static int __dsos__write_buildid_table(struct list_head *head, int fd)
173{ 179{
174#define NAME_ALIGN 64 180#define NAME_ALIGN 64
175 struct dso *pos; 181 struct dso *pos;
176 static const char zero_buf[NAME_ALIGN]; 182 static const char zero_buf[NAME_ALIGN];
177 183
178 list_for_each_entry(pos, head, node) { 184 dsos__for_each_with_build_id(pos, head) {
179 int err; 185 int err;
180 struct build_id_event b; 186 struct build_id_event b;
181 size_t len; 187 size_t len = pos->long_name_len + 1;
182 188
183 if (!pos->has_build_id)
184 continue;
185 len = pos->long_name_len + 1;
186 len = ALIGN(len, NAME_ALIGN); 189 len = ALIGN(len, NAME_ALIGN);
187 memset(&b, 0, sizeof(b)); 190 memset(&b, 0, sizeof(b));
188 memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); 191 memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
@@ -209,6 +212,74 @@ static int dsos__write_buildid_table(int fd)
209 return err; 212 return err;
210} 213}
211 214
215static int dso__cache_build_id(struct dso *self, const char *debugdir)
216{
217 const size_t size = PATH_MAX;
218 char *filename = malloc(size),
219 *linkname = malloc(size), *targetname, *sbuild_id;
220 int len, err = -1;
221
222 if (filename == NULL || linkname == NULL)
223 goto out_free;
224
225 len = snprintf(filename, size, "%s%s", debugdir, self->long_name);
226 if (mkdir_p(filename, 0755))
227 goto out_free;
228
229 len += snprintf(filename + len, sizeof(filename) - len, "/");
230 sbuild_id = filename + len;
231 build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id);
232
233 if (access(filename, F_OK) && link(self->long_name, filename) &&
234 copyfile(self->long_name, filename))
235 goto out_free;
236
237 len = snprintf(linkname, size, "%s/.build-id/%.2s",
238 debugdir, sbuild_id);
239
240 if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
241 goto out_free;
242
243 snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
244 targetname = filename + strlen(debugdir) - 5;
245 memcpy(targetname, "../..", 5);
246
247 if (symlink(targetname, linkname) == 0)
248 err = 0;
249out_free:
250 free(filename);
251 free(linkname);
252 return err;
253}
254
255static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
256{
257 struct dso *pos;
258 int err = 0;
259
260 dsos__for_each_with_build_id(pos, head)
261 if (dso__cache_build_id(pos, debugdir))
262 err = -1;
263
264 return err;
265}
266
267static int dsos__cache_build_ids(void)
268{
269 int err_kernel, err_user;
270 char debugdir[PATH_MAX];
271
272 snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"),
273 DEBUG_CACHE_DIR);
274
275 if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
276 return -1;
277
278 err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir);
279 err_user = __dsos__cache_build_ids(&dsos__user, debugdir);
280 return err_kernel || err_user ? -1 : 0;
281}
282
212static int perf_header__adds_write(struct perf_header *self, int fd) 283static int perf_header__adds_write(struct perf_header *self, int fd)
213{ 284{
214 int nr_sections; 285 int nr_sections;
@@ -258,6 +329,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
258 goto out_free; 329 goto out_free;
259 } 330 }
260 buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; 331 buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset;
332 dsos__cache_build_ids();
261 } 333 }
262 334
263 lseek(fd, sec_start, SEEK_SET); 335 lseek(fd, sec_start, SEEK_SET);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index ab92763edb03..79ca6a099f96 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -22,6 +22,7 @@
22enum dso_origin { 22enum dso_origin {
23 DSO__ORIG_KERNEL = 0, 23 DSO__ORIG_KERNEL = 0,
24 DSO__ORIG_JAVA_JIT, 24 DSO__ORIG_JAVA_JIT,
25 DSO__ORIG_BUILD_ID_CACHE,
25 DSO__ORIG_FEDORA, 26 DSO__ORIG_FEDORA,
26 DSO__ORIG_UBUNTU, 27 DSO__ORIG_UBUNTU,
27 DSO__ORIG_BUILDID, 28 DSO__ORIG_BUILDID,
@@ -1191,6 +1192,7 @@ char dso__symtab_origin(const struct dso *self)
1191 static const char origin[] = { 1192 static const char origin[] = {
1192 [DSO__ORIG_KERNEL] = 'k', 1193 [DSO__ORIG_KERNEL] = 'k',
1193 [DSO__ORIG_JAVA_JIT] = 'j', 1194 [DSO__ORIG_JAVA_JIT] = 'j',
1195 [DSO__ORIG_BUILD_ID_CACHE] = 'B',
1194 [DSO__ORIG_FEDORA] = 'f', 1196 [DSO__ORIG_FEDORA] = 'f',
1195 [DSO__ORIG_UBUNTU] = 'u', 1197 [DSO__ORIG_UBUNTU] = 'u',
1196 [DSO__ORIG_BUILDID] = 'b', 1198 [DSO__ORIG_BUILDID] = 'b',
@@ -1209,6 +1211,7 @@ int dso__load(struct dso *self, struct map *map, struct perf_session *session,
1209 int size = PATH_MAX; 1211 int size = PATH_MAX;
1210 char *name; 1212 char *name;
1211 u8 build_id[BUILD_ID_SIZE]; 1213 u8 build_id[BUILD_ID_SIZE];
1214 char build_id_hex[BUILD_ID_SIZE * 2 + 1];
1212 int ret = -1; 1215 int ret = -1;
1213 int fd; 1216 int fd;
1214 1217
@@ -1230,8 +1233,16 @@ int dso__load(struct dso *self, struct map *map, struct perf_session *session,
1230 return ret; 1233 return ret;
1231 } 1234 }
1232 1235
1233 self->origin = DSO__ORIG_FEDORA - 1; 1236 self->origin = DSO__ORIG_BUILD_ID_CACHE;
1234 1237
1238 if (self->has_build_id) {
1239 build_id__sprintf(self->build_id, sizeof(self->build_id),
1240 build_id_hex);
1241 snprintf(name, size, "%s/%s/.build-id/%.2s/%s",
1242 getenv("HOME"), DEBUG_CACHE_DIR,
1243 build_id_hex, build_id_hex + 2);
1244 goto open_file;
1245 }
1235more: 1246more:
1236 do { 1247 do {
1237 self->origin++; 1248 self->origin++;
@@ -1247,8 +1258,6 @@ more:
1247 case DSO__ORIG_BUILDID: 1258 case DSO__ORIG_BUILDID:
1248 if (filename__read_build_id(self->long_name, build_id, 1259 if (filename__read_build_id(self->long_name, build_id,
1249 sizeof(build_id))) { 1260 sizeof(build_id))) {
1250 char build_id_hex[BUILD_ID_SIZE * 2 + 1];
1251
1252 build_id__sprintf(build_id, sizeof(build_id), 1261 build_id__sprintf(build_id, sizeof(build_id),
1253 build_id_hex); 1262 build_id_hex);
1254 snprintf(name, size, 1263 snprintf(name, size,
@@ -1276,7 +1285,7 @@ compare_build_id:
1276 if (!dso__build_id_equal(self, build_id)) 1285 if (!dso__build_id_equal(self, build_id))
1277 goto more; 1286 goto more;
1278 } 1287 }
1279 1288open_file:
1280 fd = open(name, O_RDONLY); 1289 fd = open(name, O_RDONLY);
1281 } while (fd < 0); 1290 } while (fd < 0);
1282 1291
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 9eabd60f819d..f27e158943e9 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -8,6 +8,8 @@
8#include <linux/rbtree.h> 8#include <linux/rbtree.h>
9#include "event.h" 9#include "event.h"
10 10
11#define DEBUG_CACHE_DIR ".debug"
12
11#ifdef HAVE_CPLUS_DEMANGLE 13#ifdef HAVE_CPLUS_DEMANGLE
12extern char *cplus_demangle(const char *, int); 14extern char *cplus_demangle(const char *, int);
13 15
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
new file mode 100644
index 000000000000..f3c0798a5e78
--- /dev/null
+++ b/tools/perf/util/util.c
@@ -0,0 +1,69 @@
1#include <sys/mman.h>
2#include <sys/stat.h>
3#include <sys/types.h>
4#include <fcntl.h>
5#include <string.h>
6#include <unistd.h>
7#include "util.h"
8
9int mkdir_p(char *path, mode_t mode)
10{
11 struct stat st;
12 int err;
13 char *d = path;
14
15 if (*d != '/')
16 return -1;
17
18 if (stat(path, &st) == 0)
19 return 0;
20
21 while (*++d == '/');
22
23 while ((d = strchr(d, '/'))) {
24 *d = '\0';
25 err = stat(path, &st) && mkdir(path, mode);
26 *d++ = '/';
27 if (err)
28 return -1;
29 while (*d == '/')
30 ++d;
31 }
32 return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;
33}
34
35int copyfile(const char *from, const char *to)
36{
37 int fromfd, tofd;
38 struct stat st;
39 void *addr;
40 int err = -1;
41
42 if (stat(from, &st))
43 goto out;
44
45 fromfd = open(from, O_RDONLY);
46 if (fromfd < 0)
47 goto out;
48
49 tofd = creat(to, 0755);
50 if (tofd < 0)
51 goto out_close_from;
52
53 addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0);
54 if (addr == MAP_FAILED)
55 goto out_close_to;
56
57 if (write(tofd, addr, st.st_size) == st.st_size)
58 err = 0;
59
60 munmap(addr, st.st_size);
61out_close_to:
62 close(tofd);
63 if (err)
64 unlink(to);
65out_close_from:
66 close(fromfd);
67out:
68 return err;
69}
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index c673d8825883..0f5b2a6f1080 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -403,4 +403,7 @@ void git_qsort(void *base, size_t nmemb, size_t size,
403#endif 403#endif
404#endif 404#endif
405 405
406int mkdir_p(char *path, mode_t mode);
407int copyfile(const char *from, const char *to);
408
406#endif 409#endif