forked from OSchip/llvm-project
[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:
parent
07bf5349ee
commit
bc929e4765
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue