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

Add Throwable capturing fields support for JDK16+ #7047

Merged
merged 2 commits into from
May 21, 2024

Conversation

jpbempel
Copy link
Member

@jpbempel jpbempel commented May 17, 2024

What Does This Do

We introduce in WellKnownClasses the mechanism to fetch fields through dedicated call to getter for registered classes.
In the special case of Throwable we are allowing any inherited classes to capture the Throwable fields as well as custom fields. Though, to capture Throwable fields (like detailMessage) we need to call getter that could be overridden. Hence ,we are checking before calling them that they are not overridden by checking the declaring class of the getter.
Add option in build to not allow reflection access for debugger tests to assess that we can access correctly even for recent JDKs

Motivation

Since JDK16 it's not possible to extract values by reflection for JDK modules without adding --add-opens=java.base/java.lang=ALL-UNNAMED so when capturing an exception (like in @exception) we are not allowed to capture fields like detailMessage, cause, stacktrace which can be valuable to troubleshoot.

Additional Notes

Results:
Screenshot 2024-05-17 at 14 00 48

Screenshot 2024-05-17 at 14 01 23

Jira ticket: DEBUG-2389

Since JDK16 it's not possible to extract values by reflection for JDK
modules without adding `--add-opens=java.base/java.lang=ALL-UNNAMED`
so when capturing an exception (like in @exception) we are not allowed
to capture fields like detailMessage, cause, stacktrace which can be
valuable to troubleshoot.
We introduce in WellKnownClasses the mechanism to fetch fields through
dedicated call to getter for registered classes.
In the special case of Throwable we are allowing any inherited classes
to capture the Throwable fields as well as custom fields. Though to
capture Throwable fields (like detailMessage) we need to call getter
that could be overridden. Hence we are checking before calling them
that they are not overridden by checking the declaring class of the
getter.
Add option in build to not allow reflection access for debugger tests
to assess that we can access correctly even for recent JDKs
@jpbempel jpbempel requested review from a team as code owners May 17, 2024 13:27
@jpbempel jpbempel requested review from cimi, dougqh and nayeem-kamal and removed request for a team May 17, 2024 13:27
@jpbempel jpbempel added comp: debugger Dynamic Instrumentation type: enhancement labels May 17, 2024
Copy link
Contributor

@shatzi shatzi left a comment

Choose a reason for hiding this comment

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

can we please add tests to verify that all this works?

if (specialTypeAccess != null) {
return specialTypeAccess;
}
if (value instanceof Throwable) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would recommend add support here for Optional with a single member and reduce some code. they doing the same logic with if type == X and member == Y return the value.

If this is the case we can just live specialTypeAccess logic.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

public static final String BECAUSE_OVERRIDDEN =
"Special access method not safe to be called because overridden";

public static CapturedContext.CapturedValue detailMessage(Object o) {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe refactor this.

private static captureValueIfNotOverridden(Object o, string method, string field, string type, callback callback) {
   if (isOverridden(o, method, Throwable.class)) {
        return CapturedContext.CapturedValue.notCapturedReason(field, type, BECAUSE_OVERRIDDEN);
      }
      return CapturedContext.CapturedValue.of(field, type, callback());
    }
}

then every function can be a single line:

captureValueIfNotOverridden(o, "getMessage", "detailMessage", String.class.getTypeName(), () => ((Throwable) o).getMessage());

Copy link
Member Author

Choose a reason for hiding this comment

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

done. a little more complex than what you suggest :)

refactor captureifOverridden
@jpbempel jpbempel merged commit dedce3e into master May 21, 2024
82 of 85 checks passed
@jpbempel jpbempel deleted the jpbempel/access-exception-msg branch May 21, 2024 13:25
@github-actions github-actions bot added this to the 1.35.0 milestone May 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants