Skip to content

fix: add ThreadLocal reentrancy guard to SpyImpl to prevent recursive advice dispatch#3161

Open
daguimu wants to merge 2 commits intoalibaba:masterfrom
daguimu:fix/spyimpl-reentrance-3083
Open

fix: add ThreadLocal reentrancy guard to SpyImpl to prevent recursive advice dispatch#3161
daguimu wants to merge 2 commits intoalibaba:masterfrom
daguimu:fix/spyimpl-reentrance-3083

Conversation

@daguimu
Copy link
Contributor

@daguimu daguimu commented Mar 24, 2026

Problem

When using watch to intercept a toString() method, Arthas's own output rendering code (ObjectView.renderObject()) calls toString() on the watched object to display results. This call re-triggers the advice listener on the same thread, causing infinite recursion.

Steps to reproduce:

watch com.example.MyClass toString '{target}' -n 5 -x 1

If MyClass.toString() is called by Arthas's rendering pipeline, it re-fires the watch, which renders again, which calls toString() again — infinite loop.

Root Cause

SpyImpl.atEnter/atExit/atExceptionExit had no guard against re-entrant calls on the same thread.

Fix

Add a ThreadLocal<Boolean> DISPATCHING guard to SpyImpl. Before dispatching advice listeners, check if we are already dispatching on the current thread. If so, return early to break the re-entrancy cycle.

The guard is reset in a finally block to ensure correctness even if an exception occurs during dispatch.

Impact

  • Fixes recursive advice dispatch for watch/trace/monitor/tt when intercepting methods called by Arthas internals
  • Zero overhead on non-recursive paths (single ThreadLocal boolean read)
  • Only atEnter, atExit, atExceptionExit are guarded; trace invoke methods are unaffected

Fixes #3083

@CLAassistant
Copy link

CLAassistant commented Mar 24, 2026

CLA assistant check
All committers have signed the CLA.

@daguimu daguimu force-pushed the fix/spyimpl-reentrance-3083 branch 2 times, most recently from bfe1ef7 to ff7e2b8 Compare March 24, 2026 20:11
… advice dispatch

When watching a toString() method, Arthas's own ObjectView.renderObject()
calls toString() on the watched object to display results. This triggers
the advice listener again on the same thread, causing infinite recursion.

Fix by adding a ThreadLocal<Boolean> DISPATCHING guard to SpyImpl.
When already dispatching advice on the current thread, re-entrant calls
from the same thread are skipped, preventing recursion.

Applies guard to atEnter(), atExit(), and atExceptionExit().
Zero overhead on non-recursive paths (single ThreadLocal read).

Fixes alibaba#3083
@daguimu daguimu force-pushed the fix/spyimpl-reentrance-3083 branch from ff7e2b8 to b552f81 Compare March 24, 2026 20:18
…thod coverage

- Add debug logging when reentrancy is detected for easier troubleshooting
- Extend DISPATCHING guard to trace invoke methods (atBeforeInvoke, atAfterInvoke,
  atInvokeException) which have the same recursive dispatch risk

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

watch 业务类toString()方法概率也会watch到arthas自身ObjectView中renderObject的toString方法,造成递归

2 participants