• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- 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.
26 extern "C" {
27 extern void __cxa_atexit(void (*function)(void));
28 }
29 
30 namespace __esan {
31 
32 bool EsanIsInitialized;
33 bool EsanDuringInit;
34 ShadowMapping 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.
39 static 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 
processRangeAccess(uptr PC,uptr Addr,int Size,bool IsWrite)65 void 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 
processSignal(int SigNum,void (* Handler)(int),void (** Result)(int))76 bool 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 
processSigaction(int SigNum,const void * Act,void * OldAct)82 bool 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 
processSigprocmask(int How,void * Set,void * OldSet)88 bool 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
verifyShadowScheme()95 static 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 
initializeShadow()144 static 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 
initializeLibrary(ToolType Tool)182 void 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 
finalizeLibrary()221 int 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 
reportResults()231 void 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 
processCompilationUnitInit(void * Ptr)240 void 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.
processCompilationUnitExit(void * Ptr)252 void 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
263