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