blob: 3c69b4e91b59344f4e159ca67b455a2a2d923af9 [file] [log] [blame]
Pirama Arumuga Nainarc58a4362016-09-19 23:00:23 -07001//===-- esan.cpp ----------------------------------------------------------===//
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// This file is a part of EfficiencySanitizer, a family of performance tuners.
11//
12// Main file (entry points) for the Esan run-time.
13//===----------------------------------------------------------------------===//
14
15#include "esan.h"
16#include "esan_flags.h"
17#include "esan_interface_internal.h"
18#include "esan_shadow.h"
19#include "cache_frag.h"
20#include "sanitizer_common/sanitizer_common.h"
21#include "sanitizer_common/sanitizer_flag_parser.h"
22#include "sanitizer_common/sanitizer_flags.h"
23#include "working_set.h"
24
25// See comment below.
26extern "C" {
27extern void __cxa_atexit(void (*function)(void));
28}
29
30namespace __esan {
31
32bool EsanIsInitialized;
33bool EsanDuringInit;
34ShadowMapping Mapping;
35
36// Different tools use different scales within the same shadow mapping scheme.
37// The scale used here must match that used by the compiler instrumentation.
38// This array is indexed by the ToolType enum.
39static const uptr ShadowScale[] = {
40 0, // ESAN_None.
41 2, // ESAN_CacheFrag: 4B:1B, so 4 to 1 == >>2.
42 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6.
43};
44
45// We are combining multiple performance tuning tools under the umbrella of
46// one EfficiencySanitizer super-tool. Most of our tools have very similar
47// memory access instrumentation, shadow memory mapping, libc interception,
48// etc., and there is typically more shared code than distinct code.
49//
50// We are not willing to dispatch on tool dynamically in our fastpath
51// instrumentation: thus, which tool to use is a static option selected
52// at compile time and passed to __esan_init().
53//
54// We are willing to pay the overhead of tool dispatch in the slowpath to more
55// easily share code. We expect to only come here rarely.
56// If this becomes a performance hit, we can add separate interface
57// routines for each subtool (e.g., __esan_cache_frag_aligned_load_4).
58// But for libc interceptors, we'll have to do one of the following:
59// A) Add multiple-include support to sanitizer_common_interceptors.inc,
60// instantiate it separately for each tool, and call the selected
61// tool's intercept setup code.
62// B) Build separate static runtime libraries, one for each tool.
63// C) Completely split the tools into separate sanitizers.
64
65void processRangeAccess(uptr PC, uptr Addr, int Size, bool IsWrite) {
66 VPrintf(3, "in esan::%s %p: %c %p %d\n", __FUNCTION__, PC,
67 IsWrite ? 'w' : 'r', Addr, Size);
68 if (__esan_which_tool == ESAN_CacheFrag) {
69 // TODO(bruening): add shadow mapping and update shadow bits here.
70 // We'll move this to cache_frag.cpp once we have something.
71 } else if (__esan_which_tool == ESAN_WorkingSet) {
72 processRangeAccessWorkingSet(PC, Addr, Size, IsWrite);
73 }
74}
75
76bool processSignal(int SigNum, void (*Handler)(int), void (**Result)(int)) {
77 if (__esan_which_tool == ESAN_WorkingSet)
78 return processWorkingSetSignal(SigNum, Handler, Result);
79 return true;
80}
81
82bool processSigaction(int SigNum, const void *Act, void *OldAct) {
83 if (__esan_which_tool == ESAN_WorkingSet)
84 return processWorkingSetSigaction(SigNum, Act, OldAct);
85 return true;
86}
87
88bool processSigprocmask(int How, void *Set, void *OldSet) {
89 if (__esan_which_tool == ESAN_WorkingSet)
90 return processWorkingSetSigprocmask(How, Set, OldSet);
91 return true;
92}
93
94#if SANITIZER_DEBUG
95static bool verifyShadowScheme() {
96 // Sanity checks for our shadow mapping scheme.
97 uptr AppStart, AppEnd;
98 if (Verbosity() >= 3) {
99 for (int i = 0; getAppRegion(i, &AppStart, &AppEnd); ++i) {
100 VPrintf(3, "App #%d: [%zx-%zx) (%zuGB)\n", i, AppStart, AppEnd,
101 (AppEnd - AppStart) >> 30);
102 }
103 }
104 for (int Scale = 0; Scale < 8; ++Scale) {
105 Mapping.initialize(Scale);
106 if (Verbosity() >= 3) {
107 VPrintf(3, "\nChecking scale %d\n", Scale);
108 uptr ShadowStart, ShadowEnd;
109 for (int i = 0; getShadowRegion(i, &ShadowStart, &ShadowEnd); ++i) {
110 VPrintf(3, "Shadow #%d: [%zx-%zx) (%zuGB)\n", i, ShadowStart,
111 ShadowEnd, (ShadowEnd - ShadowStart) >> 30);
112 }
113 for (int i = 0; getShadowRegion(i, &ShadowStart, &ShadowEnd); ++i) {
114 VPrintf(3, "Shadow(Shadow) #%d: [%zx-%zx)\n", i,
115 appToShadow(ShadowStart), appToShadow(ShadowEnd - 1)+1);
116 }
117 }
118 for (int i = 0; getAppRegion(i, &AppStart, &AppEnd); ++i) {
119 DCHECK(isAppMem(AppStart));
120 DCHECK(!isAppMem(AppStart - 1));
121 DCHECK(isAppMem(AppEnd - 1));
122 DCHECK(!isAppMem(AppEnd));
123 DCHECK(!isShadowMem(AppStart));
124 DCHECK(!isShadowMem(AppEnd - 1));
125 DCHECK(isShadowMem(appToShadow(AppStart)));
126 DCHECK(isShadowMem(appToShadow(AppEnd - 1)));
127 // Double-shadow checks.
128 DCHECK(!isShadowMem(appToShadow(appToShadow(AppStart))));
129 DCHECK(!isShadowMem(appToShadow(appToShadow(AppEnd - 1))));
130 }
131 // Ensure no shadow regions overlap each other.
132 uptr ShadowAStart, ShadowBStart, ShadowAEnd, ShadowBEnd;
133 for (int i = 0; getShadowRegion(i, &ShadowAStart, &ShadowAEnd); ++i) {
134 for (int j = 0; getShadowRegion(j, &ShadowBStart, &ShadowBEnd); ++j) {
135 DCHECK(i == j || ShadowAStart >= ShadowBEnd ||
136 ShadowAEnd <= ShadowBStart);
137 }
138 }
139 }
140 return true;
141}
142#endif
143
144static void initializeShadow() {
145 verifyAddressSpace();
146
147 DCHECK(verifyShadowScheme());
148
149 Mapping.initialize(ShadowScale[__esan_which_tool]);
150
151 VPrintf(1, "Shadow scale=%d offset=%p\n", Mapping.Scale, Mapping.Offset);
152
153 uptr ShadowStart, ShadowEnd;
154 for (int i = 0; getShadowRegion(i, &ShadowStart, &ShadowEnd); ++i) {
155 VPrintf(1, "Shadow #%d: [%zx-%zx) (%zuGB)\n", i, ShadowStart, ShadowEnd,
156 (ShadowEnd - ShadowStart) >> 30);
157
158 uptr Map;
159 if (__esan_which_tool == ESAN_WorkingSet) {
160 // We want to identify all shadow pages that are touched so we start
161 // out inaccessible.
162 Map = (uptr)MmapFixedNoAccess(ShadowStart, ShadowEnd- ShadowStart,
163 "shadow");
164 } else {
165 Map = (uptr)MmapFixedNoReserve(ShadowStart, ShadowEnd - ShadowStart,
166 "shadow");
167 }
168 if (Map != ShadowStart) {
169 Printf("FATAL: EfficiencySanitizer failed to map its shadow memory.\n");
170 Die();
171 }
172
173 if (common_flags()->no_huge_pages_for_shadow)
174 NoHugePagesInRegion(ShadowStart, ShadowEnd - ShadowStart);
175 if (common_flags()->use_madv_dontdump)
176 DontDumpShadowMemory(ShadowStart, ShadowEnd - ShadowStart);
177
178 // TODO: Call MmapNoAccess() on in-between regions.
179 }
180}
181
182void initializeLibrary(ToolType Tool) {
183 // We assume there is only one thread during init, but we need to
184 // guard against double-init when we're (re-)called from an
185 // early interceptor.
186 if (EsanIsInitialized || EsanDuringInit)
187 return;
188 EsanDuringInit = true;
189 CHECK(Tool == __esan_which_tool);
190 SanitizerToolName = "EfficiencySanitizer";
191 CacheBinaryName();
192 initializeFlags();
193
194 // Intercepting libc _exit or exit via COMMON_INTERCEPTOR_ON_EXIT only
195 // finalizes on an explicit exit call by the app. To handle a normal
196 // exit we register an atexit handler.
197 ::__cxa_atexit((void (*)())finalizeLibrary);
198
199 VPrintf(1, "in esan::%s\n", __FUNCTION__);
200 if (__esan_which_tool <= ESAN_None || __esan_which_tool >= ESAN_Max) {
201 Printf("ERROR: unknown tool %d requested\n", __esan_which_tool);
202 Die();
203 }
204
205 initializeShadow();
206 if (__esan_which_tool == ESAN_WorkingSet)
207 initializeShadowWorkingSet();
208
209 initializeInterceptors();
210
211 if (__esan_which_tool == ESAN_CacheFrag) {
212 initializeCacheFrag();
213 } else if (__esan_which_tool == ESAN_WorkingSet) {
214 initializeWorkingSet();
215 }
216
217 EsanIsInitialized = true;
218 EsanDuringInit = false;
219}
220
221int finalizeLibrary() {
222 VPrintf(1, "in esan::%s\n", __FUNCTION__);
223 if (__esan_which_tool == ESAN_CacheFrag) {
224 return finalizeCacheFrag();
225 } else if (__esan_which_tool == ESAN_WorkingSet) {
226 return finalizeWorkingSet();
227 }
228 return 0;
229}
230
231void reportResults() {
232 VPrintf(1, "in esan::%s\n", __FUNCTION__);
233 if (__esan_which_tool == ESAN_CacheFrag) {
234 return reportCacheFrag();
235 } else if (__esan_which_tool == ESAN_WorkingSet) {
236 return reportWorkingSet();
237 }
238}
239
240void processCompilationUnitInit(void *Ptr) {
241 VPrintf(2, "in esan::%s\n", __FUNCTION__);
242 if (__esan_which_tool == ESAN_CacheFrag) {
243 DCHECK(Ptr != nullptr);
244 processCacheFragCompilationUnitInit(Ptr);
245 } else {
246 DCHECK(Ptr == nullptr);
247 }
248}
249
250// This is called when the containing module is unloaded.
251// For the main executable module, this is called after finalizeLibrary.
252void processCompilationUnitExit(void *Ptr) {
253 VPrintf(2, "in esan::%s\n", __FUNCTION__);
254 if (__esan_which_tool == ESAN_CacheFrag) {
255 DCHECK(Ptr != nullptr);
256 processCacheFragCompilationUnitExit(Ptr);
257 } else {
258 DCHECK(Ptr == nullptr);
259 }
260}
261
262} // namespace __esan