@@ -243,13 +243,19 @@ class ScopedJvmtiMock {
243243 jvmtiEnv* _orig;
244244};
245245
246- // (d) Value-injection path: PROF-15395 fixed Profiler::checkState() (shared by
247- // start()/check(), and therefore also reached by the -agentpath auto-start
248- // path) to fail cleanly instead of crashing later when libgcc_s.so.1 can't be
249- // loaded. libgcc_s.so.1 is always present in this test environment, so
250- // INJECT_FAULT_BOOL_LIKELY on prewarmUnwinder()'s return value is what makes
251- // that failure path reachable here: the real dlopen() still runs and
252- // succeeds, but the caller is deterministically told it failed.
246+ // (d) Value-injection path: Profiler::checkState() (shared by start()/check(),
247+ // and therefore also reached by the -agentpath auto-start path) fails cleanly
248+ // instead of crashing later when libgcc_s.so.1 can't be loaded. libgcc_s.so.1
249+ // is always present in this test environment, so INJECT_FAULT_BOOL_LIKELY on
250+ // prewarmUnwinder()'s return value is what makes that failure path reachable
251+ // here: the real dlopen() still runs and succeeds, but the caller is
252+ // deterministically told it failed.
253+ //
254+ // checkState() only treats that failure as fatal off musl (see its comment:
255+ // musl never hits the pthread_exit path that needs libgcc_s.so.1), so on
256+ // musl an injected failure falls through to the mocked
257+ // JVMSupport::initialize() failure instead of surfacing "Missing
258+ // libgcc_s.so.1" -- expect whichever outcome the current libc implies.
253259TEST_F (FaultInjectionTest, CheckStateSurfacesInjectedPrewarmUnwinderFailure) {
254260#ifdef __linux__
255261 Profiler* p = Profiler::instance ();
@@ -259,6 +265,7 @@ TEST_F(FaultInjectionTest, CheckStateSurfacesInjectedPrewarmUnwinderFailure) {
259265 ProfilerTestAccessor::setState (p, NEW );
260266 ProfiledThread::current ()->setFiRng (0x5EED5EED5EED5EEDULL );
261267
268+ const bool prewarmFailureIsFatal = !OS::isMusl ();
262269 bool sawInjectedFailure = false ;
263270 bool sawNonInjectedPrewarm = false ;
264271 // shouldFire() mixes the fixed RNG seed above with an ASLR-dependent
@@ -267,23 +274,50 @@ TEST_F(FaultInjectionTest, CheckStateSurfacesInjectedPrewarmUnwinderFailure) {
267274 // non-injected call is observed. Keep iterating (and un-latching the ERROR
268275 // state that every outcome here leaves behind) until both have been seen.
269276 for (int i = 0 ; i < 5000 && !(sawInjectedFailure && sawNonInjectedPrewarm); i++) {
277+ // shouldFire() increments FAULTS_INJECTED exactly once whenever it fires,
278+ // and the only fault-injection site reachable from checkState() is the
279+ // INJECT_FAULT_BOOL_LIKELY around prewarmUnwinder()'s dlopen() call --
280+ // JVMSupport::initialize() below is a plain mock, not an injection site.
281+ // So the counter delta across one checkState() call, not the returned
282+ // error message, is the ground truth for whether this iteration actually
283+ // took the injected path; that lets the two outcomes be set from
284+ // independent evidence instead of both being inferred from one string
285+ // compare.
286+ long long faultsBefore = Counters::getCounter (FAULTS_INJECTED );
270287 Error error = p->checkState ();
288+ bool injectedThisCall = Counters::getCounter (FAULTS_INJECTED ) > faultsBefore;
271289 ASSERT_TRUE ((bool )error) << " checkState() must fail here: either the "
272290 " injected prewarmUnwinder() failure or the "
273291 " mocked JVMSupport::initialize() failure" ;
274- if (std::strcmp (error.message (), " Missing libgcc_s.so.1" ) == 0 ) {
275- sawInjectedFailure = true ;
292+ if (prewarmFailureIsFatal) {
293+ if (injectedThisCall) {
294+ EXPECT_STREQ (" Missing libgcc_s.so.1" , error.message ());
295+ sawInjectedFailure = true ;
296+ } else {
297+ // prewarmUnwinder() succeeded (non-injected, ~99% of calls) and fell
298+ // through to the mocked JVMSupport::initialize() failure instead.
299+ EXPECT_STREQ (" Profiler encountered fatal error" , error.message ());
300+ sawNonInjectedPrewarm = true ;
301+ }
276302 } else {
277- // prewarmUnwinder() succeeded (non-injected, ~99% of calls) and fell
278- // through to the mocked JVMSupport::initialize() failure instead.
303+ // On musl, checkState() doesn't treat a prewarmUnwinder() failure as
304+ // fatal, so both an injected and a non-injected call fall through to
305+ // the same mocked JVMSupport::initialize() failure message -- only the
306+ // counter delta distinguishes them.
279307 EXPECT_STREQ (" Profiler encountered fatal error" , error.message ());
280- sawNonInjectedPrewarm = true ;
308+ if (injectedThisCall) {
309+ sawInjectedFailure = true ;
310+ } else {
311+ sawNonInjectedPrewarm = true ;
312+ }
281313 }
282314 ProfilerTestAccessor::setState (p, NEW );
283315 }
284316
285- EXPECT_TRUE (sawInjectedFailure)
286- << " expected at least one injected prewarmUnwinder() failure within 5000 tries" ;
317+ if (prewarmFailureIsFatal) {
318+ EXPECT_TRUE (sawInjectedFailure)
319+ << " expected at least one injected prewarmUnwinder() failure within 5000 tries" ;
320+ }
287321 EXPECT_TRUE (sawNonInjectedPrewarm)
288322 << " expected at least one non-injected prewarmUnwinder() success within 5000 tries" ;
289323#endif // __linux__
0 commit comments