Home | History | Annotate | Download | only in cc
      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 
     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 
     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 
     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 %s %llx %s %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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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