]> git.karo-electronics.de Git - karo-tx-linux.git/blob - tools/perf/builtin-buildid-cache.c
Merge tag 'for-4.12/dm-fixes-3' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / tools / perf / builtin-buildid-cache.c
1 /*
2  * builtin-buildid-cache.c
3  *
4  * Builtin buildid-cache command: Manages build-id cache
5  *
6  * Copyright (C) 2010, Red Hat Inc.
7  * Copyright (C) 2010, Arnaldo Carvalho de Melo <acme@redhat.com>
8  */
9 #include <sys/types.h>
10 #include <sys/time.h>
11 #include <time.h>
12 #include <dirent.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include "builtin.h"
16 #include "perf.h"
17 #include "util/cache.h"
18 #include "util/debug.h"
19 #include "util/header.h"
20 #include <subcmd/parse-options.h>
21 #include "util/strlist.h"
22 #include "util/build-id.h"
23 #include "util/session.h"
24 #include "util/symbol.h"
25 #include "util/time-utils.h"
26
27 static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid)
28 {
29         char root_dir[PATH_MAX];
30         char *p;
31
32         strlcpy(root_dir, proc_dir, sizeof(root_dir));
33
34         p = strrchr(root_dir, '/');
35         if (!p)
36                 return -1;
37         *p = '\0';
38         return sysfs__sprintf_build_id(root_dir, sbuildid);
39 }
40
41 static int build_id_cache__kcore_dir(char *dir, size_t sz)
42 {
43         return fetch_current_timestamp(dir, sz);
44 }
45
46 static bool same_kallsyms_reloc(const char *from_dir, char *to_dir)
47 {
48         char from[PATH_MAX];
49         char to[PATH_MAX];
50         const char *name;
51         u64 addr1 = 0, addr2 = 0;
52         int i, err = -1;
53
54         scnprintf(from, sizeof(from), "%s/kallsyms", from_dir);
55         scnprintf(to, sizeof(to), "%s/kallsyms", to_dir);
56
57         for (i = 0; (name = ref_reloc_sym_names[i]) != NULL; i++) {
58                 err = kallsyms__get_function_start(from, name, &addr1);
59                 if (!err)
60                         break;
61         }
62
63         if (err)
64                 return false;
65
66         if (kallsyms__get_function_start(to, name, &addr2))
67                 return false;
68
69         return addr1 == addr2;
70 }
71
72 static int build_id_cache__kcore_existing(const char *from_dir, char *to_dir,
73                                           size_t to_dir_sz)
74 {
75         char from[PATH_MAX];
76         char to[PATH_MAX];
77         char to_subdir[PATH_MAX];
78         struct dirent *dent;
79         int ret = -1;
80         DIR *d;
81
82         d = opendir(to_dir);
83         if (!d)
84                 return -1;
85
86         scnprintf(from, sizeof(from), "%s/modules", from_dir);
87
88         while (1) {
89                 dent = readdir(d);
90                 if (!dent)
91                         break;
92                 if (dent->d_type != DT_DIR)
93                         continue;
94                 scnprintf(to, sizeof(to), "%s/%s/modules", to_dir,
95                           dent->d_name);
96                 scnprintf(to_subdir, sizeof(to_subdir), "%s/%s",
97                           to_dir, dent->d_name);
98                 if (!compare_proc_modules(from, to) &&
99                     same_kallsyms_reloc(from_dir, to_subdir)) {
100                         strlcpy(to_dir, to_subdir, to_dir_sz);
101                         ret = 0;
102                         break;
103                 }
104         }
105
106         closedir(d);
107
108         return ret;
109 }
110
111 static int build_id_cache__add_kcore(const char *filename, bool force)
112 {
113         char dir[32], sbuildid[SBUILD_ID_SIZE];
114         char from_dir[PATH_MAX], to_dir[PATH_MAX];
115         char *p;
116
117         strlcpy(from_dir, filename, sizeof(from_dir));
118
119         p = strrchr(from_dir, '/');
120         if (!p || strcmp(p + 1, "kcore"))
121                 return -1;
122         *p = '\0';
123
124         if (build_id_cache__kcore_buildid(from_dir, sbuildid) < 0)
125                 return -1;
126
127         scnprintf(to_dir, sizeof(to_dir), "%s/%s/%s",
128                   buildid_dir, DSO__NAME_KCORE, sbuildid);
129
130         if (!force &&
131             !build_id_cache__kcore_existing(from_dir, to_dir, sizeof(to_dir))) {
132                 pr_debug("same kcore found in %s\n", to_dir);
133                 return 0;
134         }
135
136         if (build_id_cache__kcore_dir(dir, sizeof(dir)))
137                 return -1;
138
139         scnprintf(to_dir, sizeof(to_dir), "%s/%s/%s/%s",
140                   buildid_dir, DSO__NAME_KCORE, sbuildid, dir);
141
142         if (mkdir_p(to_dir, 0755))
143                 return -1;
144
145         if (kcore_copy(from_dir, to_dir)) {
146                 /* Remove YYYYmmddHHMMSShh directory */
147                 if (!rmdir(to_dir)) {
148                         p = strrchr(to_dir, '/');
149                         if (p)
150                                 *p = '\0';
151                         /* Try to remove buildid directory */
152                         if (!rmdir(to_dir)) {
153                                 p = strrchr(to_dir, '/');
154                                 if (p)
155                                         *p = '\0';
156                                 /* Try to remove [kernel.kcore] directory */
157                                 rmdir(to_dir);
158                         }
159                 }
160                 return -1;
161         }
162
163         pr_debug("kcore added to build-id cache directory %s\n", to_dir);
164
165         return 0;
166 }
167
168 static int build_id_cache__add_file(const char *filename)
169 {
170         char sbuild_id[SBUILD_ID_SIZE];
171         u8 build_id[BUILD_ID_SIZE];
172         int err;
173
174         if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
175                 pr_debug("Couldn't read a build-id in %s\n", filename);
176                 return -1;
177         }
178
179         build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
180         err = build_id_cache__add_s(sbuild_id, filename,
181                                     false, false);
182         pr_debug("Adding %s %s: %s\n", sbuild_id, filename,
183                  err ? "FAIL" : "Ok");
184         return err;
185 }
186
187 static int build_id_cache__remove_file(const char *filename)
188 {
189         u8 build_id[BUILD_ID_SIZE];
190         char sbuild_id[SBUILD_ID_SIZE];
191
192         int err;
193
194         if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
195                 pr_debug("Couldn't read a build-id in %s\n", filename);
196                 return -1;
197         }
198
199         build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
200         err = build_id_cache__remove_s(sbuild_id);
201         pr_debug("Removing %s %s: %s\n", sbuild_id, filename,
202                  err ? "FAIL" : "Ok");
203
204         return err;
205 }
206
207 static int build_id_cache__purge_path(const char *pathname)
208 {
209         struct strlist *list;
210         struct str_node *pos;
211         int err;
212
213         err = build_id_cache__list_build_ids(pathname, &list);
214         if (err)
215                 goto out;
216
217         strlist__for_each_entry(pos, list) {
218                 err = build_id_cache__remove_s(pos->s);
219                 pr_debug("Removing %s %s: %s\n", pos->s, pathname,
220                          err ? "FAIL" : "Ok");
221                 if (err)
222                         break;
223         }
224         strlist__delete(list);
225
226 out:
227         pr_debug("Purging %s: %s\n", pathname, err ? "FAIL" : "Ok");
228
229         return err;
230 }
231
232 static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
233 {
234         char filename[PATH_MAX];
235         u8 build_id[BUILD_ID_SIZE];
236
237         if (dso__build_id_filename(dso, filename, sizeof(filename)) &&
238             filename__read_build_id(filename, build_id,
239                                     sizeof(build_id)) != sizeof(build_id)) {
240                 if (errno == ENOENT)
241                         return false;
242
243                 pr_warning("Problems with %s file, consider removing it from the cache\n",
244                            filename);
245         } else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) {
246                 pr_warning("Problems with %s file, consider removing it from the cache\n",
247                            filename);
248         }
249
250         return true;
251 }
252
253 static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *fp)
254 {
255         perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0);
256         return 0;
257 }
258
259 static int build_id_cache__update_file(const char *filename)
260 {
261         u8 build_id[BUILD_ID_SIZE];
262         char sbuild_id[SBUILD_ID_SIZE];
263
264         int err = 0;
265
266         if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
267                 pr_debug("Couldn't read a build-id in %s\n", filename);
268                 return -1;
269         }
270
271         build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
272         if (build_id_cache__cached(sbuild_id))
273                 err = build_id_cache__remove_s(sbuild_id);
274
275         if (!err)
276                 err = build_id_cache__add_s(sbuild_id, filename, false, false);
277
278         pr_debug("Updating %s %s: %s\n", sbuild_id, filename,
279                  err ? "FAIL" : "Ok");
280
281         return err;
282 }
283
284 int cmd_buildid_cache(int argc, const char **argv)
285 {
286         struct strlist *list;
287         struct str_node *pos;
288         int ret = 0;
289         bool force = false;
290         char const *add_name_list_str = NULL,
291                    *remove_name_list_str = NULL,
292                    *purge_name_list_str = NULL,
293                    *missing_filename = NULL,
294                    *update_name_list_str = NULL,
295                    *kcore_filename = NULL;
296         char sbuf[STRERR_BUFSIZE];
297
298         struct perf_data_file file = {
299                 .mode  = PERF_DATA_MODE_READ,
300         };
301         struct perf_session *session = NULL;
302
303         const struct option buildid_cache_options[] = {
304         OPT_STRING('a', "add", &add_name_list_str,
305                    "file list", "file(s) to add"),
306         OPT_STRING('k', "kcore", &kcore_filename,
307                    "file", "kcore file to add"),
308         OPT_STRING('r', "remove", &remove_name_list_str, "file list",
309                     "file(s) to remove"),
310         OPT_STRING('p', "purge", &purge_name_list_str, "path list",
311                     "path(s) to remove (remove old caches too)"),
312         OPT_STRING('M', "missing", &missing_filename, "file",
313                    "to find missing build ids in the cache"),
314         OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
315         OPT_STRING('u', "update", &update_name_list_str, "file list",
316                     "file(s) to update"),
317         OPT_INCR('v', "verbose", &verbose, "be more verbose"),
318         OPT_END()
319         };
320         const char * const buildid_cache_usage[] = {
321                 "perf buildid-cache [<options>]",
322                 NULL
323         };
324
325         argc = parse_options(argc, argv, buildid_cache_options,
326                              buildid_cache_usage, 0);
327
328         if (argc || (!add_name_list_str && !kcore_filename &&
329                      !remove_name_list_str && !purge_name_list_str &&
330                      !missing_filename && !update_name_list_str))
331                 usage_with_options(buildid_cache_usage, buildid_cache_options);
332
333         if (missing_filename) {
334                 file.path = missing_filename;
335                 file.force = force;
336
337                 session = perf_session__new(&file, false, NULL);
338                 if (session == NULL)
339                         return -1;
340         }
341
342         if (symbol__init(session ? &session->header.env : NULL) < 0)
343                 goto out;
344
345         setup_pager();
346
347         if (add_name_list_str) {
348                 list = strlist__new(add_name_list_str, NULL);
349                 if (list) {
350                         strlist__for_each_entry(pos, list)
351                                 if (build_id_cache__add_file(pos->s)) {
352                                         if (errno == EEXIST) {
353                                                 pr_debug("%s already in the cache\n",
354                                                          pos->s);
355                                                 continue;
356                                         }
357                                         pr_warning("Couldn't add %s: %s\n",
358                                                    pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
359                                 }
360
361                         strlist__delete(list);
362                 }
363         }
364
365         if (remove_name_list_str) {
366                 list = strlist__new(remove_name_list_str, NULL);
367                 if (list) {
368                         strlist__for_each_entry(pos, list)
369                                 if (build_id_cache__remove_file(pos->s)) {
370                                         if (errno == ENOENT) {
371                                                 pr_debug("%s wasn't in the cache\n",
372                                                          pos->s);
373                                                 continue;
374                                         }
375                                         pr_warning("Couldn't remove %s: %s\n",
376                                                    pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
377                                 }
378
379                         strlist__delete(list);
380                 }
381         }
382
383         if (purge_name_list_str) {
384                 list = strlist__new(purge_name_list_str, NULL);
385                 if (list) {
386                         strlist__for_each_entry(pos, list)
387                                 if (build_id_cache__purge_path(pos->s)) {
388                                         if (errno == ENOENT) {
389                                                 pr_debug("%s wasn't in the cache\n",
390                                                          pos->s);
391                                                 continue;
392                                         }
393                                         pr_warning("Couldn't remove %s: %s\n",
394                                                    pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
395                                 }
396
397                         strlist__delete(list);
398                 }
399         }
400
401         if (missing_filename)
402                 ret = build_id_cache__fprintf_missing(session, stdout);
403
404         if (update_name_list_str) {
405                 list = strlist__new(update_name_list_str, NULL);
406                 if (list) {
407                         strlist__for_each_entry(pos, list)
408                                 if (build_id_cache__update_file(pos->s)) {
409                                         if (errno == ENOENT) {
410                                                 pr_debug("%s wasn't in the cache\n",
411                                                          pos->s);
412                                                 continue;
413                                         }
414                                         pr_warning("Couldn't update %s: %s\n",
415                                                    pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
416                                 }
417
418                         strlist__delete(list);
419                 }
420         }
421
422         if (kcore_filename && build_id_cache__add_kcore(kcore_filename, force))
423                 pr_warning("Couldn't add %s\n", kcore_filename);
424
425 out:
426         perf_session__delete(session);
427
428         return ret;
429 }