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