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