• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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