2014-08-04 Arnaud Charlet <charlet@adacore.com>
[official-gcc.git] / libsanitizer / sanitizer_common / sanitizer_procmaps_linux.cc
blob20a074a799f71237871a8c952668e3265b943fcb
1 //===-- sanitizer_procmaps_linux.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 (Linux-specific parts).
9 //===----------------------------------------------------------------------===//
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"
17 #if SANITIZER_FREEBSD
18 #include <unistd.h>
19 #include <sys/sysctl.h>
20 #include <sys/user.h>
21 #endif
23 namespace __sanitizer {
25 // Linker initialized.
26 ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
27 StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
29 static void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
30 #if SANITIZER_FREEBSD
31 const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
32 size_t Size = 0;
33 int Err = sysctl(Mib, 4, NULL, &Size, NULL, 0);
34 CHECK_EQ(Err, 0);
35 CHECK_GT(Size, 0);
37 size_t MmapedSize = Size * 4 / 3;
38 void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
39 Size = MmapedSize;
40 Err = sysctl(Mib, 4, VmMap, &Size, NULL, 0);
41 CHECK_EQ(Err, 0);
43 proc_maps->data = (char*)VmMap;
44 proc_maps->mmaped_size = MmapedSize;
45 proc_maps->len = Size;
46 #else
47 proc_maps->len = ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
48 &proc_maps->mmaped_size, 1 << 26);
49 #endif
52 MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
53 ReadProcMaps(&proc_self_maps_);
54 if (cache_enabled) {
55 if (proc_self_maps_.mmaped_size == 0) {
56 LoadFromCache();
57 CHECK_GT(proc_self_maps_.len, 0);
59 } else {
60 CHECK_GT(proc_self_maps_.mmaped_size, 0);
62 Reset();
63 // FIXME: in the future we may want to cache the mappings on demand only.
64 if (cache_enabled)
65 CacheMemoryMappings();
68 MemoryMappingLayout::~MemoryMappingLayout() {
69 // Only unmap the buffer if it is different from the cached one. Otherwise
70 // it will be unmapped when the cache is refreshed.
71 if (proc_self_maps_.data != cached_proc_self_maps_.data) {
72 UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
76 void MemoryMappingLayout::Reset() {
77 current_ = proc_self_maps_.data;
80 // static
81 void MemoryMappingLayout::CacheMemoryMappings() {
82 SpinMutexLock l(&cache_lock_);
83 // Don't invalidate the cache if the mappings are unavailable.
84 ProcSelfMapsBuff old_proc_self_maps;
85 old_proc_self_maps = cached_proc_self_maps_;
86 ReadProcMaps(&cached_proc_self_maps_);
87 if (cached_proc_self_maps_.mmaped_size == 0) {
88 cached_proc_self_maps_ = old_proc_self_maps;
89 } else {
90 if (old_proc_self_maps.mmaped_size) {
91 UnmapOrDie(old_proc_self_maps.data,
92 old_proc_self_maps.mmaped_size);
97 void MemoryMappingLayout::LoadFromCache() {
98 SpinMutexLock l(&cache_lock_);
99 if (cached_proc_self_maps_.data) {
100 proc_self_maps_ = cached_proc_self_maps_;
104 #if !SANITIZER_FREEBSD
105 // Parse a hex value in str and update str.
106 static uptr ParseHex(char **str) {
107 uptr x = 0;
108 char *s;
109 for (s = *str; ; s++) {
110 char c = *s;
111 uptr v = 0;
112 if (c >= '0' && c <= '9')
113 v = c - '0';
114 else if (c >= 'a' && c <= 'f')
115 v = c - 'a' + 10;
116 else if (c >= 'A' && c <= 'F')
117 v = c - 'A' + 10;
118 else
119 break;
120 x = x * 16 + v;
122 *str = s;
123 return x;
126 static bool IsOneOf(char c, char c1, char c2) {
127 return c == c1 || c == c2;
129 #endif
131 static bool IsDecimal(char c) {
132 return c >= '0' && c <= '9';
135 static bool IsHex(char c) {
136 return (c >= '0' && c <= '9')
137 || (c >= 'a' && c <= 'f');
140 static uptr ReadHex(const char *p) {
141 uptr v = 0;
142 for (; IsHex(p[0]); p++) {
143 if (p[0] >= '0' && p[0] <= '9')
144 v = v * 16 + p[0] - '0';
145 else
146 v = v * 16 + p[0] - 'a' + 10;
148 return v;
151 static uptr ReadDecimal(const char *p) {
152 uptr v = 0;
153 for (; IsDecimal(p[0]); p++)
154 v = v * 10 + p[0] - '0';
155 return v;
158 bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
159 char filename[], uptr filename_size,
160 uptr *protection) {
161 char *last = proc_self_maps_.data + proc_self_maps_.len;
162 if (current_ >= last) return false;
163 uptr dummy;
164 if (!start) start = &dummy;
165 if (!end) end = &dummy;
166 if (!offset) offset = &dummy;
167 if (!protection) protection = &dummy;
168 #if SANITIZER_FREEBSD
169 struct kinfo_vmentry *VmEntry = (struct kinfo_vmentry*)current_;
171 *start = (uptr)VmEntry->kve_start;
172 *end = (uptr)VmEntry->kve_end;
173 *offset = (uptr)VmEntry->kve_offset;
175 *protection = 0;
176 if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
177 *protection |= kProtectionRead;
178 if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
179 *protection |= kProtectionWrite;
180 if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
181 *protection |= kProtectionExecute;
183 if (filename != NULL && filename_size > 0) {
184 internal_snprintf(filename,
185 Min(filename_size, (uptr)PATH_MAX),
186 "%s", VmEntry->kve_path);
189 current_ += VmEntry->kve_structsize;
190 #else // !SANITIZER_FREEBSD
191 char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
192 if (next_line == 0)
193 next_line = last;
194 // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar
195 *start = ParseHex(&current_);
196 CHECK_EQ(*current_++, '-');
197 *end = ParseHex(&current_);
198 CHECK_EQ(*current_++, ' ');
199 CHECK(IsOneOf(*current_, '-', 'r'));
200 *protection = 0;
201 if (*current_++ == 'r')
202 *protection |= kProtectionRead;
203 CHECK(IsOneOf(*current_, '-', 'w'));
204 if (*current_++ == 'w')
205 *protection |= kProtectionWrite;
206 CHECK(IsOneOf(*current_, '-', 'x'));
207 if (*current_++ == 'x')
208 *protection |= kProtectionExecute;
209 CHECK(IsOneOf(*current_, 's', 'p'));
210 if (*current_++ == 's')
211 *protection |= kProtectionShared;
212 CHECK_EQ(*current_++, ' ');
213 *offset = ParseHex(&current_);
214 CHECK_EQ(*current_++, ' ');
215 ParseHex(&current_);
216 CHECK_EQ(*current_++, ':');
217 ParseHex(&current_);
218 CHECK_EQ(*current_++, ' ');
219 while (IsDecimal(*current_))
220 current_++;
221 // Qemu may lack the trailing space.
222 // http://code.google.com/p/address-sanitizer/issues/detail?id=160
223 // CHECK_EQ(*current_++, ' ');
224 // Skip spaces.
225 while (current_ < next_line && *current_ == ' ')
226 current_++;
227 // Fill in the filename.
228 uptr i = 0;
229 while (current_ < next_line) {
230 if (filename && i < filename_size - 1)
231 filename[i++] = *current_;
232 current_++;
234 if (filename && i < filename_size)
235 filename[i] = 0;
236 current_ = next_line + 1;
237 #endif // !SANITIZER_FREEBSD
238 return true;
241 uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
242 uptr max_modules,
243 string_predicate_t filter) {
244 Reset();
245 uptr cur_beg, cur_end, cur_offset;
246 InternalScopedBuffer<char> module_name(kMaxPathLength);
247 uptr n_modules = 0;
248 for (uptr i = 0; n_modules < max_modules &&
249 Next(&cur_beg, &cur_end, &cur_offset, module_name.data(),
250 module_name.size(), 0);
251 i++) {
252 const char *cur_name = module_name.data();
253 if (cur_name[0] == '\0')
254 continue;
255 if (filter && !filter(cur_name))
256 continue;
257 void *mem = &modules[n_modules];
258 // Don't subtract 'cur_beg' from the first entry:
259 // * If a binary is compiled w/o -pie, then the first entry in
260 // process maps is likely the binary itself (all dynamic libs
261 // are mapped higher in address space). For such a binary,
262 // instruction offset in binary coincides with the actual
263 // instruction address in virtual memory (as code section
264 // is mapped to a fixed memory range).
265 // * If a binary is compiled with -pie, all the modules are
266 // mapped high at address space (in particular, higher than
267 // shadow memory of the tool), so the module can't be the
268 // first entry.
269 uptr base_address = (i ? cur_beg : 0) - cur_offset;
270 LoadedModule *cur_module = new(mem) LoadedModule(cur_name, base_address);
271 cur_module->addAddressRange(cur_beg, cur_end);
272 n_modules++;
274 return n_modules;
277 void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
278 char *smaps = 0;
279 uptr smaps_cap = 0;
280 uptr smaps_len = ReadFileToBuffer("/proc/self/smaps",
281 &smaps, &smaps_cap, 64<<20);
282 uptr start = 0;
283 bool file = false;
284 const char *pos = smaps;
285 while (pos < smaps + smaps_len) {
286 if (IsHex(pos[0])) {
287 start = ReadHex(pos);
288 for (; *pos != '/' && *pos > '\n'; pos++) {}
289 file = *pos == '/';
290 } else if (internal_strncmp(pos, "Rss:", 4) == 0) {
291 for (; *pos < '0' || *pos > '9'; pos++) {}
292 uptr rss = ReadDecimal(pos) * 1024;
293 cb(start, rss, file, stats, stats_size);
295 while (*pos++ != '\n') {}
297 UnmapOrDie(smaps, smaps_cap);
300 } // namespace __sanitizer
302 #endif // SANITIZER_FREEBSD || SANITIZER_LINUX