diff options
-rw-r--r-- | tools/perf/util/machine.c | 4 | ||||
-rw-r--r-- | tools/perf/util/map.c | 23 | ||||
-rw-r--r-- | tools/perf/util/map.h | 2 | ||||
-rw-r--r-- | tools/perf/util/namespaces.c | 83 | ||||
-rw-r--r-- | tools/perf/util/namespaces.h | 5 | ||||
-rw-r--r-- | tools/perf/util/symbol.c | 70 |
6 files changed, 160 insertions, 27 deletions
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 2e9eb6aa3ce2..246b441110a1 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1392,7 +1392,7 @@ int machine__process_mmap2_event(struct machine *machine, | |||
1392 | 1392 | ||
1393 | map = map__new(machine, event->mmap2.start, | 1393 | map = map__new(machine, event->mmap2.start, |
1394 | event->mmap2.len, event->mmap2.pgoff, | 1394 | event->mmap2.len, event->mmap2.pgoff, |
1395 | event->mmap2.pid, event->mmap2.maj, | 1395 | event->mmap2.maj, |
1396 | event->mmap2.min, event->mmap2.ino, | 1396 | event->mmap2.min, event->mmap2.ino, |
1397 | event->mmap2.ino_generation, | 1397 | event->mmap2.ino_generation, |
1398 | event->mmap2.prot, | 1398 | event->mmap2.prot, |
@@ -1450,7 +1450,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
1450 | 1450 | ||
1451 | map = map__new(machine, event->mmap.start, | 1451 | map = map__new(machine, event->mmap.start, |
1452 | event->mmap.len, event->mmap.pgoff, | 1452 | event->mmap.len, event->mmap.pgoff, |
1453 | event->mmap.pid, 0, 0, 0, 0, 0, 0, | 1453 | 0, 0, 0, 0, 0, 0, |
1454 | event->mmap.filename, | 1454 | event->mmap.filename, |
1455 | type, thread); | 1455 | type, thread); |
1456 | 1456 | ||
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 5dc60ca5a294..bdaa0a4edc17 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -146,11 +146,13 @@ void map__init(struct map *map, enum map_type type, | |||
146 | } | 146 | } |
147 | 147 | ||
148 | struct map *map__new(struct machine *machine, u64 start, u64 len, | 148 | struct map *map__new(struct machine *machine, u64 start, u64 len, |
149 | u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, | 149 | u64 pgoff, u32 d_maj, u32 d_min, u64 ino, |
150 | u64 ino_gen, u32 prot, u32 flags, char *filename, | 150 | u64 ino_gen, u32 prot, u32 flags, char *filename, |
151 | enum map_type type, struct thread *thread) | 151 | enum map_type type, struct thread *thread) |
152 | { | 152 | { |
153 | struct map *map = malloc(sizeof(*map)); | 153 | struct map *map = malloc(sizeof(*map)); |
154 | struct nsinfo *nsi = NULL; | ||
155 | struct nsinfo *nnsi; | ||
154 | 156 | ||
155 | if (map != NULL) { | 157 | if (map != NULL) { |
156 | char newfilename[PATH_MAX]; | 158 | char newfilename[PATH_MAX]; |
@@ -168,9 +170,11 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, | |||
168 | map->ino_generation = ino_gen; | 170 | map->ino_generation = ino_gen; |
169 | map->prot = prot; | 171 | map->prot = prot; |
170 | map->flags = flags; | 172 | map->flags = flags; |
173 | nsi = nsinfo__get(thread->nsinfo); | ||
171 | 174 | ||
172 | if ((anon || no_dso) && type == MAP__FUNCTION) { | 175 | if ((anon || no_dso) && nsi && type == MAP__FUNCTION) { |
173 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); | 176 | snprintf(newfilename, sizeof(newfilename), |
177 | "/tmp/perf-%d.map", nsi->pid); | ||
174 | filename = newfilename; | 178 | filename = newfilename; |
175 | } | 179 | } |
176 | 180 | ||
@@ -180,6 +184,16 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, | |||
180 | } | 184 | } |
181 | 185 | ||
182 | if (vdso) { | 186 | if (vdso) { |
187 | /* The vdso maps are always on the host and not the | ||
188 | * container. Ensure that we don't use setns to look | ||
189 | * them up. | ||
190 | */ | ||
191 | nnsi = nsinfo__copy(nsi); | ||
192 | if (nnsi) { | ||
193 | nsinfo__put(nsi); | ||
194 | nnsi->need_setns = false; | ||
195 | nsi = nnsi; | ||
196 | } | ||
183 | pgoff = 0; | 197 | pgoff = 0; |
184 | dso = machine__findnew_vdso(machine, thread); | 198 | dso = machine__findnew_vdso(machine, thread); |
185 | } else | 199 | } else |
@@ -201,11 +215,12 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, | |||
201 | if (type != MAP__FUNCTION) | 215 | if (type != MAP__FUNCTION) |
202 | dso__set_loaded(dso, map->type); | 216 | dso__set_loaded(dso, map->type); |
203 | } | 217 | } |
204 | dso->nsinfo = nsinfo__get(thread->nsinfo); | 218 | dso->nsinfo = nsi; |
205 | dso__put(dso); | 219 | dso__put(dso); |
206 | } | 220 | } |
207 | return map; | 221 | return map; |
208 | out_delete: | 222 | out_delete: |
223 | nsinfo__put(nsi); | ||
209 | free(map); | 224 | free(map); |
210 | return NULL; | 225 | return NULL; |
211 | } | 226 | } |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index f9e8ac8a52cd..73aacf7a7dc4 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -141,7 +141,7 @@ struct thread; | |||
141 | void map__init(struct map *map, enum map_type type, | 141 | void map__init(struct map *map, enum map_type type, |
142 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 142 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
143 | struct map *map__new(struct machine *machine, u64 start, u64 len, | 143 | struct map *map__new(struct machine *machine, u64 start, u64 len, |
144 | u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, | 144 | u64 pgoff, u32 d_maj, u32 d_min, u64 ino, |
145 | u64 ino_gen, u32 prot, u32 flags, | 145 | u64 ino_gen, u32 prot, u32 flags, |
146 | char *filename, enum map_type type, struct thread *thread); | 146 | char *filename, enum map_type type, struct thread *thread); |
147 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 147 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c index bcc6bb19cf10..fc5f398779a4 100644 --- a/tools/perf/util/namespaces.c +++ b/tools/perf/util/namespaces.c | |||
@@ -40,18 +40,23 @@ void namespaces__free(struct namespaces *namespaces) | |||
40 | free(namespaces); | 40 | free(namespaces); |
41 | } | 41 | } |
42 | 42 | ||
43 | void nsinfo__init(struct nsinfo *nsi) | 43 | int nsinfo__init(struct nsinfo *nsi) |
44 | { | 44 | { |
45 | char oldns[PATH_MAX]; | 45 | char oldns[PATH_MAX]; |
46 | char spath[PATH_MAX]; | ||
46 | char *newns = NULL; | 47 | char *newns = NULL; |
48 | char *statln = NULL; | ||
47 | struct stat old_stat; | 49 | struct stat old_stat; |
48 | struct stat new_stat; | 50 | struct stat new_stat; |
51 | FILE *f = NULL; | ||
52 | size_t linesz = 0; | ||
53 | int rv = -1; | ||
49 | 54 | ||
50 | if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) | 55 | if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) |
51 | return; | 56 | return rv; |
52 | 57 | ||
53 | if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1) | 58 | if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1) |
54 | return; | 59 | return rv; |
55 | 60 | ||
56 | if (stat(oldns, &old_stat) < 0) | 61 | if (stat(oldns, &old_stat) < 0) |
57 | goto out; | 62 | goto out; |
@@ -68,24 +73,89 @@ void nsinfo__init(struct nsinfo *nsi) | |||
68 | newns = NULL; | 73 | newns = NULL; |
69 | } | 74 | } |
70 | 75 | ||
76 | /* If we're dealing with a process that is in a different PID namespace, | ||
77 | * attempt to work out the innermost tgid for the process. | ||
78 | */ | ||
79 | if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsi->pid) >= PATH_MAX) | ||
80 | goto out; | ||
81 | |||
82 | f = fopen(spath, "r"); | ||
83 | if (f == NULL) | ||
84 | goto out; | ||
85 | |||
86 | while (getline(&statln, &linesz, f) != -1) { | ||
87 | /* Use tgid if CONFIG_PID_NS is not defined. */ | ||
88 | if (strstr(statln, "Tgid:") != NULL) { | ||
89 | nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'), | ||
90 | NULL, 10); | ||
91 | nsi->nstgid = nsi->tgid; | ||
92 | } | ||
93 | |||
94 | if (strstr(statln, "NStgid:") != NULL) { | ||
95 | nsi->nstgid = (pid_t)strtol(strrchr(statln, '\t'), | ||
96 | NULL, 10); | ||
97 | break; | ||
98 | } | ||
99 | } | ||
100 | rv = 0; | ||
101 | |||
71 | out: | 102 | out: |
103 | if (f != NULL) | ||
104 | (void) fclose(f); | ||
105 | free(statln); | ||
72 | free(newns); | 106 | free(newns); |
107 | return rv; | ||
73 | } | 108 | } |
74 | 109 | ||
75 | struct nsinfo *nsinfo__new(pid_t pid) | 110 | struct nsinfo *nsinfo__new(pid_t pid) |
76 | { | 111 | { |
77 | struct nsinfo *nsi = calloc(1, sizeof(*nsi)); | 112 | struct nsinfo *nsi; |
78 | 113 | ||
114 | if (pid == 0) | ||
115 | return NULL; | ||
116 | |||
117 | nsi = calloc(1, sizeof(*nsi)); | ||
79 | if (nsi != NULL) { | 118 | if (nsi != NULL) { |
80 | nsi->pid = pid; | 119 | nsi->pid = pid; |
120 | nsi->tgid = pid; | ||
121 | nsi->nstgid = pid; | ||
81 | nsi->need_setns = false; | 122 | nsi->need_setns = false; |
82 | nsinfo__init(nsi); | 123 | /* Init may fail if the process exits while we're trying to look |
124 | * at its proc information. In that case, save the pid but | ||
125 | * don't try to enter the namespace. | ||
126 | */ | ||
127 | if (nsinfo__init(nsi) == -1) | ||
128 | nsi->need_setns = false; | ||
129 | |||
83 | refcount_set(&nsi->refcnt, 1); | 130 | refcount_set(&nsi->refcnt, 1); |
84 | } | 131 | } |
85 | 132 | ||
86 | return nsi; | 133 | return nsi; |
87 | } | 134 | } |
88 | 135 | ||
136 | struct nsinfo *nsinfo__copy(struct nsinfo *nsi) | ||
137 | { | ||
138 | struct nsinfo *nnsi; | ||
139 | |||
140 | nnsi = calloc(1, sizeof(*nnsi)); | ||
141 | if (nnsi != NULL) { | ||
142 | nnsi->pid = nsi->pid; | ||
143 | nnsi->tgid = nsi->tgid; | ||
144 | nnsi->nstgid = nsi->nstgid; | ||
145 | nnsi->need_setns = nsi->need_setns; | ||
146 | if (nsi->mntns_path) { | ||
147 | nnsi->mntns_path = strdup(nsi->mntns_path); | ||
148 | if (!nnsi->mntns_path) { | ||
149 | free(nnsi); | ||
150 | return NULL; | ||
151 | } | ||
152 | } | ||
153 | refcount_set(&nnsi->refcnt, 1); | ||
154 | } | ||
155 | |||
156 | return nnsi; | ||
157 | } | ||
158 | |||
89 | void nsinfo__delete(struct nsinfo *nsi) | 159 | void nsinfo__delete(struct nsinfo *nsi) |
90 | { | 160 | { |
91 | zfree(&nsi->mntns_path); | 161 | zfree(&nsi->mntns_path); |
@@ -105,7 +175,8 @@ void nsinfo__put(struct nsinfo *nsi) | |||
105 | nsinfo__delete(nsi); | 175 | nsinfo__delete(nsi); |
106 | } | 176 | } |
107 | 177 | ||
108 | void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc) | 178 | void nsinfo__mountns_enter(struct nsinfo *nsi, |
179 | struct nscookie *nc) | ||
109 | { | 180 | { |
110 | char curpath[PATH_MAX]; | 181 | char curpath[PATH_MAX]; |
111 | int oldns = -1; | 182 | int oldns = -1; |
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h index b20f6ead7b2d..f19aa41119ae 100644 --- a/tools/perf/util/namespaces.h +++ b/tools/perf/util/namespaces.h | |||
@@ -26,6 +26,8 @@ void namespaces__free(struct namespaces *namespaces); | |||
26 | 26 | ||
27 | struct nsinfo { | 27 | struct nsinfo { |
28 | pid_t pid; | 28 | pid_t pid; |
29 | pid_t tgid; | ||
30 | pid_t nstgid; | ||
29 | bool need_setns; | 31 | bool need_setns; |
30 | char *mntns_path; | 32 | char *mntns_path; |
31 | refcount_t refcnt; | 33 | refcount_t refcnt; |
@@ -36,8 +38,9 @@ struct nscookie { | |||
36 | int newns; | 38 | int newns; |
37 | }; | 39 | }; |
38 | 40 | ||
39 | void nsinfo__init(struct nsinfo *nsi); | 41 | int nsinfo__init(struct nsinfo *nsi); |
40 | struct nsinfo *nsinfo__new(pid_t pid); | 42 | struct nsinfo *nsinfo__new(pid_t pid); |
43 | struct nsinfo *nsinfo__copy(struct nsinfo *nsi); | ||
41 | void nsinfo__delete(struct nsinfo *nsi); | 44 | void nsinfo__delete(struct nsinfo *nsi); |
42 | 45 | ||
43 | struct nsinfo *nsinfo__get(struct nsinfo *nsi); | 46 | struct nsinfo *nsinfo__get(struct nsinfo *nsi); |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 60a9eaa372ef..21c97cc41cfc 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -19,7 +19,6 @@ | |||
19 | #include "strlist.h" | 19 | #include "strlist.h" |
20 | #include "intlist.h" | 20 | #include "intlist.h" |
21 | #include "namespaces.h" | 21 | #include "namespaces.h" |
22 | #include "vdso.h" | ||
23 | #include "header.h" | 22 | #include "header.h" |
24 | #include "path.h" | 23 | #include "path.h" |
25 | #include "sane_ctype.h" | 24 | #include "sane_ctype.h" |
@@ -1327,14 +1326,15 @@ int dso__load_kallsyms(struct dso *dso, const char *filename, | |||
1327 | return __dso__load_kallsyms(dso, filename, map, false); | 1326 | return __dso__load_kallsyms(dso, filename, map, false); |
1328 | } | 1327 | } |
1329 | 1328 | ||
1330 | static int dso__load_perf_map(struct dso *dso, struct map *map) | 1329 | static int dso__load_perf_map(const char *map_path, struct dso *dso, |
1330 | struct map *map) | ||
1331 | { | 1331 | { |
1332 | char *line = NULL; | 1332 | char *line = NULL; |
1333 | size_t n; | 1333 | size_t n; |
1334 | FILE *file; | 1334 | FILE *file; |
1335 | int nr_syms = 0; | 1335 | int nr_syms = 0; |
1336 | 1336 | ||
1337 | file = fopen(dso->long_name, "r"); | 1337 | file = fopen(map_path, "r"); |
1338 | if (file == NULL) | 1338 | if (file == NULL) |
1339 | goto out_failure; | 1339 | goto out_failure; |
1340 | 1340 | ||
@@ -1426,6 +1426,45 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, | |||
1426 | } | 1426 | } |
1427 | } | 1427 | } |
1428 | 1428 | ||
1429 | /* Checks for the existence of the perf-<pid>.map file in two different | ||
1430 | * locations. First, if the process is a separate mount namespace, check in | ||
1431 | * that namespace using the pid of the innermost pid namespace. If's not in a | ||
1432 | * namespace, or the file can't be found there, try in the mount namespace of | ||
1433 | * the tracing process using our view of its pid. | ||
1434 | */ | ||
1435 | static int dso__find_perf_map(char *filebuf, size_t bufsz, | ||
1436 | struct nsinfo **nsip) | ||
1437 | { | ||
1438 | struct nscookie nsc; | ||
1439 | struct nsinfo *nsi; | ||
1440 | struct nsinfo *nnsi; | ||
1441 | int rc = -1; | ||
1442 | |||
1443 | nsi = *nsip; | ||
1444 | |||
1445 | if (nsi->need_setns) { | ||
1446 | snprintf(filebuf, bufsz, "/tmp/perf-%d.map", nsi->nstgid); | ||
1447 | nsinfo__mountns_enter(nsi, &nsc); | ||
1448 | rc = access(filebuf, R_OK); | ||
1449 | nsinfo__mountns_exit(&nsc); | ||
1450 | if (rc == 0) | ||
1451 | return rc; | ||
1452 | } | ||
1453 | |||
1454 | nnsi = nsinfo__copy(nsi); | ||
1455 | if (nnsi) { | ||
1456 | nsinfo__put(nsi); | ||
1457 | |||
1458 | nnsi->need_setns = false; | ||
1459 | snprintf(filebuf, bufsz, "/tmp/perf-%d.map", nnsi->tgid); | ||
1460 | *nsip = nnsi; | ||
1461 | rc = 0; | ||
1462 | } | ||
1463 | |||
1464 | return rc; | ||
1465 | } | ||
1466 | |||
1467 | |||
1429 | int dso__load(struct dso *dso, struct map *map) | 1468 | int dso__load(struct dso *dso, struct map *map) |
1430 | { | 1469 | { |
1431 | char *name; | 1470 | char *name; |
@@ -1437,18 +1476,23 @@ int dso__load(struct dso *dso, struct map *map) | |||
1437 | struct symsrc ss_[2]; | 1476 | struct symsrc ss_[2]; |
1438 | struct symsrc *syms_ss = NULL, *runtime_ss = NULL; | 1477 | struct symsrc *syms_ss = NULL, *runtime_ss = NULL; |
1439 | bool kmod; | 1478 | bool kmod; |
1479 | bool perfmap; | ||
1440 | unsigned char build_id[BUILD_ID_SIZE]; | 1480 | unsigned char build_id[BUILD_ID_SIZE]; |
1441 | struct nscookie nsc; | 1481 | struct nscookie nsc; |
1482 | char newmapname[PATH_MAX]; | ||
1483 | const char *map_path = dso->long_name; | ||
1484 | |||
1485 | perfmap = strncmp(dso->name, "/tmp/perf-", 10) == 0; | ||
1486 | if (perfmap) { | ||
1487 | if (dso->nsinfo && (dso__find_perf_map(newmapname, | ||
1488 | sizeof(newmapname), &dso->nsinfo) == 0)) { | ||
1489 | map_path = newmapname; | ||
1490 | } | ||
1491 | } | ||
1442 | 1492 | ||
1443 | nsinfo__mountns_enter(dso->nsinfo, &nsc); | 1493 | nsinfo__mountns_enter(dso->nsinfo, &nsc); |
1444 | pthread_mutex_lock(&dso->lock); | 1494 | pthread_mutex_lock(&dso->lock); |
1445 | 1495 | ||
1446 | /* The vdso files always live in the host container, so don't go looking | ||
1447 | * for them in the container's mount namespace. | ||
1448 | */ | ||
1449 | if (dso__is_vdso(dso)) | ||
1450 | nsinfo__mountns_exit(&nsc); | ||
1451 | |||
1452 | /* check again under the dso->lock */ | 1496 | /* check again under the dso->lock */ |
1453 | if (dso__loaded(dso, map->type)) { | 1497 | if (dso__loaded(dso, map->type)) { |
1454 | ret = 1; | 1498 | ret = 1; |
@@ -1471,19 +1515,19 @@ int dso__load(struct dso *dso, struct map *map) | |||
1471 | 1515 | ||
1472 | dso->adjust_symbols = 0; | 1516 | dso->adjust_symbols = 0; |
1473 | 1517 | ||
1474 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { | 1518 | if (perfmap) { |
1475 | struct stat st; | 1519 | struct stat st; |
1476 | 1520 | ||
1477 | if (lstat(dso->name, &st) < 0) | 1521 | if (lstat(map_path, &st) < 0) |
1478 | goto out; | 1522 | goto out; |
1479 | 1523 | ||
1480 | if (!symbol_conf.force && st.st_uid && (st.st_uid != geteuid())) { | 1524 | if (!symbol_conf.force && st.st_uid && (st.st_uid != geteuid())) { |
1481 | pr_warning("File %s not owned by current user or root, " | 1525 | pr_warning("File %s not owned by current user or root, " |
1482 | "ignoring it (use -f to override).\n", dso->name); | 1526 | "ignoring it (use -f to override).\n", map_path); |
1483 | goto out; | 1527 | goto out; |
1484 | } | 1528 | } |
1485 | 1529 | ||
1486 | ret = dso__load_perf_map(dso, map); | 1530 | ret = dso__load_perf_map(map_path, dso, map); |
1487 | dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT : | 1531 | dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT : |
1488 | DSO_BINARY_TYPE__NOT_FOUND; | 1532 | DSO_BINARY_TYPE__NOT_FOUND; |
1489 | goto out; | 1533 | goto out; |