Skip to content
13 changes: 7 additions & 6 deletions ddprof-lib/src/main/cpp/counters.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,10 @@
X(SAMPLES_DROPPED_THREAD_LOCAL, "samples_dropped_thread_local") \
X(SAFECOPY_FAILED, "safecopy_failed") \
X(SAFEFETCH_FAILED, "safefetch_failed") \
/* Every siglongjmp recovery, from any protected window, counted centrally \
* in Profiler::checkFault(). */ \
X(STACKWALK_LONGJMP_RECOVERED, "stackwalk_longjmp_recovered") \
/* Dump-time raw-Method* resolution (HotspotSupport::resolve, reached only \
* for cstack=vm + fjmethodid=false frames). NOT additive with \
* STACKWALK_LONGJMP_RECOVERED: checkFault() bumps that one unconditionally \
* before every siglongjmp, so each fault counted here is counted there too. \
* Subtract, never sum. Non-zero means stale HotSpot metadata (GC or class \
* unloading) reached the dump thread; the frame serializes as "unknown". */ \
/* Dump-time method resolution failures. The frame serializes as "unknown". */ \
X(METHOD_RESOLVE_FAULT_RECOVERED, "method_resolve_fault_recovered") \
/* Symbol length/body rejected during the same resolution: unreadable body, \
* empty (recycled slot), or over MAX_SYMBOL_LEN. A name that merely exceeds \
Expand All @@ -156,6 +153,10 @@
* samples_dropped_thread_local) to isolate non-pool priming drops, never \
* summed. */ \
X(SAMPLES_DROPPED_TLS_POOL_EXHAUSTED, "thread_local_pool_exhausted") \
/* Lookup::resolveMethod() calls that dropped method resolution, \
* because no ProfiledThread could be allocated for the dump thread (OOM): \
* there is nowhere to publish a landing pad. Expected to stay at 0. */ \
X(METHOD_RESOLUTION_DROPPED_TLS, "method_resolution_dropped_tls") \
/* writeElement() guards against a corrupted/dangling JfrMetadata tree. \
* Root cause is still unconfirmed, so these counters are the durable \
* signal for spotting a recurrence. */ \
Expand Down
12 changes: 10 additions & 2 deletions ddprof-lib/src/main/cpp/faultInjection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,16 @@

#include "faultInjection.h"

// The whole translation unit is empty unless fault injection is enabled, so a
// normal build links a no-op object file.
#if defined(__FAULT_INJECTION__) || defined(DEBUG)

#include <stdint.h>
void crashNow() {
volatile uintptr_t* p = (volatile uintptr_t*)nullptr;
*p = 0xBAD;
__builtin_unreachable(); // the store above never returns.
}
#endif

#ifdef __FAULT_INJECTION__

#include "counters.h" // Counters::increment (FAULTS_INJECTED)
Expand Down
52 changes: 52 additions & 0 deletions ddprof-lib/src/main/cpp/faultInjection.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@
//
// return INJECT_FAULT_BOOL_LIKELY(dlopen(name, flags) != nullptr);
//
// INJECT_CRASH_* goes at the same kind of site as INJECT_FAULT_ADDRESS_*, but it
// is a statement rather than an expression wrapper: it takes no argument and
// yields no value. Instead of substituting a poison address for the caller to
// dereference -- which a downstream recovery path (SafeAccess safefetch, a
// sigsetjmp/siglongjmp window) may absorb without a signal ever being raised --
// it raises the SIGSEGV itself, right at the call site. Use it to exercise the
// sigsetjmp/siglongjmp window enclosing the call site, or the top-level crash
// handler where there is no such window:
//
// INJECT_CRASH_LIKELY();
//
// The four tiers name their firing frequency: RARE 0.01%, UNLIKELY 0.1%,
// LIKELY 1%, HIGH 10%. See faultInjection.cpp for the poison-address and PRNG
// details.
Expand All @@ -43,6 +54,14 @@

#include <cassert>

#if defined(__FAULT_INJECTION__) || defined(DEBUG)
// Deliberately dereferences nullptr to raise a real SIGSEGV right now,
// unconditionally (no probability gate, no shouldFire() draw). For exercising
// crash-handler / recovery paths on demand (e.g. from a test), never from a
// production code path.
[[noreturn]] void crashNow();
#endif

#ifdef __FAULT_INJECTION__

#include "arch.h" // u64
Expand Down Expand Up @@ -88,6 +107,22 @@ inline T injectAddress(T ptr, u64 threshold, const char* fn) {
return ptr;
}

// Like injectAddress(), but instead of substituting a poison pointer into the
// expression (leaving recovery to whatever the caller does with it downstream
// -- SafeAccess safefetch, walkVM's sigsetjmp/siglongjmp), this crashes right
// here, right now, when the tier fires. Whatever encloses the call site is what
// gets exercised: the nearest sigsetjmp/siglongjmp window if there is one, the
// top-level crash handler otherwise.
//
// Unlike injectAddress() this wraps no expression -- it takes no pointer and
// returns nothing, so it is a statement, not a drop-in for an
// INJECT_FAULT_ADDRESS_* site. It does nothing when the tier does not fire.
inline void injectCrash(u64 threshold, const char* fn) {
if (__builtin_expect(shouldFire(threshold, fn), 0)) {
crashNow();
}
}

// Returns orig unchanged, or `faulty` when the tier fires. Unlike
// injectAddress() (which fakes an input about to be dereferenced), this fakes
// the *outcome* of a call that already ran for real — e.g. making a
Expand Down Expand Up @@ -120,6 +155,15 @@ inline T injectValue(T orig, T faulty, u64 threshold, const char* fn) {
#define INJECT_FAULT_BOOL_HIGH(v) \
::faultinj::injectValue((v), false, ::faultinj::PROB_HIGH, __func__)

#define INJECT_CRASH_RARE() \
::faultinj::injectCrash(::faultinj::PROB_RARE, __func__)
#define INJECT_CRASH_UNLIKELY() \
::faultinj::injectCrash(::faultinj::PROB_UNLIKELY, __func__)
#define INJECT_CRASH_LIKELY() \
::faultinj::injectCrash(::faultinj::PROB_LIKELY, __func__)
#define INJECT_CRASH_HIGH() \
::faultinj::injectCrash(::faultinj::PROB_HIGH, __func__)

#else // __FAULT_INJECTION__ not defined — strict identity, zero cost.

#define INJECT_FAULT_ADDRESS_RARE(ptr) (ptr)
Expand All @@ -132,6 +176,14 @@ inline T injectValue(T orig, T faulty, u64 threshold, const char* fn) {
#define INJECT_FAULT_BOOL_LIKELY(v) (v)
#define INJECT_FAULT_BOOL_HIGH(v) (v)

// ((void)0) rather than nothing, so `INJECT_CRASH_LIKELY();` stays a
// well-formed expression statement in every context (e.g. as the sole body of
// an unbraced if/else) instead of collapsing to a stray semicolon.
#define INJECT_CRASH_RARE() ((void)0)
#define INJECT_CRASH_UNLIKELY() ((void)0)
#define INJECT_CRASH_LIKELY() ((void)0)
#define INJECT_CRASH_HIGH() ((void)0)

#define NO_INJECTION_ASSERT(a) (assert(a))

#endif // __FAULT_INJECTION__
Expand Down
Loading
Loading