34 #include <unordered_map>
44 static KNOB<string>
KnobCheck(KNOB_MODE_WRITEONCE,
"pintool",
"check",
"",
45 "name of the per-victim CCT-shape check function to run at Fini (required)");
53 unordered_map<uint32_t, uint64_t>
hits;
82 PIN_GetLock(&
g_lock, t + 1);
123 INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)
RecordCtxt,
124 IARG_UINT32, slot, IARG_THREAD_ID, IARG_END);
130 IARG_UINT32, slot, IARG_THREAD_ID, IARG_END);
146 return INS_IsProcedureCall(ins) || INS_IsRet(ins);
152 if (!IMG_IsMainExecutable(img))
154 RTN mainRtn = RTN_FindByName(img,
"main");
155 if (!RTN_Valid(mainRtn))
176 size_t maxDepthObserved = 0;
177 size_t totalDistinctHandles = 0;
188 size_t subMaxDepthObserved = 0;
191 auto it = byLeafFn.find(fn);
192 return it == byLeafFn.end() ? 0 : it->second.size();
194 bool hasChain(
const vector<string>& expected)
const {
195 return chains.count(expected) > 0;
197 bool hasFn(
const string& fn)
const {
198 return byLeafFn.count(fn) > 0;
201 for (
const auto& kv : chains) {
202 for (
const auto& n : kv.first)
212 const string& expectedParent)
const {
213 auto it = byLeafFn.find(leafFn);
214 if (it == byLeafFn.end() || it->second.empty())
216 return std::all_of(it->second.begin(), it->second.end(),
218 return chain.size() >= 2 && chain[chain.size() - 2] == expectedParent;
228 for (
const auto& kv : chains) {
230 for (
const auto& n : kv.first)
242 auto it = subByLeafFn.find(fn);
243 return it == subByLeafFn.end() ? 0 : it->second.size();
246 return subByLeafFn.count(fn) > 0;
249 for (
const auto& kv : subChains) {
250 for (
const auto& n : kv.first)
258 for (
const auto& kv : subChains) {
260 for (
const auto& n : kv.first)
273 for (
const auto& kv : subChains) {
274 for (
const auto& e : kv.first)
285 const string& expectedParent)
const {
286 auto it = subByLeafFn.find(leafFn);
287 if (it == subByLeafFn.end() || it->second.empty())
289 return std::all_of(it->second.begin(), it->second.end(),
291 return chain.size() >= 2 && chain[chain.size() - 2] == expectedParent;
304 const set<string>& badAncestors)
const {
305 for (
const auto& kv : chains) {
306 const auto& chain = kv.first;
307 for (
size_t i = 0; i < chain.size(); ++i) {
310 for (
size_t j = 0; j < i; ++j) {
311 if (badAncestors.count(chain[j]))
343 return n ==
"CRASHED !!" ||
345 n ==
"FAILED_TO_READ" ||
350 return n ==
"IN PLT BUT NOT VALID GOT" ||
351 n ==
"UNRECOGNIZED PLT SIGNATURE" ||
352 n ==
"Truncated call path (due to deep call chain)";
361 return n.rfind(
"THREAD[", 0) == 0 && n.find(
"]_ROOT_CTXT") != string::npos;
379 for (
const auto& kv : td->hits) {
380 uint32_t
h = kv.first;
381 uint64_t hits = kv.second;
382 vector<Context> chain;
398 names.reserve(chain.size());
399 int mainIdxLeafFirst = -1;
400 for (
size_t i = 0; i < chain.size(); ++i) {
410 names.push_back(c.functionName);
414 if (mainTraceStart != 0 &&
416 mainIdxLeafFirst = (int)(names.size() - 1);
423 FnChain sig(names.rbegin(), names.rend());
427 const string& leafFn = sig.back();
435 if (mainIdxLeafFirst >= 0) {
436 FnChain subLeafFirst(names.begin(),
437 names.begin() + mainIdxLeafFirst + 1);
438 FnChain subSig(subLeafFirst.rbegin(), subLeafFirst.rend());
443 const string& subLeaf = subSig.back();
455 template <
typename A,
typename B>
457 if (!(obs <= (
A)lim)) {
459 os <<
"FAIL " << checkName <<
": " << what
460 <<
" expected <= " << lim <<
" observed " << obs;
461 failures.push_back(os.str());
464 template <
typename A,
typename B>
466 if (!(obs >= (
A)lim)) {
468 os <<
"FAIL " << checkName <<
": " << what
469 <<
" expected >= " << lim <<
" observed " << obs;
470 failures.push_back(os.str());
473 template <
typename A,
typename B,
typename C>
475 if (!(obs >= (
A)lo && obs <= (
A)hi)) {
477 os <<
"FAIL " << checkName <<
": " << what
478 <<
" expected in [" << lo <<
"," << hi <<
"] observed " << obs;
479 failures.push_back(os.str());
482 template <
typename A,
typename B>
484 if (!(obs == (
A)want)) {
486 os <<
"FAIL " << checkName <<
": " << what
487 <<
" expected == " << want <<
" observed " << obs;
488 failures.push_back(os.str());
494 os <<
"FAIL " << checkName <<
": " << what <<
" (expected true)";
495 failures.push_back(os.str());
501 os <<
"FAIL " << checkName <<
": sentinel frames appeared:";
503 os <<
" [" << kv.first <<
" x" << kv.second <<
"]";
505 failures.push_back(os.str());
519 "__cxa_allocate_exception",
522 "__gxx_personality_v0",
523 "_Unwind_RaiseException",
525 "_Unwind_Resume_or_Rethrow",
526 "_Unwind_ForcedUnwind",
530 "_Unwind_GetTextRelBase",
545 const string& parent) {
548 (fn +
"'s immediate parent is " + parent).c_str());
550 (fn +
" has NO ancestor in the throw/unwind machinery "
551 "(catch/try body must not be rooted under __cxa_throw)")
569 "sub-CCT under main has exactly 1 distinct chain ending in fib");
571 "fib appears at most once along any path from main to leaf");
573 "fib's immediate parent in every sub-chain is main");
581 "sub-CCT under main has exactly 1 distinct chain ending in A");
583 "A appears at most once along any path from main to leaf");
585 "A's immediate parent in every sub-chain is main");
594 "sub-CCT under main has exactly 1 distinct chain ending in multi "
595 "(all 3 direct self-call sites collapsed into one physical frame)");
597 "multi appears at most once along any path from main to leaf");
599 "multi's immediate parent in every sub-chain is main");
619 "sub-CCT under main has exactly 13 distinct chains ending in indirect_rec "
620 "(1 initial direct call from main + 12 indirect frames, no collapse)");
622 "indirect_rec appears exactly 13 times along the deepest path from main");
636 "sub-CCT under main has exactly 7 distinct chains ending in mixed "
637 "(6 indirect frames + 1 initial call, direct sites collapse within each)");
639 "mixed appears exactly 7 times along the deepest path from main");
652 "at least one app-side leaf attributed (empty-name or .text)");
663 "sub-CCT under main has exactly 1 distinct chain ending in descend "
664 "(collapse holds under a throw path)");
666 "descend appears at most once along any path from main");
668 "descend's immediate parent in every sub-chain is main");
674 r.
expectGE(inv.
subChains.size(), (
size_t)1,
"at least one sub-chain under main");
714 r.
expectTrue(inv.
hasFn(
"recurse"),
"recurse appears as a leaf function");
716 "recurse's immediate parent is always main (recurse fully collapses even under throw)");
718 "recurse never appears as ancestor of recurse (landing-pad re-anchor holds)");
734 "main still reachable after the ctor throw");
736 "main appears at most a small bounded number of times (bug pre-fix: unbounded)");
761 "may_throw's immediate parent in every chain is resume_after_catch");
763 "post_catch_worker's immediate parent in every chain is main "
764 "(NOT resume_after_catch or may_throw)");
777 r.
expectTrue(inv.
hasFn(
"go_deep"),
"go_deep appears as a leaf function");
779 "go_deep's immediate parent is always main");
795 r.
expectTrue(inv.
hasFn(
"handler"),
"signal handler appears in the CCT");
797 "main still reachable under itself after SIGSEGV+siglongjmp");
819 "inner's immediate parent is middle");
821 "middle's immediate parent is outer");
823 "outer's immediate parent is main (unwind didn't strand it)");
842 r.
expectTrue(inv.
hasFn(
"raise_it"),
"raise_it appears as a leaf function");
845 "main still reachable after nested rethrow");
859 r.
expectTrue(inv.
hasFn(
"thrower"),
"thrower appears as a leaf function");
861 "main still reachable under itself after catch-all");
875 r.
expectTrue(inv.
hasFn(
"thrower"),
"thrower appears as a leaf function");
877 "main still reachable under itself after cleanup unwind");
889 "thrower's immediate parent is main");
901 "thrower's immediate parent is main");
938 "rec appears at most twice per chain (collapsed TraceNode + "
939 "catch-landing-pad Pin trace labeled 'rec' by RTN name lookup)");
948 "rectry_after_marker must NOT appear (unreachable code path)");
959 "main is reachable in the sub-CCT");
973 r.
expectTrue(inv.
hasFn(
"thrower"),
"thrower appears as a leaf function");
975 "thrower's immediate parent is main");
1031 static VOID
ThreadFini(THREADID t,
const CONTEXT*, INT32, VOID*) {
1039 if (check.empty()) {
1040 fprintf(stderr,
"cct_shape_check: -check <name> is required\n");
1043 auto it =
kChecks.find(check);
1045 fprintf(stderr,
"cct_shape_check: no check registered for '%s'\n", check.c_str());
1051 if (getenv(
"CCT_SHAPE_DEBUG")) {
1056 fprintf(stderr,
"== thread %u has %zu handles\n", t, td->hits.size());
1058 for (
const auto& kv : td->hits) {
1061 fprintf(stderr,
"-- handle %u --\n", kv.first);
1063 fprintf(stderr,
"\n");
1077 fprintf(stderr,
"cct_shape_check[%s] %zu failure(s):\n",
1080 fprintf(stderr,
" %s\n",
f.c_str());
1085 if (!r.
failures.empty() || getenv(
"CCT_SHAPE_ALWAYS_DUMP")) {
1086 fprintf(stderr,
"-- inventory summary [%s] --\n", check.c_str());
1087 fprintf(stderr,
"distinct_chains=%zu total_distinct_handles=%zu max_depth=%zu\n",
1089 for (
const auto& kv : inv.
byLeafFn) {
1090 fprintf(stderr,
" leaf_fn '%s' chains=%zu hits=%llu\n",
1091 kv.first.c_str(), kv.second.size(),
1096 if (getenv(
"CCT_SHAPE_DUMP_CHAINS")) {
1097 for (
const auto& kv : inv.
chains) {
1098 fprintf(stderr,
" CHAIN hits=%llu :", (
unsigned long long)kv.second);
1099 for (
const auto& n : kv.first)
1100 fprintf(stderr,
" %s ;", n.c_str());
1101 fprintf(stderr,
"\n");
1128 PIN_ERROR(
"cct_shape_check: cctlib CCT-shape assertion tool. Requires -check <victim_name>.\n" + KNOB_BASE::StringKnobSummary() +
"\n");
1135 if (PIN_Init(argc, argv))
1145 g_tlsKey = PIN_CreateThreadDataKey(
nullptr);
1147 PIN_AddThreadFiniFunction(
ThreadFini,
nullptr);
1148 PIN_AddFiniFunction(
FiniFunc,
nullptr);
1165 IMG_AddInstrumentFunction(
OnImgLoad,
nullptr);
static void check_exc_deep_unwind(const CctInventory &inv, AssertionRecorder &r)
int main(int argc, char *argv[])
static void check_exc_none_tn(const CctInventory &inv, AssertionRecorder &r)
static void check_exc_ctor_throw(const CctInventory &inv, AssertionRecorder &r)
static bool isSentinelName(const string &n)
static vector< THREADID > g_threads
static KNOB< string > KnobCheck(KNOB_MODE_WRITEONCE, "pintool", "check", "", "name of the per-victim CCT-shape check function to run at Fini (required)")
static void FiniFunc(INT32 code, VOID *v)
static void check_exc_dtor_cleanup(const CctInventory &inv, AssertionRecorder &r)
static void RunChecksAndExit(const string &check)
static void check_rec_fib_deep(const CctInventory &inv, AssertionRecorder &r)
static VOID OnImgLoad(IMG img, VOID *)
static void check_exc_stress_loop(const CctInventory &inv, AssertionRecorder &r)
static VOID CaptureMainHandle(uint32_t opaqueHandle, THREADID t)
static bool isRootName(const string &n)
static void check_rec_baseline_nonrec(const CctInventory &inv, AssertionRecorder &r)
static void check_exc_catch_and_resume(const CctInventory &inv, AssertionRecorder &r)
static void check_sig_sigsegv_recover(const CctInventory &inv, AssertionRecorder &r)
static const set< string > kThrowMachineryFns
static void check_sig_longjmp(const CctInventory &inv, AssertionRecorder &r)
static void check_rec_ackermann(const CctInventory &inv, AssertionRecorder &r)
static VOID InstrumentInsCallback(INS ins, VOID *, const uint32_t slot)
static void check_exc_uncaught_tn(const CctInventory &inv, AssertionRecorder &r)
BOOL InterestingInsCallOrRet(INS ins)
static void check_exc_polymorphic(const CctInventory &inv, AssertionRecorder &r)
static VOID ThreadFini(THREADID t, const CONTEXT *, INT32, VOID *)
static VOID RecordCtxt(uint32_t opaqueHandle, THREADID t)
void(*)(const CctInventory &, AssertionRecorder &) CheckFn
static void expectMarkerAnchored(AssertionRecorder &r, const CctInventory &inv, const string &fn, const string &parent)
static bool isSoftSentinelName(const string &n)
static void check_rec_exception(const CctInventory &inv, AssertionRecorder &r)
static void check_exc_simple_throw(const CctInventory &inv, AssertionRecorder &r)
static bool isHardSentinelName(const string &n)
static void check_exc_recurse_trycatch(const CctInventory &inv, AssertionRecorder &r)
static void check_rec_indirect_only(const CctInventory &inv, AssertionRecorder &r)
static void check_rec_multi_direct(const CctInventory &inv, AssertionRecorder &r)
static VOID ThreadStart(THREADID t, CONTEXT *, INT32, VOID *)
static const map< string, CheckFn > kChecks
static VOID ThreadStartRegister(THREADID t, CONTEXT *ctxt, INT32 flags, VOID *v)
static void check_exc_rethrow(const CctInventory &inv, AssertionRecorder &r)
static TData * GetTls(THREADID t)
static void check_rec_stripped(const CctInventory &inv, AssertionRecorder &r)
static void check_exc_catchall(const CctInventory &inv, AssertionRecorder &r)
static void BuildInventoryFromThread(THREADID t, CctInventory &inv)
static void check_rec_mixed_direct_indirect(const CctInventory &inv, AssertionRecorder &r)
int PinCCTLibInit(IsInterestingInsFptr isInterestingIns, FILE *logFile, CCTLibInstrumentInsCallback userCallback, VOID *userCallbackArg, BOOL doDataCentric=false)
hpcrun_metricFlags_t flags
VOID PrintFullCallingContext(const ContextHandle_t ctxtHandle)
VOID GetFullCallingContext(const ContextHandle_t curCtxtHndle, std::vector< Context > &contextVec)
ContextHandle_t GetContextHandle(const THREADID id, const uint32_t slot)
ContextHandle_t GetTraceStartHandle(ContextHandle_t ctxtHandle)
#define PIN_ExitProcess(a)
void expectGE(A obs, B lim, const char *what)
void expectEQ(A obs, B want, const char *what)
void expectNoSentinels(const CctInventory &inv)
void expectInRange(A obs, B lo, C hi, const char *what)
void expectTrue(bool b, const char *what)
vector< string > failures
void expectLE(A obs, B lim, const char *what)
size_t subMaxCountInAnyChain(const string &fn) const
bool everyChainToFnHasImmediateParent(const string &leafFn, const string &expectedParent) const
bool noAncestorOfFnIsInSet(const string &fn, const set< string > &badAncestors) const
size_t subMaxDepthObserved
bool subAnyChainContainsFn(const string &fn) const
map< string, size_t > sentinelCounts
bool hasFn(const string &fn) const
size_t totalDistinctHandles
map< FnChain, uint64_t > subChains
map< string, size_t > softSentinelCounts
map< string, set< FnChain > > byLeafFn
bool hasChain(const vector< string > &expected) const
map< string, uint64_t > hitsByLeafFn
size_t maxCountInAnyChain(const string &fn) const
bool anyChainContainsFn(const string &fn) const
size_t chainCountForFn(const string &fn) const
map< string, set< FnChain > > subByLeafFn
bool everySubChainToFnHasImmediateParent(const string &leafFn, const string &expectedParent) const
size_t subChainCountForFn(const string &fn) const
size_t subChainsContainingFn(const string &fn) const
map< FnChain, uint64_t > chains
bool subHasFn(const string &fn) const
unordered_map< uint32_t, uint64_t > hits
ContextHandle_t mainCtxtHndl