| OLD | NEW |
| 1 // Copyright (c) 2006, Google Inc. | 1 // Copyright (c) 2006, Google Inc. |
| 2 // All rights reserved. | 2 // All rights reserved. |
| 3 // | 3 // |
| 4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
| 5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
| 6 // met: | 6 // met: |
| 7 // | 7 // |
| 8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
| 9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
| 10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 # define saferead(fd, buffer, size) read(fd, buffer, size) | 93 # define saferead(fd, buffer, size) read(fd, buffer, size) |
| 94 # define safeclose(fd) close(fd) | 94 # define safeclose(fd) close(fd) |
| 95 #endif | 95 #endif |
| 96 | 96 |
| 97 // ---------------------------------------------------------------------- | 97 // ---------------------------------------------------------------------- |
| 98 // GetenvBeforeMain() | 98 // GetenvBeforeMain() |
| 99 // GetUniquePathFromEnv() | 99 // GetUniquePathFromEnv() |
| 100 // Some non-trivial getenv-related functions. | 100 // Some non-trivial getenv-related functions. |
| 101 // ---------------------------------------------------------------------- | 101 // ---------------------------------------------------------------------- |
| 102 | 102 |
| 103 // It's not safe to call getenv() in the malloc hooks, because they |
| 104 // might be called extremely early, before libc is done setting up |
| 105 // correctly. In particular, the thread library may not be done |
| 106 // setting up errno. So instead, we use the built-in __environ array |
| 107 // if it exists, and otherwise read /proc/self/environ directly, using |
| 108 // system calls to read the file, and thus avoid setting errno. |
| 109 // /proc/self/environ has a limit of how much data it exports (around |
| 110 // 8K), so it's not an ideal solution. |
| 103 const char* GetenvBeforeMain(const char* name) { | 111 const char* GetenvBeforeMain(const char* name) { |
| 112 #if defined(HAVE___ENVIRON) // if we have it, it's declared in unistd.h |
| 113 const int namelen = strlen(name); |
| 114 for (char** p = __environ; *p; p++) { |
| 115 if (!memcmp(*p, name, namelen) && (*p)[namelen] == '=') // it's a match |
| 116 return *p + namelen+1; // point after = |
| 117 } |
| 118 return NULL; |
| 119 #elif defined(PLATFORM_WINDOWS) |
| 120 // TODO(mbelshe) - repeated calls to this function will overwrite the |
| 121 // contents of the static buffer. |
| 122 static char envbuf[1024]; // enough to hold any envvar we care about |
| 123 if (!GetEnvironmentVariableA(name, envbuf, sizeof(envbuf)-1)) |
| 124 return NULL; |
| 125 return envbuf; |
| 126 #else |
| 104 // static is ok because this function should only be called before | 127 // static is ok because this function should only be called before |
| 105 // main(), when we're single-threaded. | 128 // main(), when we're single-threaded. |
| 106 static char envbuf[16<<10]; | 129 static char envbuf[16<<10]; |
| 107 #ifndef PLATFORM_WINDOWS | |
| 108 if (*envbuf == '\0') { // haven't read the environ yet | 130 if (*envbuf == '\0') { // haven't read the environ yet |
| 109 int fd = safeopen("/proc/self/environ", O_RDONLY); | 131 int fd = safeopen("/proc/self/environ", O_RDONLY); |
| 110 // The -2 below guarantees the last two bytes of the buffer will be \0\0 | 132 // The -2 below guarantees the last two bytes of the buffer will be \0\0 |
| 111 if (fd == -1 || // unable to open the file, fall back onto libc | 133 if (fd == -1 || // unable to open the file, fall back onto libc |
| 112 saferead(fd, envbuf, sizeof(envbuf) - 2) < 0) { // error reading file | 134 saferead(fd, envbuf, sizeof(envbuf) - 2) < 0) { // error reading file |
| 113 RAW_VLOG(1, "Unable to open /proc/self/environ, falling back " | 135 RAW_VLOG(1, "Unable to open /proc/self/environ, falling back " |
| 114 "on getenv(\"%s\"), which may not work", name); | 136 "on getenv(\"%s\"), which may not work", name); |
| 115 if (fd != -1) safeclose(fd); | 137 if (fd != -1) safeclose(fd); |
| 116 return getenv(name); | 138 return getenv(name); |
| 117 } | 139 } |
| 118 safeclose(fd); | 140 safeclose(fd); |
| 119 } | 141 } |
| 120 const int namelen = strlen(name); | 142 const int namelen = strlen(name); |
| 121 const char* p = envbuf; | 143 const char* p = envbuf; |
| 122 while (*p != '\0') { // will happen at the \0\0 that terminates the buffer | 144 while (*p != '\0') { // will happen at the \0\0 that terminates the buffer |
| 123 // proc file has the format NAME=value\0NAME=value\0NAME=value\0... | 145 // proc file has the format NAME=value\0NAME=value\0NAME=value\0... |
| 124 const char* endp = (char*)memchr(p, '\0', sizeof(envbuf) - (p - envbuf)); | 146 const char* endp = (char*)memchr(p, '\0', sizeof(envbuf) - (p - envbuf)); |
| 125 if (endp == NULL) // this entry isn't NUL terminated | 147 if (endp == NULL) // this entry isn't NUL terminated |
| 126 return NULL; | 148 return NULL; |
| 127 else if (!memcmp(p, name, namelen) && p[namelen] == '=') // it's a match | 149 else if (!memcmp(p, name, namelen) && p[namelen] == '=') // it's a match |
| 128 return p + namelen+1; // point after = | 150 return p + namelen+1; // point after = |
| 129 p = endp + 1; | 151 p = endp + 1; |
| 130 } | 152 } |
| 131 return NULL; // env var never found | 153 return NULL; // env var never found |
| 132 #else | |
| 133 // TODO(mbelshe) - repeated calls to this function will overwrite the | |
| 134 // contents of the static buffer. | |
| 135 if (!GetEnvironmentVariableA(name, envbuf, sizeof(envbuf)-1)) | |
| 136 return NULL; | |
| 137 return envbuf; | |
| 138 #endif | 154 #endif |
| 139 } | 155 } |
| 140 | 156 |
| 141 // This takes as an argument an environment-variable name (like | 157 // This takes as an argument an environment-variable name (like |
| 142 // CPUPROFILE) whose value is supposed to be a file-path, and sets | 158 // CPUPROFILE) whose value is supposed to be a file-path, and sets |
| 143 // path to that path, and returns true. If the env var doesn't exist, | 159 // path to that path, and returns true. If the env var doesn't exist, |
| 144 // or is the empty string, leave path unchanged and returns false. | 160 // or is the empty string, leave path unchanged and returns false. |
| 145 // The reason this is non-trivial is that this function handles munged | 161 // The reason this is non-trivial is that this function handles munged |
| 146 // pathnames. Here's why: | 162 // pathnames. Here's why: |
| 147 // | 163 // |
| (...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 434 | 450 |
| 435 #if defined __linux__ || defined __FreeBSD__ || defined __sun__ || defined __CYG
WIN__ || defined __CYGWIN32__ | 451 #if defined __linux__ || defined __FreeBSD__ || defined __sun__ || defined __CYG
WIN__ || defined __CYGWIN32__ |
| 436 static void ConstructFilename(const char* spec, pid_t pid, | 452 static void ConstructFilename(const char* spec, pid_t pid, |
| 437 char* buf, int buf_size) { | 453 char* buf, int buf_size) { |
| 438 CHECK_LT(snprintf(buf, buf_size, | 454 CHECK_LT(snprintf(buf, buf_size, |
| 439 spec, | 455 spec, |
| 440 pid ? pid : getpid()), buf_size); | 456 pid ? pid : getpid()), buf_size); |
| 441 } | 457 } |
| 442 #endif | 458 #endif |
| 443 | 459 |
| 460 // A templatized helper function instantiated for Mach (OS X) only. |
| 461 // It can handle finding info for both 32 bits and 64 bits. |
| 462 // Returns true if it successfully handled the hdr, false else. |
| 463 #ifdef __MACH__ // Mac OS X, almost certainly |
| 464 template<uint32_t kMagic, uint32_t kLCSegment, |
| 465 typename MachHeader, typename SegmentCommand> |
| 466 static bool NextExtMachHelper(const mach_header* hdr, |
| 467 int current_image, int current_load_cmd, |
| 468 uint64 *start, uint64 *end, char **flags, |
| 469 uint64 *offset, int64 *inode, char **filename, |
| 470 uint64 *file_mapping, uint64 *file_pages, |
| 471 uint64 *anon_mapping, uint64 *anon_pages, |
| 472 dev_t *dev) { |
| 473 static char kDefaultPerms[5] = "r-xp"; |
| 474 if (hdr->magic != kMagic) |
| 475 return false; |
| 476 const char* lc = (const char *)hdr + sizeof(MachHeader); |
| 477 // TODO(csilvers): make this not-quadradic (increment and hold state) |
| 478 for (int j = 0; j < current_load_cmd; j++) // advance to *our* load_cmd |
| 479 lc += ((const load_command *)lc)->cmdsize; |
| 480 if (((const load_command *)lc)->cmd == kLCSegment) { |
| 481 const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image); |
| 482 const SegmentCommand* sc = (const SegmentCommand *)lc; |
| 483 if (start) *start = sc->vmaddr + dlloff; |
| 484 if (end) *end = sc->vmaddr + sc->vmsize + dlloff; |
| 485 if (flags) *flags = kDefaultPerms; // can we do better? |
| 486 if (offset) *offset = sc->fileoff; |
| 487 if (inode) *inode = 0; |
| 488 if (filename) |
| 489 *filename = const_cast<char*>(_dyld_get_image_name(current_image)); |
| 490 if (file_mapping) *file_mapping = 0; |
| 491 if (file_pages) *file_pages = 0; // could we use sc->filesize? |
| 492 if (anon_mapping) *anon_mapping = 0; |
| 493 if (anon_pages) *anon_pages = 0; |
| 494 if (dev) *dev = 0; |
| 495 return true; |
| 496 } |
| 497 |
| 498 return false; |
| 499 } |
| 500 #endif |
| 501 |
| 444 ProcMapsIterator::ProcMapsIterator(pid_t pid) { | 502 ProcMapsIterator::ProcMapsIterator(pid_t pid) { |
| 445 Init(pid, NULL, false); | 503 Init(pid, NULL, false); |
| 446 } | 504 } |
| 447 | 505 |
| 448 ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer) { | 506 ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer) { |
| 449 Init(pid, buffer, false); | 507 Init(pid, buffer, false); |
| 450 } | 508 } |
| 451 | 509 |
| 452 ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer, | 510 ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer, |
| 453 bool use_maps_backing) { | 511 bool use_maps_backing) { |
| 454 Init(pid, buffer, use_maps_backing); | 512 Init(pid, buffer, use_maps_backing); |
| 455 } | 513 } |
| 456 | 514 |
| 457 void ProcMapsIterator::Init(pid_t pid, Buffer *buffer, | 515 void ProcMapsIterator::Init(pid_t pid, Buffer *buffer, |
| 458 bool use_maps_backing) { | 516 bool use_maps_backing) { |
| 517 pid_ = pid; |
| 459 using_maps_backing_ = use_maps_backing; | 518 using_maps_backing_ = use_maps_backing; |
| 460 dynamic_buffer_ = NULL; | 519 dynamic_buffer_ = NULL; |
| 461 if (!buffer) { | 520 if (!buffer) { |
| 462 // If the user didn't pass in any buffer storage, allocate it | 521 // If the user didn't pass in any buffer storage, allocate it |
| 463 // now. This is the normal case; the signal handler passes in a | 522 // now. This is the normal case; the signal handler passes in a |
| 464 // static buffer. | 523 // static buffer. |
| 465 buffer = dynamic_buffer_ = new Buffer; | 524 buffer = dynamic_buffer_ = new Buffer; |
| 466 } else { | 525 } else { |
| 467 dynamic_buffer_ = NULL; | 526 dynamic_buffer_ = NULL; |
| 468 } | 527 } |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 684 | 743 |
| 685 return true; | 744 return true; |
| 686 } while (etext_ > ibuf_); | 745 } while (etext_ > ibuf_); |
| 687 #elif defined(__sun__) | 746 #elif defined(__sun__) |
| 688 // This is based on MA_READ == 4, MA_WRITE == 2, MA_EXEC == 1 | 747 // This is based on MA_READ == 4, MA_WRITE == 2, MA_EXEC == 1 |
| 689 static char kPerms[8][4] = { "---", "--x", "-w-", "-wx", | 748 static char kPerms[8][4] = { "---", "--x", "-w-", "-wx", |
| 690 "r--", "r-x", "rw-", "rwx" }; | 749 "r--", "r-x", "rw-", "rwx" }; |
| 691 COMPILE_ASSERT(MA_READ == 4, solaris_ma_read_must_equal_4); | 750 COMPILE_ASSERT(MA_READ == 4, solaris_ma_read_must_equal_4); |
| 692 COMPILE_ASSERT(MA_WRITE == 2, solaris_ma_write_must_equal_2); | 751 COMPILE_ASSERT(MA_WRITE == 2, solaris_ma_write_must_equal_2); |
| 693 COMPILE_ASSERT(MA_EXEC == 1, solaris_ma_exec_must_equal_1); | 752 COMPILE_ASSERT(MA_EXEC == 1, solaris_ma_exec_must_equal_1); |
| 753 Buffer object_path; |
| 694 int nread = 0; // fill up buffer with text | 754 int nread = 0; // fill up buffer with text |
| 695 NO_INTR(nread = read(fd_, ibuf_, sizeof(prmap_t))); | 755 NO_INTR(nread = read(fd_, ibuf_, sizeof(prmap_t))); |
| 696 if (nread == sizeof(prmap_t)) { | 756 if (nread == sizeof(prmap_t)) { |
| 697 long inode_from_mapname = 0; | 757 long inode_from_mapname = 0; |
| 698 prmap_t* mapinfo = reinterpret_cast<prmap_t*>(ibuf_); | 758 prmap_t* mapinfo = reinterpret_cast<prmap_t*>(ibuf_); |
| 699 // Best-effort attempt to get the inode from the filename. I think the | 759 // Best-effort attempt to get the inode from the filename. I think the |
| 700 // two middle ints are major and minor device numbers, but I'm not sure. | 760 // two middle ints are major and minor device numbers, but I'm not sure. |
| 701 sscanf(mapinfo->pr_mapname, "ufs.%*d.%*d.%ld", &inode_from_mapname); | 761 sscanf(mapinfo->pr_mapname, "ufs.%*d.%*d.%ld", &inode_from_mapname); |
| 702 | 762 |
| 763 if (pid_ == 0) { |
| 764 CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize, |
| 765 "/proc/self/path/%s", mapinfo->pr_mapname), |
| 766 Buffer::kBufSize); |
| 767 } else { |
| 768 CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize, |
| 769 "/proc/%d/path/%s", pid_, mapinfo->pr_mapname), |
| 770 Buffer::kBufSize); |
| 771 } |
| 772 ssize_t len = readlink(object_path.buf_, current_filename_, PATH_MAX); |
| 773 CHECK_LT(len, PATH_MAX); |
| 774 if (len < 0) |
| 775 len = 0; |
| 776 current_filename_[len] = '\0'; |
| 777 |
| 703 if (start) *start = mapinfo->pr_vaddr; | 778 if (start) *start = mapinfo->pr_vaddr; |
| 704 if (end) *end = mapinfo->pr_vaddr + mapinfo->pr_size; | 779 if (end) *end = mapinfo->pr_vaddr + mapinfo->pr_size; |
| 705 if (flags) *flags = kPerms[mapinfo->pr_mflags & 7]; | 780 if (flags) *flags = kPerms[mapinfo->pr_mflags & 7]; |
| 706 if (offset) *offset = mapinfo->pr_offset; | 781 if (offset) *offset = mapinfo->pr_offset; |
| 707 if (inode) *inode = inode_from_mapname; | 782 if (inode) *inode = inode_from_mapname; |
| 708 // TODO(csilvers): How to map from /proc/map/object to filename? | 783 if (filename) *filename = current_filename_; |
| 709 if (filename) *filename = mapinfo->pr_mapname; // format is ufs.?.?.inode | |
| 710 if (file_mapping) *file_mapping = 0; | 784 if (file_mapping) *file_mapping = 0; |
| 711 if (file_pages) *file_pages = 0; | 785 if (file_pages) *file_pages = 0; |
| 712 if (anon_mapping) *anon_mapping = 0; | 786 if (anon_mapping) *anon_mapping = 0; |
| 713 if (anon_pages) *anon_pages = 0; | 787 if (anon_pages) *anon_pages = 0; |
| 714 if (dev) *dev = 0; | 788 if (dev) *dev = 0; |
| 715 return true; | 789 return true; |
| 716 } | 790 } |
| 717 #elif defined(__MACH__) | 791 #elif defined(__MACH__) |
| 718 static char kDefaultPerms[5] = "r-xp"; | |
| 719 // We return a separate entry for each segment in the DLL. (TODO(csilvers): | 792 // We return a separate entry for each segment in the DLL. (TODO(csilvers): |
| 720 // can we do better?) A DLL ("image") has load-commands, some of which | 793 // can we do better?) A DLL ("image") has load-commands, some of which |
| 721 // talk about segment boundaries. | 794 // talk about segment boundaries. |
| 722 // cf image_for_address from http://svn.digium.com/view/asterisk/team/oej/mini
voicemail/dlfcn.c?revision=53912 | 795 // cf image_for_address from http://svn.digium.com/view/asterisk/team/oej/mini
voicemail/dlfcn.c?revision=53912 |
| 723 for (; current_image_ >= 0; current_image_--) { | 796 for (; current_image_ >= 0; current_image_--) { |
| 724 const mach_header* hdr = _dyld_get_image_header(current_image_); | 797 const mach_header* hdr = _dyld_get_image_header(current_image_); |
| 725 if (!hdr) continue; | 798 if (!hdr) continue; |
| 726 if (current_load_cmd_ < 0) // set up for this image | 799 if (current_load_cmd_ < 0) // set up for this image |
| 727 current_load_cmd_ = hdr->ncmds; // again, go from the top down | 800 current_load_cmd_ = hdr->ncmds; // again, go from the top down |
| 728 | 801 |
| 729 // We start with the next load command (we've already looked at this one). | 802 // We start with the next load command (we've already looked at this one). |
| 730 for (current_load_cmd_--; current_load_cmd_ >= 0; current_load_cmd_--) { | 803 for (current_load_cmd_--; current_load_cmd_ >= 0; current_load_cmd_--) { |
| 731 const char* lc = ((const char *)hdr + sizeof(struct mach_header)); | 804 #ifdef MH_MAGIC_64 |
| 732 // TODO(csilvers): make this not-quadradic (increment and hold state) | 805 if (NextExtMachHelper<MH_MAGIC_64, LC_SEGMENT_64, |
| 733 for (int j = 0; j < current_load_cmd_; j++) // advance to *our* load_cmd | 806 struct mach_header_64, struct segment_command_64>( |
| 734 lc += ((const load_command *)lc)->cmdsize; | 807 hdr, current_image_, current_load_cmd_, |
| 735 if (((const load_command *)lc)->cmd == LC_SEGMENT) { | 808 start, end, flags, offset, inode, filename, |
| 736 const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image_); | 809 file_mapping, file_pages, anon_mapping, |
| 737 const segment_command* sc = (const segment_command *)lc; | 810 anon_pages, dev)) { |
| 738 if (start) *start = sc->vmaddr + dlloff; | 811 return true; |
| 739 if (end) *end = sc->vmaddr + sc->vmsize + dlloff; | 812 } |
| 740 if (flags) *flags = kDefaultPerms; // can we do better? | 813 #endif |
| 741 if (offset) *offset = sc->fileoff; | 814 if (NextExtMachHelper<MH_MAGIC, LC_SEGMENT, |
| 742 if (inode) *inode = 0; | 815 struct mach_header, struct segment_command>( |
| 743 if (filename) | 816 hdr, current_image_, current_load_cmd_, |
| 744 *filename = const_cast<char*>(_dyld_get_image_name(current_image_)); | 817 start, end, flags, offset, inode, filename, |
| 745 if (file_mapping) *file_mapping = 0; | 818 file_mapping, file_pages, anon_mapping, |
| 746 if (file_pages) *file_pages = 0; // could we use sc->filesize? | 819 anon_pages, dev)) { |
| 747 if (anon_mapping) *anon_mapping = 0; | |
| 748 if (anon_pages) *anon_pages = 0; | |
| 749 if (dev) *dev = 0; | |
| 750 return true; | 820 return true; |
| 751 } | 821 } |
| 752 } | 822 } |
| 753 // If we get here, no more load_cmd's in this image talk about | 823 // If we get here, no more load_cmd's in this image talk about |
| 754 // segments. Go on to the next image. | 824 // segments. Go on to the next image. |
| 755 } | 825 } |
| 756 #elif defined(PLATFORM_WINDOWS) | 826 #elif defined(PLATFORM_WINDOWS) |
| 757 static char kDefaultPerms[5] = "r-xp"; | 827 static char kDefaultPerms[5] = "r-xp"; |
| 758 BOOL ok; | 828 BOOL ok; |
| 759 if (module_.dwSize == 0) { // only possible before first call | 829 if (module_.dwSize == 0) { // only possible before first call |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 838 ProcMapsIterator::Buffer linebuf; | 908 ProcMapsIterator::Buffer linebuf; |
| 839 while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) { | 909 while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) { |
| 840 int written = it.FormatLine(linebuf.buf_, sizeof(linebuf.buf_), | 910 int written = it.FormatLine(linebuf.buf_, sizeof(linebuf.buf_), |
| 841 start, end, flags, offset, inode, filename, | 911 start, end, flags, offset, inode, filename, |
| 842 0); | 912 0); |
| 843 RawWrite(fd, linebuf.buf_, written); | 913 RawWrite(fd, linebuf.buf_, written); |
| 844 } | 914 } |
| 845 } | 915 } |
| 846 | 916 |
| 847 } // namespace tcmalloc | 917 } // namespace tcmalloc |
| OLD | NEW |