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