1 /*
2 * Copyright (c) 2016 GitHub, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/mman.h>
20 #include <ctype.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <math.h>
24 #include <stdbool.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "bcc_perf_map.h"
32 #include "bcc_proc.h"
33 #include "bcc_elf.h"
34
35 #ifdef __x86_64__
36 // https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt
37 const unsigned long long kernelAddrSpace = 0x00ffffffffffffff;
38 #else
39 const unsigned long long kernelAddrSpace = 0x0;
40 #endif
41
bcc_procutils_which(const char * binpath)42 char *bcc_procutils_which(const char *binpath) {
43 char buffer[4096];
44 const char *PATH;
45
46 if (strchr(binpath, '/'))
47 return bcc_elf_is_exe(binpath) ? strdup(binpath) : 0;
48
49 if (!(PATH = getenv("PATH")))
50 return 0;
51
52 while (PATH) {
53 const char *next = strchr(PATH, ':') ?: strchr(PATH, '\0');
54 const size_t path_len = next - PATH;
55
56 if (path_len) {
57 int ret = snprintf(buffer, sizeof(buffer), "%.*s/%s",
58 (int)path_len, PATH, binpath);
59 if (ret < 0 || ret >= sizeof(buffer))
60 return 0;
61
62 if (bcc_elf_is_exe(buffer))
63 return strdup(buffer);
64 }
65
66 PATH = *next ? (next + 1) : 0;
67 }
68
69 return 0;
70 }
71
72 #define STARTS_WITH(mapname, prefix) (!strncmp(mapname, prefix, sizeof(prefix)-1))
73
bcc_mapping_is_file_backed(const char * mapname)74 int bcc_mapping_is_file_backed(const char *mapname) {
75 return mapname[0] && !(
76 STARTS_WITH(mapname, "//anon") ||
77 STARTS_WITH(mapname, "/dev/zero") ||
78 STARTS_WITH(mapname, "/anon_hugepage") ||
79 STARTS_WITH(mapname, "[stack") ||
80 STARTS_WITH(mapname, "/SYSV") ||
81 STARTS_WITH(mapname, "[heap]") ||
82 STARTS_WITH(mapname, "[vsyscall]"));
83 }
84
bcc_procutils_each_module(int pid,bcc_procutils_modulecb callback,void * payload)85 int bcc_procutils_each_module(int pid, bcc_procutils_modulecb callback,
86 void *payload) {
87 char procmap_filename[128];
88 FILE *procmap;
89 snprintf(procmap_filename, sizeof(procmap_filename), "/proc/%ld/maps",
90 (long)pid);
91 procmap = fopen(procmap_filename, "r");
92 if (!procmap)
93 return -1;
94
95 char buf[PATH_MAX + 1], perm[5], dev[8];
96 char *name;
97 uint64_t begin, end, inode;
98 unsigned long long offset;
99 while (true) {
100 buf[0] = '\0';
101 // From fs/proc/task_mmu.c:show_map_vma
102 if (fscanf(procmap, "%lx-%lx %4s %llx %7s %lu%[^\n]", &begin, &end, perm,
103 &offset, dev, &inode, buf) != 7)
104 break;
105
106 if (perm[2] != 'x')
107 continue;
108
109 name = buf;
110 while (isspace(*name))
111 name++;
112 if (!bcc_mapping_is_file_backed(name))
113 continue;
114
115 if (callback(name, begin, end, (uint64_t)offset, true, payload) < 0)
116 break;
117 }
118
119 fclose(procmap);
120
121 // Address mapping for the entire address space maybe in /tmp/perf-<PID>.map
122 // This will be used if symbols aren't resolved in an earlier mapping.
123 char map_path[4096];
124 // Try perf-<PID>.map path with process's mount namespace, chroot and NSPID,
125 // in case it is generated by the process itself.
126 if (bcc_perf_map_path(map_path, sizeof(map_path), pid))
127 if (callback(map_path, 0, -1, 0, true, payload) < 0)
128 return 0;
129 // Try perf-<PID>.map path with global root and PID, in case it is generated
130 // by other Process. Avoid checking mount namespace for this.
131 int res = snprintf(map_path, 4096, "/tmp/perf-%d.map", pid);
132 if (res > 0 && res < 4096)
133 if (callback(map_path, 0, -1, 0, false, payload) < 0)
134 return 0;
135
136 return 0;
137 }
138
bcc_procutils_each_ksym(bcc_procutils_ksymcb callback,void * payload)139 int bcc_procutils_each_ksym(bcc_procutils_ksymcb callback, void *payload) {
140 char line[2048];
141 char *symname, *endsym;
142 FILE *kallsyms;
143 unsigned long long addr;
144
145 /* root is needed to list ksym addresses */
146 if (geteuid() != 0)
147 return -1;
148
149 kallsyms = fopen("/proc/kallsyms", "r");
150 if (!kallsyms)
151 return -1;
152
153 while (fgets(line, sizeof(line), kallsyms)) {
154 addr = strtoull(line, &symname, 16);
155 if (addr == 0 || addr == ULLONG_MAX)
156 continue;
157 if (addr < kernelAddrSpace)
158 continue;
159
160 symname++;
161 // Ignore data symbols
162 if (*symname == 'b' || *symname == 'B' || *symname == 'd' ||
163 *symname == 'D' || *symname == 'r' || *symname =='R')
164 continue;
165
166 endsym = (symname = symname + 2);
167 while (*endsym && !isspace(*endsym)) endsym++;
168 *endsym = '\0';
169
170 callback(symname, addr, payload);
171 }
172
173 fclose(kallsyms);
174 return 0;
175 }
176
177 #define CACHE1_HEADER "ld.so-1.7.0"
178 #define CACHE1_HEADER_LEN (sizeof(CACHE1_HEADER) - 1)
179
180 #define CACHE2_HEADER "glibc-ld.so.cache"
181 #define CACHE2_HEADER_LEN (sizeof(CACHE2_HEADER) - 1)
182 #define CACHE2_VERSION "1.1"
183
184 struct ld_cache1_entry {
185 int32_t flags;
186 uint32_t key;
187 uint32_t value;
188 };
189
190 struct ld_cache1 {
191 char header[CACHE1_HEADER_LEN];
192 uint32_t entry_count;
193 struct ld_cache1_entry entries[0];
194 };
195
196 struct ld_cache2_entry {
197 int32_t flags;
198 uint32_t key;
199 uint32_t value;
200 uint32_t pad1_;
201 uint64_t pad2_;
202 };
203
204 struct ld_cache2 {
205 char header[CACHE2_HEADER_LEN];
206 char version[3];
207 uint32_t entry_count;
208 uint32_t string_table_len;
209 uint32_t pad_[5];
210 struct ld_cache2_entry entries[0];
211 };
212
213 static int lib_cache_count;
214 static struct ld_lib {
215 char *libname;
216 char *path;
217 int flags;
218 } * lib_cache;
219
read_cache1(const char * ld_map)220 static int read_cache1(const char *ld_map) {
221 struct ld_cache1 *ldcache = (struct ld_cache1 *)ld_map;
222 const char *ldstrings =
223 (const char *)(ldcache->entries + ldcache->entry_count);
224 uint32_t i;
225
226 lib_cache =
227 (struct ld_lib *)malloc(ldcache->entry_count * sizeof(struct ld_lib));
228 lib_cache_count = (int)ldcache->entry_count;
229
230 for (i = 0; i < ldcache->entry_count; ++i) {
231 const char *key = ldstrings + ldcache->entries[i].key;
232 const char *val = ldstrings + ldcache->entries[i].value;
233 const int flags = ldcache->entries[i].flags;
234
235 lib_cache[i].libname = strdup(key);
236 lib_cache[i].path = strdup(val);
237 lib_cache[i].flags = flags;
238 }
239 return 0;
240 }
241
read_cache2(const char * ld_map)242 static int read_cache2(const char *ld_map) {
243 struct ld_cache2 *ldcache = (struct ld_cache2 *)ld_map;
244 uint32_t i;
245
246 if (memcmp(ld_map, CACHE2_HEADER, CACHE2_HEADER_LEN))
247 return -1;
248
249 lib_cache =
250 (struct ld_lib *)malloc(ldcache->entry_count * sizeof(struct ld_lib));
251 lib_cache_count = (int)ldcache->entry_count;
252
253 for (i = 0; i < ldcache->entry_count; ++i) {
254 const char *key = ld_map + ldcache->entries[i].key;
255 const char *val = ld_map + ldcache->entries[i].value;
256 const int flags = ldcache->entries[i].flags;
257
258 lib_cache[i].libname = strdup(key);
259 lib_cache[i].path = strdup(val);
260 lib_cache[i].flags = flags;
261 }
262 return 0;
263 }
264
load_ld_cache(const char * cache_path)265 static int load_ld_cache(const char *cache_path) {
266 struct stat st;
267 size_t ld_size;
268 const char *ld_map;
269 int ret, fd = open(cache_path, O_RDONLY);
270
271 if (fd < 0)
272 return -1;
273
274 if (fstat(fd, &st) < 0 || st.st_size < sizeof(struct ld_cache1)) {
275 close(fd);
276 return -1;
277 }
278
279 ld_size = st.st_size;
280 ld_map = (const char *)mmap(NULL, ld_size, PROT_READ, MAP_PRIVATE, fd, 0);
281 if (ld_map == MAP_FAILED) {
282 close(fd);
283 return -1;
284 }
285
286 if (memcmp(ld_map, CACHE1_HEADER, CACHE1_HEADER_LEN) == 0) {
287 const struct ld_cache1 *cache1 = (struct ld_cache1 *)ld_map;
288 size_t cache1_len = sizeof(struct ld_cache1) +
289 (cache1->entry_count * sizeof(struct ld_cache1_entry));
290 cache1_len = (cache1_len + 0x7) & ~0x7ULL;
291
292 if (ld_size > (cache1_len + sizeof(struct ld_cache2)))
293 ret = read_cache2(ld_map + cache1_len);
294 else
295 ret = read_cache1(ld_map);
296 } else {
297 ret = read_cache2(ld_map);
298 }
299
300 munmap((void *)ld_map, ld_size);
301 close(fd);
302 return ret;
303 }
304
305 #define LD_SO_CACHE "/etc/ld.so.cache"
306 #define FLAG_TYPE_MASK 0x00ff
307 #define TYPE_ELF_LIBC6 0x0003
308 #define FLAG_ABI_MASK 0xff00
309 #define ABI_SPARC_LIB64 0x0100
310 #define ABI_IA64_LIB64 0x0200
311 #define ABI_X8664_LIB64 0x0300
312 #define ABI_S390_LIB64 0x0400
313 #define ABI_POWERPC_LIB64 0x0500
314 #define ABI_AARCH64_LIB64 0x0a00
315
match_so_flags(int flags)316 static bool match_so_flags(int flags) {
317 if ((flags & FLAG_TYPE_MASK) != TYPE_ELF_LIBC6)
318 return false;
319
320 switch (flags & FLAG_ABI_MASK) {
321 case ABI_SPARC_LIB64:
322 case ABI_IA64_LIB64:
323 case ABI_X8664_LIB64:
324 case ABI_S390_LIB64:
325 case ABI_POWERPC_LIB64:
326 case ABI_AARCH64_LIB64:
327 return (sizeof(void *) == 8);
328 }
329
330 return sizeof(void *) == 4;
331 }
332
which_so_in_process(const char * libname,int pid,char * libpath)333 static bool which_so_in_process(const char* libname, int pid, char* libpath) {
334 int ret, found = false;
335 char endline[4096], *mapname = NULL, *newline;
336 char mappings_file[128];
337 const size_t search_len = strlen(libname) + strlen("/lib.");
338 char search1[search_len + 1];
339 char search2[search_len + 1];
340
341 snprintf(mappings_file, sizeof(mappings_file), "/proc/%ld/maps", (long)pid);
342 FILE *fp = fopen(mappings_file, "r");
343 if (!fp)
344 return NULL;
345
346 snprintf(search1, search_len + 1, "/lib%s.", libname);
347 snprintf(search2, search_len + 1, "/lib%s-", libname);
348
349 do {
350 ret = fscanf(fp, "%*x-%*x %*s %*x %*s %*d");
351 if (!fgets(endline, sizeof(endline), fp))
352 break;
353
354 mapname = endline;
355 newline = strchr(endline, '\n');
356 if (newline)
357 newline[0] = '\0';
358
359 while (isspace(mapname[0])) mapname++;
360
361 if (strstr(mapname, ".so") && (strstr(mapname, search1) ||
362 strstr(mapname, search2))) {
363 found = true;
364 memcpy(libpath, mapname, strlen(mapname) + 1);
365 break;
366 }
367 } while (ret != EOF);
368
369 fclose(fp);
370 return found;
371 }
372
bcc_procutils_which_so(const char * libname,int pid)373 char *bcc_procutils_which_so(const char *libname, int pid) {
374 const size_t soname_len = strlen(libname) + strlen("lib.so");
375 char soname[soname_len + 1];
376 char libpath[4096];
377 int i;
378
379 if (strchr(libname, '/'))
380 return strdup(libname);
381
382 if (pid && which_so_in_process(libname, pid, libpath))
383 return strdup(libpath);
384
385 if (lib_cache_count < 0)
386 return NULL;
387
388 if (!lib_cache_count && load_ld_cache(LD_SO_CACHE) < 0) {
389 lib_cache_count = -1;
390 return NULL;
391 }
392
393 snprintf(soname, soname_len + 1, "lib%s.so", libname);
394
395 for (i = 0; i < lib_cache_count; ++i) {
396 if (!strncmp(lib_cache[i].libname, soname, soname_len) &&
397 match_so_flags(lib_cache[i].flags)) {
398 return strdup(lib_cache[i].path);
399 }
400 }
401 return NULL;
402 }
403
bcc_procutils_free(const char * ptr)404 void bcc_procutils_free(const char *ptr) {
405 free((void *)ptr);
406 }
407
408 /* Detects the following languages + C. */
409 const char *languages[] = {"java", "node", "perl", "php", "python", "ruby"};
410 const char *language_c = "c";
411 const int nb_languages = 6;
412
bcc_procutils_language(int pid)413 const char *bcc_procutils_language(int pid) {
414 char procfilename[24], line[4096], pathname[32], *str;
415 FILE *procfile;
416 int i, ret;
417
418 /* Look for clues in the absolute path to the executable. */
419 snprintf(procfilename, sizeof(procfilename), "/proc/%ld/exe", (long)pid);
420 if (realpath(procfilename, line)) {
421 for (i = 0; i < nb_languages; i++)
422 if (strstr(line, languages[i]))
423 return languages[i];
424 }
425
426
427 snprintf(procfilename, sizeof(procfilename), "/proc/%ld/maps", (long)pid);
428 procfile = fopen(procfilename, "r");
429 if (!procfile)
430 return NULL;
431
432 /* Look for clues in memory mappings. */
433 bool libc = false;
434 do {
435 char perm[8], dev[8];
436 long long begin, end, size, inode;
437 ret = fscanf(procfile, "%llx-%llx %s %llx %s %lld", &begin, &end, perm,
438 &size, dev, &inode);
439 if (!fgets(line, sizeof(line), procfile))
440 break;
441 if (ret == 6) {
442 char *mapname = line;
443 char *newline = strchr(line, '\n');
444 if (newline)
445 newline[0] = '\0';
446 while (isspace(mapname[0])) mapname++;
447 for (i = 0; i < nb_languages; i++) {
448 snprintf(pathname, sizeof(pathname), "/lib%s", languages[i]);
449 if (strstr(mapname, pathname)) {
450 fclose(procfile);
451 return languages[i];
452 }
453 if ((str = strstr(mapname, "libc")) &&
454 (str[4] == '-' || str[4] == '.'))
455 libc = true;
456 }
457 }
458 } while (ret && ret != EOF);
459
460 fclose(procfile);
461
462 /* Return C as the language if libc was found and nothing else. */
463 return libc ? language_c : NULL;
464 }
465