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

7439: Introduced IConstantPoolExtension for Constant Pools #333

Closed
wants to merge 5 commits into from

Conversation

jpbempel
Copy link
Member

@jpbempel jpbempel commented Oct 29, 2021

Add extension methods when constants are read, referenced, resolved, constant pools fully resolved and parsing finished to be able for example:

  • track constant pool usage inside events
  • replace or translate symbols, method names, etc... like
    de-obfuscations of stack traces

Progress

  • Commit message must refer to an issue
  • Change must be properly reviewed

Issue

  • JMC-7439: Introduced IConstantPoolExtension for Constant Pools

Reviewers

Reviewing

Using git

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

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

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 333

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

Using diff file

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

@bridgekeeper
Copy link

bridgekeeper bot commented Oct 29, 2021

👋 Welcome back jpbempel! 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 label Oct 29, 2021
@mlbridge
Copy link

mlbridge bot commented Oct 29, 2021

Webrevs

Add extension methods when constants are read, referenced, resolved
and constant pools fully resolved to be able for example:

 * track constant pool usage inside events
 * replace or translate symbols, method names, etc... like
de-obfuscations of stack traces
@jpbempel jpbempel changed the title 7439: Add methods in IParserExtension for Constant Pools 7439: Introduced IConstantPoolExtension for Constant Pools Oct 29, 2021
*
* @return an instance implementing IConstantPoolExtension
*/
default IConstantPoolExtension createConstantPoolExtension() {
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps we might as well make the other methods default too?

	default String getValueInterpretation(String eventTypeId, String fieldId) {
		return null;
	}
	default IEventSinkFactory getEventSinkFactory(IEventSinkFactory subFactory) {
		return subFactory;
	}

@docwarems
Copy link

Hi, I wanted to test this PR but are still unsure with Eclipse setup/usage. I managed to check out the PR in my fork (what a GitHub Desktop magic :-) ). The project builds and I can run JMC.
For testing the PR I wanted to start with the ConstantPoolExtensionTest Unittest and debug it. It's the first time I tried to run a JMC unittest fro Eclipse. As used to from IntelliJ IDE I tried "run as unittest" from the class context menu. This created a JUnit launch config.
But the launcher fails with a basic ClassNotFoundException for this this test class. So obviously Eclipse failed to automatically create a working launch config.

Can you help me with this basic setup question? And sorry for beeing OT here. I can later delete this message again.

@thegreystone
Copy link
Member

Normally I just have separate clones and workspaces for various people that contribute, e.g.:

Directory of C:\Users\Marcus\git\jmcreviews
2021-10-30 16:26

.
2021-10-30 16:26 ..
2021-09-14 10:52 cimi
2020-11-13 22:19 guru
2021-03-17 22:51 jpbempel
2021-01-12 17:31 miro
2020-12-07 23:57 Suchitainf
and so on... (Yep, I'm on my Windows machine right now.)

I then copy my eclipse workspace and simply remove all the projects and re-import them, so that I have separate Eclipse workspaces for various people that I need to review, and separate clones to try their various PRs.

For running the tests:
cd core
mvn verify

To run the test from within Eclipse, I simply right clicked on the class and selected Run as JUnit Test:
image

This gave me:
image

@docwarems
Copy link

To run the test from within Eclipse, I simply right clicked on the class and selected Run as JUnit Test:
image

So it's just as simple as expected and I as I did too. But I get
grafik

This is how the automatically generated Launch Config looks like

grafik

So I will skip the test...

@docwarems
Copy link

docwarems commented Oct 30, 2021

Rather than starting with the unit test, I added the MyConstantPoolExtension from to test to the FlightRecordingLoader.loadStream() (only for testing of course). I added breakpoints to all methods of the MyConstantPoolExtension class and loaded a recording (recorded from our company developed app). All methods were executed a lot times as expected. I expected to see my own class or packages names sooner or later, but I only saw JDK internal names. So I probably didn't got the idea. Can you help?

I pushed the change to my fork FYI.
docwarems@c9669a7

@thegreystone
Copy link
Member

thegreystone commented Oct 31, 2021

I would avoid cherry picking changes to other forks when reviewing. Your commit in your fork shows one changed file with 80 additions and 1 deletion. This PR has 8 changed files.

It is much easier to simply clone JPs fork, and switch to the branch with his changes.

@docwarems
Copy link

I haven't CPed anything. Look at my commit history - it contains the changes from jpbempel - even if I did it differently than you propsed.
But OK, I just spent a another 2hrs to set up a another Eclipse project with jpbempels fork. The usual problems with the setup - I'll had to repeat project importing and platform reloading a lot of times until all dependencies where available. And Maven internal error from Eclipse all the time. It's a pain.
I'll continue tests within the next days.

@jpbempel
Copy link
Member Author

jpbempel commented Nov 9, 2021

FYI, I am reworking the PR to match better with my needs and the dependencies

- getId
- eventsLoaded
- getItemCollection to expose data gathered

and exposed in IParserStats all ConstantPool extensions
@docwarems
Copy link

Back to the tests - this time with the JPs clone and branch "pool-extension". I realized that in my Eclipse setup the ConstantPoolExtensionTest class doesn't get compiled unless I do a

  • cd core
  • mvn package
    on console. That's why I obiously got the ClassNotFoundException previously. I realized that when I finally found the "Show command line" option with the class path.
    Now I can run the test but have to do "mvn package" always if I want to make changes, which is obviously very clumsy.
    Sorry that I report more about my Eclipse setup problems here than regarding the PR...

Regarding the PR, I have the same test result as last time. I just can't seenany of my own classes in the Parsewr Extension callbacks when I replaced the test JFR file with my own. As I said, I probably didn't got the idea.

I would suggest that you provide a test JFR file with a custom class like fr.jpbempel.HelloWorld and demonstrate in the test how to get it.

@jpbempel
Copy link
Member Author

hello @docwarems
My test is focusing on event Types that referencing constants.

when using the callback:

Object constantRead(long constantIndex, Object constant, String eventTypeId) {
	return constant;
}

you should look at constant argument that should be what you would like to de-obfuscate and return a translated value instead of the original one:

Object constantRead(long constantIndex, Object constant, String eventTypeId) {
	return deobfuscate(constant);
}


import org.openjdk.jmc.common.collection.FastAccessNumberMap;
import org.openjdk.jmc.common.item.IAttribute;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.unit.ContentType;
Copy link
Member

Choose a reason for hiding this comment

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

Not needed, right?

@@ -39,6 +39,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
Copy link
Member

Choose a reason for hiding this comment

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

Using Maps, not Sets - not needed.

@docwarems
Copy link

My test is focusing on event Types that referencing constants.

Yeah, that's more or less what I expected. Maybe I'm unable to find my constant of interest in the mass of other classes. Could you provide a breakpoint condition for the breakpoint condition shown below which would stop at a class "fr.jpbempel.HelloWorld"?

grafik

@jpbempel
Copy link
Member Author

jpbempel commented Nov 21, 2021

Hey, @docwarems

I have created a custom event:

package com.bempel.events;

import jdk.jfr.Event;
import jdk.jfr.Label;
import jdk.jfr.Name;

@Name("com.bempel.events.MyEvent")
@Label("MyEvent")
public class MyEvent extends Event {
	@Label("message")
	public String message;
}

I have used like this:

MyEvent myEvent = new MyEvent();
myEvent.message = "showVetList " + uuid;
myEvent.commit();

I have recording with this event in it:

Screenshot 2021-11-21 at 22 31 13

You can look at constant pools :

in java.lang.Class
Screenshot 2021-11-21 at 22 32 25

but also in jdk.Types.Symbol:
Screenshot 2021-11-21 at 22 32 58

in debugging ConstantPoolExtensionTest:
Screenshot 2021-11-21 at 22 34 38

But it's a little bit complex because:
a class constant is not having directly the name of the class but reference another symbol constant for that
see:
https://github.com/openjdk/jmc/blob/master/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/internal/parser/v1/StructTypes.java#L277-L287

That's why it may have more sense to look for the symbol constant instead of directly the class constant.

@docwarems
Copy link

Hello @jpbempel, the breakpoint condition was the breakthrough - I just wasn't able to catch the desired constant before. Even with the simplest HelloWork console program I ended up with a JFR file where I wasn't able to step through all constants in a reasonable time and find my desired constant.

Next I should be able to take my deobfuscator remapping code and build a true extension. I'll come back then. Regarding the PR review, I wonder if you want to wait for my further work, or if you prefer to merge.

@jpbempel
Copy link
Member Author

It would be nice to have your feedback on this, because I came back a lot on the design to implement my needs. So depending on yours, we may revisit some parts...

@thegreystone
Copy link
Member

thegreystone commented Nov 26, 2021

One last nit for this PR - can you please check the files for unnecessary imports? I'll add an issue for ensuring that this is done automatically.

@docwarems
Copy link

@jpbempel I will not find enough time to continue before my holidays starting in mid December. It's better than one hour this day, one hour another day.

@openjdk
Copy link

openjdk bot commented Nov 29, 2021

@jpbempel This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

7439: Introduced IConstantPoolExtension for Constant Pools

Reviewed-by: hirt

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 24 new commits pushed to the master branch:

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added the ready label Nov 29, 2021
@thegreystone
Copy link
Member

thegreystone commented Dec 1, 2021

Hi @jpbempel - you can now integrate this PR. :)

@jpbempel
Copy link
Member Author

jpbempel commented Dec 1, 2021

/integrate

@openjdk
Copy link

openjdk bot commented Dec 1, 2021

Going to push as commit d4449ce.
Since your change was applied there have been 25 commits pushed to the master branch:

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot closed this Dec 1, 2021
@openjdk openjdk bot added integrated and removed ready rfr labels Dec 1, 2021
@openjdk
Copy link

openjdk bot commented Dec 1, 2021

@jpbempel Pushed as commit d4449ce.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

@docwarems
Copy link

The PR is merged, but I've resumed my tests and are in the middle of adapting my de-obfuscation code to the new extension mechanism.
Question: How would you load such an extension in real life? By means of a plugin JAR? There are a lot of plugin JARs in the plugin folder of the build JMC, but I couldn't find a starting point for development. Could you point me to such a resource?

@docwarems
Copy link

@jpbempel as assumed doing the deobfuscation using the IConstantPoolExtension is much more complicated than as I did it previously because it modifies internal state. I'm facing follow-up exceptions and have been trying for a while to sort them out. But I am clueless now.
I overwrote constantRead() and look for constants with instanceof String. Then I look up these Strings in my obfuscation mapping and if I get a hit, I replace the strings or the relevant substring with the deobfuscated string and return it. But this seem to create wrong internal state. I'm logging to console original and replaced strings and what Is see look fine to me according to my understanding.

Do you have any more hint? Otherwise I would push my current code to my fork and post the link here. If you can help I will continue, otherwise I'll have to stay with my existing solution doing the deobfuscation on formatting level, which is just fine for our purpose.

@docwarems
Copy link

docwarems commented Dec 20, 2021

@jpbempel in this branch https://github.com/docwarems/jmc/tree/PR-333-Deobfuscate you'll find a demonstration of my current problem. The README describes how to run the demo. I will/can not further investigate unless you come with a hint what I'm doing wrong.

@jpbempel
Copy link
Member Author

is your stacktrace for the error similar to:

java.lang.StringIndexOutOfBoundsException: String index out of range: -11
        at java.base/java.lang.String.substring(String.java:1841)
        at org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes$JfrJavaClass.convertNames(StructTypes.java:331)
        at org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes$JfrJavaClass.getFullName(StructTypes.java:315)
        at org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes$JfrJavaClass.hashCode(StructTypes.java:341)
        at java.base/java.util.Objects.hashCode(Objects.java:116)
        at org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes$JfrMethod.hashCode(StructTypes.java:625)
        at org.openjdk.jmc.flightrecorder.stacktrace.graph.AggregatableFrame.hashCode(AggregatableFrame.java:90)
        at java.base/java.util.HashMap.hash(HashMap.java:339)
        at java.base/java.util.HashMap.get(HashMap.java:552)

?
in that case it's because you have changed the class name but not the package name associated.
Then when referencing a class or a method, package name & class name are not the same, which is not expected.

in your example to fix the issue:

    private static Deobfuscator createFooDeobfuscator() {
    	Map<String, String> obfuscatorClassMap = new HashMap<String, String>();  // obfuscated class -> class
    	obfuscatorClassMap.put("se/hirt/jmc/tutorial/hotmethods/HotMethods", "de/docware/ms/foo/Foo");
		Map<String, String> obfuscatorPackageMap = new HashMap<String, String>();  // obfuscated package -> package
+++             obfuscatorPackageMap.put("se/hirt/jmc/tutorial/hotmethods", "de/docware/ms/foo");
		Map<String, Map<String, String>> obfuscatorClassMethodsMap = new HashMap<String, Map<String, String>>();  // (class, obfuscated method) -> method
    	return new Deobfuscator(obfuscatorClassMap, obfuscatorPackageMap, obfuscatorClassMethodsMap);
    }

@docwarems
Copy link

@jpbempel A happy new to year to you and everyone of the JMC team :-)

Yes, that's very obvious, once I replaced the package too, the error was gone and I could see the replacement effect inside JMC.
I should now be able to complete my code. I will provide a new PR for review in a while.

Thanks a lot!

@docwarems
Copy link

docwarems commented Jan 2, 2022

Back to my real deobfuscation mapping, I found that of course I DID handle the package remapping there, but using real Proguard obfuscation mapping the situation is more complex (or real) than the demo I provided.

When I debugged one of these StringIndexOutOfBoundsExceptions, I have this situation in StructTypes.convertNames()

name=de.docware.apps.etk.base.db.cache.EtkDbsCacheElems, getPackageName()=de.docware.apps.etk.base.db.AbstractRevisionChangeSet

And these are the relevant entries from the Proguard mapping file.

de.docware.apps.etk.base.db.cache.EtkDbsCacheElems -> de.docware.apps.etk.base.d.a.e:
de.docware.apps.etk.base.db.AbstractRevisionChangeSet -> de.docware.apps.etk.base.d.a:

So "de.docware.apps.etk.base.d.a" refers both to a class and a package name.

When in constantRead() the String constant ist de-obfuscated to a clear name I wonder if it's possible to tell whether the constant refers to a class or a package name. At least I don't see a difference in the debugger. The first map which gets a hit returns the deobfuscated string, which in my code is the class map. That's why getPackageName() delivers the class name rather than the package.

Is there a possibility in constantRead() to tell what kind of object (class, package, ...) the constant refers to?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

3 participants