]>
Commit | Line | Data |
---|---|---|
b667dd70 | 1 | //===-- sanitizer_procmaps_common.cpp -------------------------------------===// |
866e32ad | 2 | // |
b667dd70 ML |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. | |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
866e32ad KS |
6 | // |
7 | //===----------------------------------------------------------------------===// | |
8 | // | |
9 | // Information about the process mappings (common parts). | |
10 | //===----------------------------------------------------------------------===// | |
11 | ||
12 | #include "sanitizer_platform.h" | |
696d846a | 13 | |
eac97531 ML |
14 | #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ |
15 | SANITIZER_OPENBSD || SANITIZER_SOLARIS | |
696d846a | 16 | |
866e32ad KS |
17 | #include "sanitizer_common.h" |
18 | #include "sanitizer_placement_new.h" | |
19 | #include "sanitizer_procmaps.h" | |
20 | ||
21 | namespace __sanitizer { | |
22 | ||
5d3805fc JJ |
23 | static ProcSelfMapsBuff cached_proc_self_maps; |
24 | static StaticSpinMutex cache_lock; | |
866e32ad KS |
25 | |
26 | static int TranslateDigit(char c) { | |
27 | if (c >= '0' && c <= '9') | |
28 | return c - '0'; | |
29 | if (c >= 'a' && c <= 'f') | |
30 | return c - 'a' + 10; | |
31 | if (c >= 'A' && c <= 'F') | |
32 | return c - 'A' + 10; | |
33 | return -1; | |
34 | } | |
35 | ||
36 | // Parse a number and promote 'p' up to the first non-digit character. | |
37 | static uptr ParseNumber(const char **p, int base) { | |
38 | uptr n = 0; | |
39 | int d; | |
40 | CHECK(base >= 2 && base <= 16); | |
41 | while ((d = TranslateDigit(**p)) >= 0 && d < base) { | |
42 | n = n * base + d; | |
43 | (*p)++; | |
44 | } | |
45 | return n; | |
46 | } | |
47 | ||
48 | bool IsDecimal(char c) { | |
49 | int d = TranslateDigit(c); | |
50 | return d >= 0 && d < 10; | |
51 | } | |
52 | ||
53 | uptr ParseDecimal(const char **p) { | |
54 | return ParseNumber(p, 10); | |
55 | } | |
56 | ||
57 | bool IsHex(char c) { | |
58 | int d = TranslateDigit(c); | |
59 | return d >= 0 && d < 16; | |
60 | } | |
61 | ||
62 | uptr ParseHex(const char **p) { | |
63 | return ParseNumber(p, 16); | |
64 | } | |
65 | ||
5d3805fc JJ |
66 | void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) { |
67 | // data_ should be unused on this platform | |
68 | CHECK(!data_); | |
69 | module->addAddressRange(start, end, IsExecutable(), IsWritable()); | |
70 | } | |
71 | ||
866e32ad | 72 | MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) { |
866e32ad KS |
73 | // FIXME: in the future we may want to cache the mappings on demand only. |
74 | if (cache_enabled) | |
75 | CacheMemoryMappings(); | |
eac97531 ML |
76 | |
77 | // Read maps after the cache update to capture the maps/unmaps happening in | |
78 | // the process of updating. | |
79 | ReadProcMaps(&data_.proc_self_maps); | |
80 | if (cache_enabled && data_.proc_self_maps.mmaped_size == 0) | |
81 | LoadFromCache(); | |
eac97531 ML |
82 | |
83 | Reset(); | |
866e32ad KS |
84 | } |
85 | ||
b667dd70 ML |
86 | bool MemoryMappingLayout::Error() const { |
87 | return data_.current == nullptr; | |
88 | } | |
89 | ||
866e32ad KS |
90 | MemoryMappingLayout::~MemoryMappingLayout() { |
91 | // Only unmap the buffer if it is different from the cached one. Otherwise | |
92 | // it will be unmapped when the cache is refreshed. | |
eac97531 | 93 | if (data_.proc_self_maps.data != cached_proc_self_maps.data) |
5d3805fc | 94 | UnmapOrDie(data_.proc_self_maps.data, data_.proc_self_maps.mmaped_size); |
866e32ad KS |
95 | } |
96 | ||
eac97531 ML |
97 | void MemoryMappingLayout::Reset() { |
98 | data_.current = data_.proc_self_maps.data; | |
99 | } | |
866e32ad KS |
100 | |
101 | // static | |
102 | void MemoryMappingLayout::CacheMemoryMappings() { | |
eac97531 ML |
103 | ProcSelfMapsBuff new_proc_self_maps; |
104 | ReadProcMaps(&new_proc_self_maps); | |
866e32ad | 105 | // Don't invalidate the cache if the mappings are unavailable. |
eac97531 ML |
106 | if (new_proc_self_maps.mmaped_size == 0) |
107 | return; | |
108 | SpinMutexLock l(&cache_lock); | |
109 | if (cached_proc_self_maps.mmaped_size) | |
110 | UnmapOrDie(cached_proc_self_maps.data, cached_proc_self_maps.mmaped_size); | |
111 | cached_proc_self_maps = new_proc_self_maps; | |
866e32ad KS |
112 | } |
113 | ||
114 | void MemoryMappingLayout::LoadFromCache() { | |
5d3805fc | 115 | SpinMutexLock l(&cache_lock); |
eac97531 | 116 | if (cached_proc_self_maps.data) |
5d3805fc | 117 | data_.proc_self_maps = cached_proc_self_maps; |
866e32ad KS |
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 | |
eac97531 | 174 | #endif |