aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/header.c
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/header.c
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/header.c')
-rw-r--r--tools/perf/util/header.c82
1 files changed, 77 insertions, 5 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);