[esan|cfrag] Compute the struct field access difference ratio

Summary:
Computes the struct field access variation based on each field access
count.

Adds a flag to control the report thresholds.

Updates struct-simple.cpp with variance report output.

Reviewers: aizatsky

Subscribers: kubabrecka, zhaoqin, llvm-commits, eugenis, vitalybuka, kcc, bruening

Differential Revision: http://reviews.llvm.org/D20914

llvm-svn: 271734
This commit is contained in:
Qin Zhao 2016-06-03 20:48:17 +00:00
parent 07bf5349ee
commit bc929e4765
4 changed files with 122 additions and 23 deletions

View File

@ -13,8 +13,11 @@
//===----------------------------------------------------------------------===//
#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 <string.h>
namespace __esan {
@ -38,8 +41,8 @@ struct CacheFragInfo {
struct StructCounter {
StructInfo *Struct;
u64 Count; // The total access count of the struct.
u32 Variance; // Variance score for the struct layout access.
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.
@ -51,18 +54,73 @@ struct Context {
};
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 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(" count = %llu, ratio = %llu\n", Handle->Count, Handle->Ratio);
for (u32 i = 0; i < Struct->NumFields; ++i) {
Report(" #%2u: count = %llu,\t type = %s\n", i, Struct->FieldCounters[i],
Struct->FieldTypeNames[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)
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);
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);
VPrintf(2, " Duplicated %s: %u fields\n", Struct->StructName,
Struct->NumFields);
}
}
}
@ -74,34 +132,37 @@ static void unregisterStructInfo(CacheFragInfo *CacheFrag) {
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);
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);
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);
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);
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);
VPrintf(2, "in esan::%s: %s with %u class(es)/struct(s)\n", __FUNCTION__,
CacheFrag->UnitName, CacheFrag->NumStructs);
unregisterStructInfo(CacheFrag);
}
@ -110,13 +171,12 @@ void initializeCacheFrag() {
// 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 = new (CtxMem) Context();
Ctx->NumStructs = 0;
}
int finalizeCacheFrag() {
VPrintf(2, "in esan::%s\n", __FUNCTION__);
reportStructSummary();
return 0;
}

View File

@ -45,3 +45,12 @@ ESAN_FLAG(int, sample_freq, 20,
// Number N samples number N-1 every (1 << snapshot_step) instance of N-1.
ESAN_FLAG(int, snapshot_step, 2, "Working set tool: the log of the sampling "
"performed for the next-higher-frequency snapshot series.")
//===----------------------------------------------------------------------===//
// Cache Fragmentation tool options
//===----------------------------------------------------------------------===//
// The difference information of a struct is reported if the struct's difference
// score is greater than the report_threshold.
ESAN_FLAG(int, report_threshold, 1<<10, "Cache-frag tool: the struct difference"
" score threshold for reporting.")

View File

@ -138,22 +138,52 @@ int main(int argc, char **argv) {
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 5 class(es)/struct(s)
// CHECK-NEXT: Unregister class.C#3#14#13#13: 3 fields
// CHECK-NEXT: {{.*}} class C
// CHECK-NEXT: {{.*}} count = 5, ratio = 3
// CHECK-NEXT: {{.*}} # 0: count = 2, type = %struct.anon = type { i32, i32 }
// CHECK-NEXT: {{.*}} # 1: count = 2, type = %union.anon = type { double }
// CHECK-NEXT: {{.*}} # 2: count = 1, type = [10 x i8]
// CHECK-NEXT: Unregister struct.anon#2#11#11: 2 fields
// CHECK-NEXT: {{.*}} struct anon
// CHECK-NEXT: {{.*}} count = 2, ratio = 1
// CHECK-NEXT: {{.*}} # 0: count = 1, type = i32
// CHECK-NEXT: {{.*}} # 1: count = 1, type = i32
// CHECK-NEXT: Unregister union.anon#1#3: 1 fields
// CHECK-NEXT: Unregister struct.S#2#11#11: 2 fields
// CHECK-NEXT: {{.*}} struct S
// CHECK-NEXT: {{.*}} count = 2, ratio = 2
// CHECK-NEXT: {{.*}} # 0: count = 2, type = i32
// CHECK-NEXT: {{.*}} # 1: count = 0, type = i32
// CHECK-NEXT: Unregister struct.D#3#11#11#11: 3 fields
// CHECK-NEXT: {{.*}} struct D
// CHECK-NEXT: {{.*}} count = 2, ratio = 2
// CHECK-NEXT: {{.*}} # 0: count = 1, type = i32
// CHECK-NEXT: {{.*}} # 1: count = 1, type = i32
// CHECK-NEXT: {{.*}} # 2: count = 0, type = i32
// 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: {{.*}} struct A
// CHECK-NEXT: {{.*}} count = 2049, ratio = 2048
// CHECK-NEXT: {{.*}} # 0: count = 2048, type = i32
// CHECK-NEXT: {{.*}} # 1: count = 1, type = i32
// CHECK-NEXT: Unregister struct.B#2#3#2: 2 fields
// CHECK-NEXT: {{.*}} struct B
// CHECK-NEXT: {{.*}} count = 2097153, ratio = 2097152
// CHECK-NEXT: {{.*}} # 0: count = 1, type = float
// CHECK-NEXT: {{.*}} # 1: count = 2097152, type = double
// 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
// CHECK-NEXT: {{.*}} struct D
// CHECK-NEXT: {{.*}} count = 1, ratio = 1
// CHECK-NEXT: {{.*}} # 0: count = 1, type = i32
// CHECK-NEXT: {{.*}} # 1: count = 0, type = i32
// CHECK-NEXT: {{.*}}EfficiencySanitizer: total struct field access count = 2099214
}
#endif // MAIN

View File

@ -9,6 +9,6 @@ int main(int argc, char **argv) {
// CHECK-NEXT: Shadow #1: [124000000000-12c000000000) (512GB)
// CHECK-NEXT: Shadow #2: [148000000000-150000000000) (512GB)
// CHECK-NEXT: in esan::finalizeLibrary
// CHECK-NEXT: ==verbose-simple{{.*}}EfficiencySanitizer is not finished: nothing yet to report
// CHECK-NEXT: ==verbose-simple{{.*}}EfficiencySanitizer: total struct field access count = 0
return 0;
}