1 //===-- sanitizer_procmaps_linux.cc ---------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
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"
19 #include <sys/sysctl.h>
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
) {
31 const int Mib
[4] = { CTL_KERN
, KERN_PROC
, KERN_PROC_VMMAP
, getpid() };
33 int Err
= sysctl(Mib
, 4, NULL
, &Size
, NULL
, 0);
37 size_t MmapedSize
= Size
* 4 / 3;
38 void *VmMap
= MmapOrDie(MmapedSize
, "ReadProcMaps()");
40 Err
= sysctl(Mib
, 4, VmMap
, &Size
, NULL
, 0);
43 proc_maps
->data
= (char*)VmMap
;
44 proc_maps
->mmaped_size
= MmapedSize
;
45 proc_maps
->len
= Size
;
47 proc_maps
->len
= ReadFileToBuffer("/proc/self/maps", &proc_maps
->data
,
48 &proc_maps
->mmaped_size
, 1 << 26);
52 MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled
) {
53 ReadProcMaps(&proc_self_maps_
);
55 if (proc_self_maps_
.mmaped_size
== 0) {
57 CHECK_GT(proc_self_maps_
.len
, 0);
60 CHECK_GT(proc_self_maps_
.mmaped_size
, 0);
63 // FIXME: in the future we may want to cache the mappings on demand only.
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
;
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
;
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
) {
109 for (s
= *str
; ; s
++) {
112 if (c
>= '0' && c
<= '9')
114 else if (c
>= 'a' && c
<= 'f')
116 else if (c
>= 'A' && c
<= 'F')
126 static bool IsOneOf(char c
, char c1
, char c2
) {
127 return c
== c1
|| c
== c2
;
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
) {
142 for (; IsHex(p
[0]); p
++) {
143 if (p
[0] >= '0' && p
[0] <= '9')
144 v
= v
* 16 + p
[0] - '0';
146 v
= v
* 16 + p
[0] - 'a' + 10;
151 static uptr
ReadDecimal(const char *p
) {
153 for (; IsDecimal(p
[0]); p
++)
154 v
= v
* 10 + p
[0] - '0';
158 bool MemoryMappingLayout::Next(uptr
*start
, uptr
*end
, uptr
*offset
,
159 char filename
[], uptr filename_size
,
161 char *last
= proc_self_maps_
.data
+ proc_self_maps_
.len
;
162 if (current_
>= last
) return false;
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
;
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_
);
194 // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar
195 *start
= ParseHex(¤t_
);
196 CHECK_EQ(*current_
++, '-');
197 *end
= ParseHex(¤t_
);
198 CHECK_EQ(*current_
++, ' ');
199 CHECK(IsOneOf(*current_
, '-', 'r'));
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(¤t_
);
214 CHECK_EQ(*current_
++, ' ');
216 CHECK_EQ(*current_
++, ':');
218 CHECK_EQ(*current_
++, ' ');
219 while (IsDecimal(*current_
))
221 // Qemu may lack the trailing space.
222 // http://code.google.com/p/address-sanitizer/issues/detail?id=160
223 // CHECK_EQ(*current_++, ' ');
225 while (current_
< next_line
&& *current_
== ' ')
227 // Fill in the filename.
229 while (current_
< next_line
) {
230 if (filename
&& i
< filename_size
- 1)
231 filename
[i
++] = *current_
;
234 if (filename
&& i
< filename_size
)
236 current_
= next_line
+ 1;
237 #endif // !SANITIZER_FREEBSD
241 uptr
MemoryMappingLayout::DumpListOfModules(LoadedModule
*modules
,
243 string_predicate_t filter
) {
245 uptr cur_beg
, cur_end
, cur_offset
;
246 InternalScopedBuffer
<char> module_name(kMaxPathLength
);
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);
252 const char *cur_name
= module_name
.data();
253 if (cur_name
[0] == '\0')
255 if (filter
&& !filter(cur_name
))
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
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
);
277 void GetMemoryProfile(fill_profile_f cb
, uptr
*stats
, uptr stats_size
) {
280 uptr smaps_len
= ReadFileToBuffer("/proc/self/smaps",
281 &smaps
, &smaps_cap
, 64<<20);
284 const char *pos
= smaps
;
285 while (pos
< smaps
+ smaps_len
) {
287 start
= ReadHex(pos
);
288 for (; *pos
!= '/' && *pos
> '\n'; 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