]>
git.proxmox.com Git - rustc.git/blob - src/compiler-rt/lib/esan/esan.cpp
1 //===-- esan.cpp ----------------------------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file is a part of EfficiencySanitizer, a family of performance tuners.
12 // Main file (entry points) for the Esan run-time.
13 //===----------------------------------------------------------------------===//
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"
27 extern void __cxa_atexit(void (*function
)(void));
32 bool EsanIsInitialized
;
34 ShadowMapping Mapping
;
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
[] = {
41 2, // ESAN_CacheFrag: 4B:1B, so 4 to 1 == >>2.
42 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6.
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.
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().
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.
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
);
76 bool processSignal(int SigNum
, void (*Handler
)(int), void (**Result
)(int)) {
77 if (__esan_which_tool
== ESAN_WorkingSet
)
78 return processWorkingSetSignal(SigNum
, Handler
, Result
);
82 bool processSigaction(int SigNum
, const void *Act
, void *OldAct
) {
83 if (__esan_which_tool
== ESAN_WorkingSet
)
84 return processWorkingSetSigaction(SigNum
, Act
, OldAct
);
88 bool processSigprocmask(int How
, void *Set
, void *OldSet
) {
89 if (__esan_which_tool
== ESAN_WorkingSet
)
90 return processWorkingSetSigprocmask(How
, Set
, OldSet
);
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);
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);
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);
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))));
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
);
146 static void initializeShadow() {
147 verifyAddressSpace();
149 // This is based on the assumption that the intial stack is always allocated
150 // in the topmost segment of the user address space and the assumption
151 // holds true on all the platforms currently supported.
153 (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
155 DCHECK(verifyShadowScheme());
157 Mapping
.initialize(ShadowScale
[__esan_which_tool
]);
159 VPrintf(1, "Shadow scale=%d offset=%p\n", Mapping
.Scale
, Mapping
.Offset
);
161 uptr ShadowStart
, ShadowEnd
;
162 for (int i
= 0; getShadowRegion(i
, &ShadowStart
, &ShadowEnd
); ++i
) {
163 VPrintf(1, "Shadow #%d: [%zx-%zx) (%zuGB)\n", i
, ShadowStart
, ShadowEnd
,
164 (ShadowEnd
- ShadowStart
) >> 30);
167 if (__esan_which_tool
== ESAN_WorkingSet
) {
168 // We want to identify all shadow pages that are touched so we start
170 Map
= (uptr
)MmapFixedNoAccess(ShadowStart
, ShadowEnd
- ShadowStart
,
173 Map
= (uptr
)MmapFixedNoReserve(ShadowStart
, ShadowEnd
- ShadowStart
,
176 if (Map
!= ShadowStart
) {
177 Printf("FATAL: EfficiencySanitizer failed to map its shadow memory.\n");
181 if (common_flags()->no_huge_pages_for_shadow
)
182 NoHugePagesInRegion(ShadowStart
, ShadowEnd
- ShadowStart
);
183 if (common_flags()->use_madv_dontdump
)
184 DontDumpShadowMemory(ShadowStart
, ShadowEnd
- ShadowStart
);
186 // TODO: Call MmapNoAccess() on in-between regions.
190 void initializeLibrary(ToolType Tool
) {
191 // We assume there is only one thread during init, but we need to
192 // guard against double-init when we're (re-)called from an
193 // early interceptor.
194 if (EsanIsInitialized
|| EsanDuringInit
)
196 EsanDuringInit
= true;
197 CHECK(Tool
== __esan_which_tool
);
198 SanitizerToolName
= "EfficiencySanitizer";
202 // Intercepting libc _exit or exit via COMMON_INTERCEPTOR_ON_EXIT only
203 // finalizes on an explicit exit call by the app. To handle a normal
204 // exit we register an atexit handler.
205 ::__cxa_atexit((void (*)())finalizeLibrary
);
207 VPrintf(1, "in esan::%s\n", __FUNCTION__
);
208 if (__esan_which_tool
<= ESAN_None
|| __esan_which_tool
>= ESAN_Max
) {
209 Printf("ERROR: unknown tool %d requested\n", __esan_which_tool
);
214 if (__esan_which_tool
== ESAN_WorkingSet
)
215 initializeShadowWorkingSet();
217 initializeInterceptors();
219 if (__esan_which_tool
== ESAN_CacheFrag
) {
220 initializeCacheFrag();
221 } else if (__esan_which_tool
== ESAN_WorkingSet
) {
222 initializeWorkingSet();
225 EsanIsInitialized
= true;
226 EsanDuringInit
= false;
229 int finalizeLibrary() {
230 VPrintf(1, "in esan::%s\n", __FUNCTION__
);
231 if (__esan_which_tool
== ESAN_CacheFrag
) {
232 return finalizeCacheFrag();
233 } else if (__esan_which_tool
== ESAN_WorkingSet
) {
234 return finalizeWorkingSet();
239 void reportResults() {
240 VPrintf(1, "in esan::%s\n", __FUNCTION__
);
241 if (__esan_which_tool
== ESAN_CacheFrag
) {
242 return reportCacheFrag();
243 } else if (__esan_which_tool
== ESAN_WorkingSet
) {
244 return reportWorkingSet();
248 void processCompilationUnitInit(void *Ptr
) {
249 VPrintf(2, "in esan::%s\n", __FUNCTION__
);
250 if (__esan_which_tool
== ESAN_CacheFrag
) {
251 DCHECK(Ptr
!= nullptr);
252 processCacheFragCompilationUnitInit(Ptr
);
254 DCHECK(Ptr
== nullptr);
258 // This is called when the containing module is unloaded.
259 // For the main executable module, this is called after finalizeLibrary.
260 void processCompilationUnitExit(void *Ptr
) {
261 VPrintf(2, "in esan::%s\n", __FUNCTION__
);
262 if (__esan_which_tool
== ESAN_CacheFrag
) {
263 DCHECK(Ptr
!= nullptr);
264 processCacheFragCompilationUnitExit(Ptr
);
266 DCHECK(Ptr
== nullptr);
270 unsigned int getSampleCount() {
271 VPrintf(1, "in esan::%s\n", __FUNCTION__
);
272 if (__esan_which_tool
== ESAN_WorkingSet
) {
273 return getSampleCountWorkingSet();
278 } // namespace __esan