Index: compiler-rt/trunk/lib/esan/cache_frag.cpp =================================================================== --- compiler-rt/trunk/lib/esan/cache_frag.cpp +++ compiler-rt/trunk/lib/esan/cache_frag.cpp @@ -13,13 +13,17 @@ //===----------------------------------------------------------------------===// #include "esan.h" +#include "sanitizer_common/sanitizer_addrhashmap.h" +#include "sanitizer_common/sanitizer_placement_new.h" namespace __esan { +//===-- Struct field access counter runtime -------------------------------===// + // This should be kept consistent with LLVM's EfficiencySanitizer StructInfo. struct StructInfo { const char *StructName; - u32 NumOfFields; + u32 NumFields; u64 *FieldCounters; const char **FieldTypeNames; }; @@ -28,32 +32,91 @@ // The tool-specific information per compilation unit (module). struct CacheFragInfo { const char *UnitName; - u32 NumOfStructs; + u32 NumStructs; StructInfo *Structs; }; +struct StructCounter { + StructInfo *Struct; + u64 Count; // The total access count of the struct. + u32 Variance; // Variance score 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 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); + --Ctx->NumStructs; + } else { + VPrintf(2, " Duplicated %s: %u fields\n", + Struct->StructName, Struct->NumFields); + } + } +} + +static void reportStructSummary() { + // FIXME: iterate StructHashMap and generate the final report. + Report("%s is not finished: nothing yet to report\n", SanitizerToolName); +} + //===-- 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->NumOfStructs); + __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->NumOfStructs); + __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__); - // FIXME: add the cache fragmentation final report. - Report("%s is not finished: nothing yet to report\n", SanitizerToolName); + reportStructSummary(); return 0; } Index: compiler-rt/trunk/test/esan/TestCases/struct-simple.cpp =================================================================== --- compiler-rt/trunk/test/esan/TestCases/struct-simple.cpp +++ compiler-rt/trunk/test/esan/TestCases/struct-simple.cpp @@ -1,6 +1,7 @@ -// RUN: %clang_esan_frag -O0 %s -DPART -c -o %t-part.o 2>&1 +// RUN: %clang_esan_frag -O0 %s -DPART1 -c -o %t-part1.o 2>&1 +// RUN: %clang_esan_frag -O0 %s -DPART2 -c -o %t-part2.o 2>&1 // RUN: %clang_esan_frag -O0 %s -DMAIN -c -o %t-main.o 2>&1 -// RUN: %clang_esan_frag -O0 %t-part.o %t-main.o -o %t 2>&1 +// RUN: %clang_esan_frag -O0 %t-part1.o %t-part2.o %t-main.o -o %t 2>&1 // RUN: %env_esan_opts=verbosity=2 %run %t 2>&1 | FileCheck %s // We generate two different object files from this file with different @@ -10,35 +11,149 @@ #include extern "C" { - void part(); + void part1(); + void part2(); } -//===-- compilation unit without main function ----------------------------===// +//===-- compilation unit part1 without main function ----------------------===// -#ifdef PART -void part() +#ifdef PART1 +struct A { + int x; + int y; +}; + +struct B { + float m; + double n; +}; + +union U { + float f; + double d; +}; + +// Same struct in both main and part1. +struct S { + int s1; + int s2; +}; + +// Different structs with the same name in main and part1. +struct D { + int d1; + int d2; +}; + +void part1() { + struct A a; + struct B b; + union U u; + struct S s; + struct D d; + for (int i = 0; i < (1 << 11); i++) + a.x = 0; + a.y = 1; + b.m = 2.0; + for (int i = 0; i < (1 << 21); i++) + b.n = 3.0; + u.f = 0.0; + u.d = 1.0; + s.s1 = 0; + d.d1 = 0; } -#endif // PART +#endif // PART1 + +//===-- compilation unit part2 without main function ----------------------===// +#ifdef PART2 +// No struct in this part. +void part2() +{ + // do nothing +} +#endif // PART2 //===-- compilation unit with main function -------------------------------===// #ifdef MAIN +class C { +public: + struct { + int x; + int y; + } cs; + union { + float f; + double d; + } cu; + char c[10]; +}; + +// Same struct in both main and part1. +struct S { + int s1; + int s2; +}; + +// Different structs with the same name in main and part1. +struct D { + int d1; + int d2; + int d3; +}; + int main(int argc, char **argv) { // CHECK: in esan::initializeLibrary // CHECK: in esan::initializeCacheFrag // CHECK-NEXT: in esan::processCompilationUnitInit - // CHECK-NEXT: in esan::processCacheFragCompilationUnitInit: {{.*}}struct-simple.cpp with 0 class(es)/struct(s) + // CHECK-NEXT: in esan::processCacheFragCompilationUnitInit: {{.*}}struct-simple.cpp with 5 class(es)/struct(s) + // CHECK-NEXT: Register struct.A#2#11#11: 2 fields + // CHECK-NEXT: Register struct.B#2#3#2: 2 fields + // CHECK-NEXT: Register union.U#1#3: 1 fields + // CHECK-NEXT: Register struct.S#2#11#11: 2 fields + // CHECK-NEXT: Register struct.D#2#11#11: 2 fields // CHECK-NEXT: in esan::processCompilationUnitInit // CHECK-NEXT: in esan::processCacheFragCompilationUnitInit: {{.*}}struct-simple.cpp with 0 class(es)/struct(s) - part(); + // CHECK-NEXT: in esan::processCompilationUnitInit + // CHECK-NEXT: in esan::processCacheFragCompilationUnitInit: {{.*}}struct-simple.cpp with 5 class(es)/struct(s) + // CHECK-NEXT: Register class.C#3#14#13#13: 3 fields + // CHECK-NEXT: Register struct.anon#2#11#11: 2 fields + // CHECK-NEXT: Register union.anon#1#3: 1 fields + // CHECK-NEXT: Duplicated struct.S#2#11#11: 2 fields + // CHECK-NEXT: Register struct.D#3#11#11#11: 3 fields + struct C c[2]; + struct S s; + struct D d; + c[0].cs.x = 0; + c[1].cs.y = 1; + c[0].cu.f = 0.0; + c[1].cu.d = 1.0; + c[0].c[2] = 0; + s.s1 = 0; + d.d1 = 0; + d.d2 = 0; + part1(); + part2(); return 0; // CHECK: in esan::finalizeLibrary // CHECK-NEXT: in esan::finalizeCacheFrag // CHECK-NEXT: {{.*}}EfficiencySanitizer is not finished: nothing yet to report // CHECK-NEXT: in esan::processCompilationUnitExit - // CHECK-NEXT: in esan::processCacheFragCompilationUnitExit: {{.*}}struct-simple.cpp with 0 class(es)/struct(s) + // CHECK-NEXT: in esan::processCacheFragCompilationUnitExit: {{.*}}struct-simple.cpp with 5 class(es)/struct(s) + // CHECK-NEXT: Unregister class.C#3#14#13#13: 3 fields + // CHECK-NEXT: Unregister struct.anon#2#11#11: 2 fields + // CHECK-NEXT: Unregister union.anon#1#3: 1 fields + // CHECK-NEXT: Unregister struct.S#2#11#11: 2 fields + // CHECK-NEXT: Unregister struct.D#3#11#11#11: 3 fields // CHECK-NEXT: in esan::processCompilationUnitExit // CHECK-NEXT: in esan::processCacheFragCompilationUnitExit: {{.*}}struct-simple.cpp with 0 class(es)/struct(s) + // CHECK-NEXT: in esan::processCompilationUnitExit + // CHECK-NEXT: in esan::processCacheFragCompilationUnitExit: {{.*}}struct-simple.cpp with 5 class(es)/struct(s) + // CHECK-NEXT: Unregister struct.A#2#11#11: 2 fields + // CHECK-NEXT: Unregister struct.B#2#3#2: 2 fields + // CHECK-NEXT: Unregister union.U#1#3: 1 fields + // CHECK-NEXT: Duplicated struct.S#2#11#11: 2 fields + // CHECK-NEXT: Unregister struct.D#2#11#11: 2 fields } #endif // MAIN