//===-- cache_frag.cpp ----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of EfficiencySanitizer, a family of performance tuners. // // This file contains cache fragmentation-specific code. //===----------------------------------------------------------------------===// #include "esan.h" #include "esan_flags.h" #include "sanitizer_common/sanitizer_addrhashmap.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include namespace __esan { //===-- Struct field access counter runtime -------------------------------===// // This should be kept consistent with LLVM's EfficiencySanitizer StructInfo. struct StructInfo { const char *StructName; u32 Size; u32 NumFields; u32 *FieldOffset; // auxiliary struct field info. u32 *FieldSize; // auxiliary struct field info. const char **FieldTypeName; // auxiliary struct field info. u64 *FieldCounters; u64 *ArrayCounter; bool hasAuxFieldInfo() { return FieldOffset != nullptr; } }; // This should be kept consistent with LLVM's EfficiencySanitizer CacheFragInfo. // The tool-specific information per compilation unit (module). struct CacheFragInfo { const char *UnitName; u32 NumStructs; StructInfo *Structs; }; struct StructCounter { StructInfo *Struct; u64 Count; // The total access count of the struct. u64 Ratio; // Difference ratio for the struct layout access. }; // We use StructHashMap to keep track of an unique copy of StructCounter. typedef AddrHashMap StructHashMap; struct Context { StructHashMap StructMap; u32 NumStructs; u64 TotalCount; // The total access count of all structs. }; static Context *Ctx; static void reportStructSummary() { // FIXME: provide a better struct field access summary report. Report("%s: total struct field access count = %llu\n", SanitizerToolName, Ctx->TotalCount); } // FIXME: we are still exploring proper ways to evaluate the difference between // struct field counts. Currently, we use a simple formula to calculate the // difference ratio: V1/V2. static inline u64 computeDifferenceRatio(u64 Val1, u64 Val2) { if (Val2 > Val1) { Swap(Val1, Val2); } if (Val2 == 0) Val2 = 1; return (Val1 / Val2); } static void reportStructCounter(StructHashMap::Handle &Handle) { const u32 TypePrintLimit = 512; const char *type, *start, *end; StructInfo *Struct = Handle->Struct; // Union field address calculation is done via bitcast instead of GEP, // so the count for union is always 0. // We skip the union report to avoid confusion. if (strncmp(Struct->StructName, "union.", 6) == 0) return; // Remove the '.' after class/struct during print. if (strncmp(Struct->StructName, "class.", 6) == 0) { type = "class"; start = &Struct->StructName[6]; } else { type = "struct"; start = &Struct->StructName[7]; } // Remove the suffixes with '$' during print. end = strchr(start, '$'); CHECK(end != nullptr); Report(" %s %.*s\n", type, end - start, start); Report(" size = %u, count = %llu, ratio = %llu, array access = %llu\n", Struct->Size, Handle->Count, Handle->Ratio, *Struct->ArrayCounter); if (Struct->hasAuxFieldInfo()) { for (u32 i = 0; i < Struct->NumFields; ++i) { Report(" #%2u: offset = %u,\t size = %u," "\t count = %llu,\t type = %.*s\n", i, Struct->FieldOffset[i], Struct->FieldSize[i], Struct->FieldCounters[i], TypePrintLimit, Struct->FieldTypeName[i]); } } else { for (u32 i = 0; i < Struct->NumFields; ++i) { Report(" #%2u: count = %llu\n", i, Struct->FieldCounters[i]); } } } static void computeStructRatio(StructHashMap::Handle &Handle) { Handle->Ratio = 0; Handle->Count = Handle->Struct->FieldCounters[0]; for (u32 i = 1; i < Handle->Struct->NumFields; ++i) { Handle->Count += Handle->Struct->FieldCounters[i]; Handle->Ratio += computeDifferenceRatio( Handle->Struct->FieldCounters[i - 1], Handle->Struct->FieldCounters[i]); } Ctx->TotalCount += Handle->Count; if (Handle->Ratio >= (u64)getFlags()->report_threshold || (Verbosity() >= 1 && Handle->Count > 0)) reportStructCounter(Handle); } static void registerStructInfo(CacheFragInfo *CacheFrag) { for (u32 i = 0; i < CacheFrag->NumStructs; ++i) { StructInfo *Struct = &CacheFrag->Structs[i]; StructHashMap::Handle H(&Ctx->StructMap, (uptr)Struct->FieldCounters); if (H.created()) { VPrintf(2, " Register %s: %u fields\n", Struct->StructName, Struct->NumFields); H->Struct = Struct; ++Ctx->NumStructs; } else { VPrintf(2, " Duplicated %s: %u fields\n", Struct->StructName, Struct->NumFields); } } } static void unregisterStructInfo(CacheFragInfo *CacheFrag) { // FIXME: if the library is unloaded before finalizeCacheFrag, we should // collect the result for later report. for (u32 i = 0; i < CacheFrag->NumStructs; ++i) { StructInfo *Struct = &CacheFrag->Structs[i]; StructHashMap::Handle H(&Ctx->StructMap, (uptr)Struct->FieldCounters, true); if (H.exists()) { VPrintf(2, " Unregister %s: %u fields\n", Struct->StructName, Struct->NumFields); // FIXME: we should move this call to finalizeCacheFrag once we can // iterate over the hash map there. computeStructRatio(H); --Ctx->NumStructs; } else { VPrintf(2, " Duplicated %s: %u fields\n", Struct->StructName, Struct->NumFields); } } static bool Reported = false; if (Ctx->NumStructs == 0 && !Reported) { Reported = true; reportStructSummary(); } } //===-- Init/exit functions -----------------------------------------------===// void processCacheFragCompilationUnitInit(void *Ptr) { CacheFragInfo *CacheFrag = (CacheFragInfo *)Ptr; VPrintf(2, "in esan::%s: %s with %u class(es)/struct(s)\n", __FUNCTION__, CacheFrag->UnitName, CacheFrag->NumStructs); registerStructInfo(CacheFrag); } void processCacheFragCompilationUnitExit(void *Ptr) { CacheFragInfo *CacheFrag = (CacheFragInfo *)Ptr; VPrintf(2, "in esan::%s: %s with %u class(es)/struct(s)\n", __FUNCTION__, CacheFrag->UnitName, CacheFrag->NumStructs); unregisterStructInfo(CacheFrag); } void initializeCacheFrag() { VPrintf(2, "in esan::%s\n", __FUNCTION__); // We use placement new to initialize Ctx before C++ static initializaion. // We make CtxMem 8-byte aligned for atomic operations in AddrHashMap. static u64 CtxMem[sizeof(Context) / sizeof(u64) + 1]; Ctx = new (CtxMem) Context(); Ctx->NumStructs = 0; } int finalizeCacheFrag() { VPrintf(2, "in esan::%s\n", __FUNCTION__); return 0; } void reportCacheFrag() { VPrintf(2, "in esan::%s\n", __FUNCTION__); // FIXME: Not yet implemented. We need to iterate over all of the // compilation unit data. } } // namespace __esan