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