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