Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8153133: Thread.dumpStack() can use StackWalker #6292

Closed
wants to merge 3 commits into from

Conversation

jaikiran
Copy link
Member

@jaikiran jaikiran commented Nov 8, 2021

Can I please get a review of this change which seeks to implement the enhancement noted in https://bugs.openjdk.java.net/browse/JDK-8153133?

The commit in this PR uses the StackWalker API to dump the stacktrace of the thread. A few things to note about this change:

  • Previously, the dumped stacktrace would be preceded by a line (from the Exception instance) which would state java.lang.Exception: Stack trace and the rest of the lines would be the stacktrace. The change in this PR does a small (unrelated) enhacement which now includes the thread name in the message. So something like MainThread Stack trace where MainThread is the name of the thread whose stacktrace is being dumped. The linked JBS doesn't mention this change as a necessity but I decided to include it in this PR since I've always missed the thread name in that dumped stacktrace and since we are now using the StackWalker API, the mention of java.lang.Exception: ... line in the thread dump will no longer make sense (unless we wanted backward compatibility)
  • The change doesn't use lambda to allow Thread.dumpStack() to be called (for debugging purposes) from places where lambda usage might be too early.
  • The change also includes a fallback to use the Exception.printStackTrace() to allow for calls to Thread.dumpStack() when StackWalker class itself is being initialized.

The PR also includes a new jtreg test which verifies this change. The test has 3 test actions - one without security manager, one with security manager and one with java.security.debug=access,stack to exercise the case where Thread.dumpStack() gets called during StackWalker class initialization.

The linked JBS issue notes that it would be good to use the StackWalker API even in the Thread.getStackTrace implementation. This PR however doesn't include that change because I wanted to tackle that separately since I think that would need to run some performance benchmarks to evaluate any performance changes. The current Thread.dumpStack method clearly states that it is meant for debugging purposes so I haven't run any kind of performance benchmarks on this change.


Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed

Issue

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/6292/head:pull/6292
$ git checkout pull/6292

Update a local copy of the PR:
$ git checkout pull/6292
$ git pull https://git.openjdk.java.net/jdk pull/6292/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 6292

View PR using the GUI difftool:
$ git pr show -t 6292

Using diff file

Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/6292.diff

@bridgekeeper
Copy link

bridgekeeper bot commented Nov 8, 2021

👋 Welcome back jpai! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk openjdk bot added the rfr Pull request is ready for review label Nov 8, 2021
@openjdk
Copy link

openjdk bot commented Nov 8, 2021

@jaikiran The following label will be automatically applied to this pull request:

  • core-libs

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.

@openjdk openjdk bot added the core-libs core-libs-dev@openjdk.org label Nov 8, 2021
@mlbridge
Copy link

mlbridge bot commented Nov 8, 2021

Webrevs

// the StackWalker itself, which means that the StackWalker isn't usable
// at this point in time. So we fallback to creating a Exception instance
// and printing its stacktrace
new Exception(Thread.currentThread().name + " Stack trace").printStackTrace();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recursive initialisation issue will require discussion to see if we can avoid StackWalker.getInstance return null (which I assume is masking the issue).

printStackTrace interacts with locking of the streams to avoid garbled output when many threads are printing to standard output output/error at the same time. If we change dumpStack to use StackWalker then it will need to do the same.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printStackTrace interacts with locking of the streams to avoid garbled output when many threads are printing to standard output output/error at the same time. If we change dumpStack to use StackWalker then it will need to do the same.

Indeed. I have updated the PR to use a lock while writing out to the System.err.
I had a look at the printStackTrace() implementation and it ends up locking the PrintStream (System.err) or PrintWriter for the duration of the entire stacktrace printing of each stacktrace element. The updated PR thus uses System.err as the lock to match that semantic.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recursive initialisation issue will require discussion to see if we can avoid StackWalker.getInstance return null (which I assume is masking the issue).

For a better context, here's the stacktrace of such a call to Thread.dumpStack during the class initialization of StackWalker:

Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.StackWalker.forEach(java.util.function.Consumer)" because the return value of "java.lang.StackWalker.getInstance()" is null
	at java.base/java.lang.Thread.dumpStack(Thread.java:1383)
	at java.base/java.security.AccessController.checkPermission(AccessController.java:1054)
	at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:411)
	at java.base/java.lang.reflect.AccessibleObject.checkPermission(AccessibleObject.java:91)
	at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
	at java.base/java.lang.Class$3.run(Class.java:3864)
	at java.base/java.lang.Class$3.run(Class.java:3862)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
	at java.base/java.lang.Class.getEnumConstantsShared(Class.java:3861)
	at java.base/java.lang.System$2.getEnumConstantsShared(System.java:2295)
	at java.base/java.util.EnumSet.getUniverse(EnumSet.java:408)
	at java.base/java.util.EnumSet.noneOf(EnumSet.java:111)
	at java.base/java.lang.StackWalker.<clinit>(StackWalker.java:291)

As you will notice, this call comes from the security (permission check) layer when StackWalker class is being clinited. The check for StackWalker.getInstance() being null, in the Thread.dumpStack() implementation is indeed almost a hackish way to identify this case where StackWalker's clinit is in progress (in the current thread). I can't think of a different way to handle this use case, so looking forward to any suggestions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated PR thus uses System.err as the lock to match that semantic.

The test case has also been updated to add a new test which invokes Thread.dumpStack() from multiple threads and verifies that the stacktrace written out isn't garbled. Running the test without the addition of the lock on System.err in the Thread.dumpStack implementation does indeed show the garbled output and causes the test failure (which is a good thing).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm uncomfortable with this change, does the change have any benefit?

Copy link
Member Author

@jaikiran jaikiran Nov 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm uncomfortable with this change, does the change have any benefit?

To me, the initial appeal of this change was to use a more natural API instead of creating an Exception instance and printing the stacktrace from it. Performance wise, I hadn't run any numbers. But having to fallback to using the Exception instance in this above case, that too by doing an almost hackish check, does indeed remove any kind of benefit to this change.

Like Mandy suggests I will go ahead and withdraw this PR and close the linked issue.

- Update test to verify that multiple threads invoking Thread.dumpStack() doesn't generate garbled output
@openjdk openjdk bot removed the rfr Pull request is ready for review label Nov 8, 2021
@openjdk openjdk bot added the rfr Pull request is ready for review label Nov 8, 2021
@mlchung
Copy link
Member

mlchung commented Nov 8, 2021

@jaikiran Thanks for making this change and identifying the recursive initialization issue. I also concur with Alan that replacing Thread::dumpStack implementation with StackWalker does not buy us much. It has to fallback to new Throwable().printStackTrace() in some cases. I think we should close this RFE as will not fix.

@jaikiran
Copy link
Member Author

jaikiran commented Nov 9, 2021

I think we should close this RFE as will not fix.

Will do.
Thank you Alan and Mandy for your time on reviewing this.

@jaikiran jaikiran closed this Nov 9, 2021
@jaikiran jaikiran deleted the 8153133 branch November 9, 2021 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core-libs core-libs-dev@openjdk.org rfr Pull request is ready for review
Development

Successfully merging this pull request may close these issues.

3 participants