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 13 #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ 14 SANITIZER_OPENBSD || SANITIZER_SOLARIS 15 16 #include "sanitizer_common.h" 17 #include "sanitizer_placement_new.h" 18 #include "sanitizer_procmaps.h" 19 20 namespace __sanitizer { 21 22 static ProcSelfMapsBuff cached_proc_self_maps; 23 static StaticSpinMutex cache_lock; 24 25 static 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. 36 static 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 47 bool IsDecimal(char c) { 48 int d = TranslateDigit(c); 49 return d >= 0 && d < 10; 50 } 51 52 uptr ParseDecimal(const char **p) { 53 return ParseNumber(p, 10); 54 } 55 56 bool IsHex(char c) { 57 int d = TranslateDigit(c); 58 return d >= 0 && d < 16; 59 } 60 61 uptr ParseHex(const char **p) { 62 return ParseNumber(p, 16); 63 } 64 65 void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) { 66 // data_ should be unused on this platform 67 CHECK(!data_); 68 module->addAddressRange(start, end, IsExecutable(), IsWritable()); 69 } 70 71 MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) { 72 // FIXME: in the future we may want to cache the mappings on demand only. 73 if (cache_enabled) 74 CacheMemoryMappings(); 75 76 // Read maps after the cache update to capture the maps/unmaps happening in 77 // the process of updating. 78 ReadProcMaps(&data_.proc_self_maps); 79 if (cache_enabled && data_.proc_self_maps.mmaped_size == 0) 80 LoadFromCache(); 81 CHECK_GT(data_.proc_self_maps.mmaped_size, 0); 82 CHECK_GT(data_.proc_self_maps.len, 0); 83 84 Reset(); 85 } 86 87 MemoryMappingLayout::~MemoryMappingLayout() { 88 // Only unmap the buffer if it is different from the cached one. Otherwise 89 // it will be unmapped when the cache is refreshed. 90 if (data_.proc_self_maps.data != cached_proc_self_maps.data) 91 UnmapOrDie(data_.proc_self_maps.data, data_.proc_self_maps.mmaped_size); 92 } 93 94 void MemoryMappingLayout::Reset() { 95 data_.current = data_.proc_self_maps.data; 96 } 97 98 // static 99 void MemoryMappingLayout::CacheMemoryMappings() { 100 ProcSelfMapsBuff new_proc_self_maps; 101 ReadProcMaps(&new_proc_self_maps); 102 // Don't invalidate the cache if the mappings are unavailable. 103 if (new_proc_self_maps.mmaped_size == 0) 104 return; 105 SpinMutexLock l(&cache_lock); 106 if (cached_proc_self_maps.mmaped_size) 107 UnmapOrDie(cached_proc_self_maps.data, cached_proc_self_maps.mmaped_size); 108 cached_proc_self_maps = new_proc_self_maps; 109 } 110 111 void MemoryMappingLayout::LoadFromCache() { 112 SpinMutexLock l(&cache_lock); 113 if (cached_proc_self_maps.data) 114 data_.proc_self_maps = cached_proc_self_maps; 115 } 116 117 void MemoryMappingLayout::DumpListOfModules( 118 InternalMmapVectorNoCtor<LoadedModule> *modules) { 119 Reset(); 120 InternalScopedString module_name(kMaxPathLength); 121 MemoryMappedSegment segment(module_name.data(), module_name.size()); 122 for (uptr i = 0; Next(&segment); i++) { 123 const char *cur_name = segment.filename; 124 if (cur_name[0] == '\0') 125 continue; 126 // Don't subtract 'cur_beg' from the first entry: 127 // * If a binary is compiled w/o -pie, then the first entry in 128 // process maps is likely the binary itself (all dynamic libs 129 // are mapped higher in address space). For such a binary, 130 // instruction offset in binary coincides with the actual 131 // instruction address in virtual memory (as code section 132 // is mapped to a fixed memory range). 133 // * If a binary is compiled with -pie, all the modules are 134 // mapped high at address space (in particular, higher than 135 // shadow memory of the tool), so the module can't be the 136 // first entry. 137 uptr base_address = (i ? segment.start : 0) - segment.offset; 138 LoadedModule cur_module; 139 cur_module.set(cur_name, base_address); 140 segment.AddAddressRanges(&cur_module); 141 modules->push_back(cur_module); 142 } 143 } 144 145 void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { 146 char *smaps = nullptr; 147 uptr smaps_cap = 0; 148 uptr smaps_len = 0; 149 if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len)) 150 return; 151 uptr start = 0; 152 bool file = false; 153 const char *pos = smaps; 154 while (pos < smaps + smaps_len) { 155 if (IsHex(pos[0])) { 156 start = ParseHex(&pos); 157 for (; *pos != '/' && *pos > '\n'; pos++) {} 158 file = *pos == '/'; 159 } else if (internal_strncmp(pos, "Rss:", 4) == 0) { 160 while (!IsDecimal(*pos)) pos++; 161 uptr rss = ParseDecimal(&pos) * 1024; 162 cb(start, rss, file, stats, stats_size); 163 } 164 while (*pos++ != '\n') {} 165 } 166 UnmapOrDie(smaps, smaps_cap); 167 } 168 169 } // namespace __sanitizer 170 171 #endif 172