• 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 <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 %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 
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