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