Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 1 | //===-- sanitizer_procmaps_common.cc --------------------------------------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // Information about the process mappings (common parts). |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "sanitizer_platform.h" |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 14 | |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 15 | #if SANITIZER_FREEBSD || SANITIZER_LINUX |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 16 | |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 17 | #include "sanitizer_common.h" |
| 18 | #include "sanitizer_placement_new.h" |
| 19 | #include "sanitizer_procmaps.h" |
| 20 | |
| 21 | namespace __sanitizer { |
| 22 | |
| 23 | // Linker initialized. |
| 24 | ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_; |
| 25 | StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized. |
| 26 | |
| 27 | static int TranslateDigit(char c) { |
| 28 | if (c >= '0' && c <= '9') |
| 29 | return c - '0'; |
| 30 | if (c >= 'a' && c <= 'f') |
| 31 | return c - 'a' + 10; |
| 32 | if (c >= 'A' && c <= 'F') |
| 33 | return c - 'A' + 10; |
| 34 | return -1; |
| 35 | } |
| 36 | |
| 37 | // Parse a number and promote 'p' up to the first non-digit character. |
| 38 | static uptr ParseNumber(const char **p, int base) { |
| 39 | uptr n = 0; |
| 40 | int d; |
| 41 | CHECK(base >= 2 && base <= 16); |
| 42 | while ((d = TranslateDigit(**p)) >= 0 && d < base) { |
| 43 | n = n * base + d; |
| 44 | (*p)++; |
| 45 | } |
| 46 | return n; |
| 47 | } |
| 48 | |
| 49 | bool IsDecimal(char c) { |
| 50 | int d = TranslateDigit(c); |
| 51 | return d >= 0 && d < 10; |
| 52 | } |
| 53 | |
| 54 | uptr ParseDecimal(const char **p) { |
| 55 | return ParseNumber(p, 10); |
| 56 | } |
| 57 | |
| 58 | bool IsHex(char c) { |
| 59 | int d = TranslateDigit(c); |
| 60 | return d >= 0 && d < 16; |
| 61 | } |
| 62 | |
| 63 | uptr ParseHex(const char **p) { |
| 64 | return ParseNumber(p, 16); |
| 65 | } |
| 66 | |
| 67 | MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) { |
| 68 | ReadProcMaps(&proc_self_maps_); |
| 69 | if (cache_enabled) { |
| 70 | if (proc_self_maps_.mmaped_size == 0) { |
| 71 | LoadFromCache(); |
| 72 | CHECK_GT(proc_self_maps_.len, 0); |
| 73 | } |
| 74 | } else { |
| 75 | CHECK_GT(proc_self_maps_.mmaped_size, 0); |
| 76 | } |
| 77 | Reset(); |
| 78 | // FIXME: in the future we may want to cache the mappings on demand only. |
| 79 | if (cache_enabled) |
| 80 | CacheMemoryMappings(); |
| 81 | } |
| 82 | |
| 83 | MemoryMappingLayout::~MemoryMappingLayout() { |
| 84 | // Only unmap the buffer if it is different from the cached one. Otherwise |
| 85 | // it will be unmapped when the cache is refreshed. |
| 86 | if (proc_self_maps_.data != cached_proc_self_maps_.data) { |
| 87 | UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size); |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | void MemoryMappingLayout::Reset() { |
| 92 | current_ = proc_self_maps_.data; |
| 93 | } |
| 94 | |
| 95 | // static |
| 96 | void MemoryMappingLayout::CacheMemoryMappings() { |
| 97 | SpinMutexLock l(&cache_lock_); |
| 98 | // Don't invalidate the cache if the mappings are unavailable. |
| 99 | ProcSelfMapsBuff old_proc_self_maps; |
| 100 | old_proc_self_maps = cached_proc_self_maps_; |
| 101 | ReadProcMaps(&cached_proc_self_maps_); |
| 102 | if (cached_proc_self_maps_.mmaped_size == 0) { |
| 103 | cached_proc_self_maps_ = old_proc_self_maps; |
| 104 | } else { |
| 105 | if (old_proc_self_maps.mmaped_size) { |
| 106 | UnmapOrDie(old_proc_self_maps.data, |
| 107 | old_proc_self_maps.mmaped_size); |
| 108 | } |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | void MemoryMappingLayout::LoadFromCache() { |
| 113 | SpinMutexLock l(&cache_lock_); |
| 114 | if (cached_proc_self_maps_.data) { |
| 115 | proc_self_maps_ = cached_proc_self_maps_; |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules, |
| 120 | uptr max_modules, |
| 121 | string_predicate_t filter) { |
| 122 | Reset(); |
| 123 | uptr cur_beg, cur_end, cur_offset, prot; |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 124 | InternalScopedString module_name(kMaxPathLength); |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 125 | uptr n_modules = 0; |
| 126 | for (uptr i = 0; n_modules < max_modules && |
| 127 | Next(&cur_beg, &cur_end, &cur_offset, module_name.data(), |
| 128 | module_name.size(), &prot); |
| 129 | i++) { |
| 130 | const char *cur_name = module_name.data(); |
| 131 | if (cur_name[0] == '\0') |
| 132 | continue; |
| 133 | if (filter && !filter(cur_name)) |
| 134 | continue; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 135 | // Don't subtract 'cur_beg' from the first entry: |
| 136 | // * If a binary is compiled w/o -pie, then the first entry in |
| 137 | // process maps is likely the binary itself (all dynamic libs |
| 138 | // are mapped higher in address space). For such a binary, |
| 139 | // instruction offset in binary coincides with the actual |
| 140 | // instruction address in virtual memory (as code section |
| 141 | // is mapped to a fixed memory range). |
| 142 | // * If a binary is compiled with -pie, all the modules are |
| 143 | // mapped high at address space (in particular, higher than |
| 144 | // shadow memory of the tool), so the module can't be the |
| 145 | // first entry. |
| 146 | uptr base_address = (i ? cur_beg : 0) - cur_offset; |
Pirama Arumuga Nainar | 259f706 | 2015-05-06 11:49:53 -0700 | [diff] [blame] | 147 | LoadedModule *cur_module = &modules[n_modules]; |
| 148 | cur_module->set(cur_name, base_address); |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 149 | cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute); |
| 150 | n_modules++; |
| 151 | } |
| 152 | return n_modules; |
| 153 | } |
| 154 | |
| 155 | void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 156 | char *smaps = nullptr; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 157 | uptr smaps_cap = 0; |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 158 | uptr smaps_len = 0; |
| 159 | if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len)) |
| 160 | return; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 161 | uptr start = 0; |
| 162 | bool file = false; |
| 163 | const char *pos = smaps; |
| 164 | while (pos < smaps + smaps_len) { |
| 165 | if (IsHex(pos[0])) { |
| 166 | start = ParseHex(&pos); |
| 167 | for (; *pos != '/' && *pos > '\n'; pos++) {} |
| 168 | file = *pos == '/'; |
| 169 | } else if (internal_strncmp(pos, "Rss:", 4) == 0) { |
| 170 | while (!IsDecimal(*pos)) pos++; |
| 171 | uptr rss = ParseDecimal(&pos) * 1024; |
| 172 | cb(start, rss, file, stats, stats_size); |
| 173 | } |
| 174 | while (*pos++ != '\n') {} |
| 175 | } |
| 176 | UnmapOrDie(smaps, smaps_cap); |
| 177 | } |
| 178 | |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 179 | } // namespace __sanitizer |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 180 | |
Pirama Arumuga Nainar | 799172d | 2016-03-03 15:50:30 -0800 | [diff] [blame^] | 181 | #endif // SANITIZER_FREEBSD || SANITIZER_LINUX |