Home | History | Annotate | Download | only in esan
      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 
     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 
     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 
     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 
     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
     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 
    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 
    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 
    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 
    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 
    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.
    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