Skip to content

Conversation

roberttoyonaga
Copy link
Collaborator

@roberttoyonaga roberttoyonaga commented Jun 16, 2022

Added support for JFR jdk.JavaMonitorEnter events. These events provide information on when threads get blocked trying to acquire a lock.

@roberttoyonaga roberttoyonaga marked this pull request as draft June 16, 2022 19:25
@graalvmbot
Copy link
Collaborator

  • Hello Robert Toyonaga, thanks for contributing a PR to our project!

We use the Oracle Contributor Agreement to make the copyright of contributions clear. We don't have a record of you having signed this yet, based on your email address rtoyonag -(at)- rtoyonag -(dot)- remote -(dot)- csb. You can sign it at that link.

If you think you've already signed it, please comment below and we'll check.

  • Hello Robert Toyonaga, thanks for contributing a PR to our project!

We use the Oracle Contributor Agreement to make the copyright of contributions clear. We don't have a record of you having signed this yet, based on your email address 35396843+roberttoyonaga -(at)- users -(dot)- noreply -(dot)- github -(dot)- com. You can sign it at that link.

If you think you've already signed it, please comment below and we'll check.

@oraluben
Copy link
Contributor

within substrateVM it is not as easy to obtain addresses of Java objects.

Have you tried org.graalvm.compiler.word.Word#objectToTrackedPointer?

@roberttoyonaga roberttoyonaga changed the title [WIP] Add support for JFR monitor enter events Add support for JFR monitor enter events Jun 17, 2022
@roberttoyonaga roberttoyonaga marked this pull request as ready for review June 17, 2022 17:37
@adinn adinn requested a review from christianhaeubl June 17, 2022 21:26
@adinn
Copy link
Collaborator

adinn commented Jun 17, 2022

@christianhaeubl Could you please review this PR or suggest someone who could do so? Thanks!

@@ -0,0 +1,48 @@
package src.com.oracle.svm.core.src.com.oracle.svm.core.jfr.events;
Copy link
Member

Choose a reason for hiding this comment

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

Please add a copyright header.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done!

public class JavaMonitorEnterEvent {

@Uninterruptible(reason = "Accesses a JFR buffer.")
public static void emit(Object obj, org.graalvm.nativeimage.IsolateThread previousOwner,long startTicks) {
Copy link
Member

Choose a reason for hiding this comment

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

Please run mx checkstyle and use the eclipse formatter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done!

@@ -255,8 +257,27 @@ private static void slowPathMonitorEnter(Object obj) {
@RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED)
@Override
public void monitorEnter(Object obj) {
long startTicks = com.oracle.svm.core.jfr.JfrTicks.elapsedTicks();
Copy link
Member

Choose a reason for hiding this comment

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

HotSpot only emits the event if there is a contention, i.e., no JFR event is emitted for the fast path. We need something similar.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've now implemented this behavior by only emitting events if tryLock() fails to get the lock

Copy link
Member

Choose a reason for hiding this comment

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

This new approach doesn't work:

  1. You only fill monitorOwners when a lock contention is happening. So, you will usually keep track of an outdated value and not of the latest previous owner. One possible solution would be to introduce a custom ReentrantLock subclass that keeps track of the previous owner in a field. Writing the JFR thread id to that field should be cheap enough so that you can do it in the fast path.
  2. There is no guarantee that your call to lockObject.lock(); really runs into a contention. So, you will emit unnecessary JFR events from time to time. To solve that, you will probably need to add a method that is similar to lock() that returns if it was necessary to park the thread because of a contention.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes I see! Thank you Christian. I have used the solution you suggested for 1.

For 2, wouldn't tryLock() returning false indicate that there is contention? Is the problem that the lock may be released in between the call to tryLock() and lock()? I think the Hotspot code makes one attempt to acquire the lock (+1 optional spin) and upon failure treats it as contention.

Copy link
Member

Choose a reason for hiding this comment

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

ad 2. you are right, I thought that HotSpot is more strict. So, the current solution is good enough.

JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class);
JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data);

if (emit0(data, obj, previousOwner, startTicks, SubstrateJVM.get().isLarge(JfrEvent.JavaMonitorEnter)) == com.oracle.svm.core.jfr.JfrEventWriteStatus.RetryLarge) {
Copy link
Member

Choose a reason for hiding this comment

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

There shouldn't be any need for the retry mechanism as this event has a fixed size (it only contains primitive data). Search the code base for JfrNativeEventWriter.beginSmallEvent(...) to find examples where we emit JFR events that have a fixed size.

Copy link
Member

Choose a reason for hiding this comment

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

Please also use your Red Hat email address for all commits. Otherwise, we can't merge the changes to master easily.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ok I should be using my redhat email now. I got rid of the retry.

@graalvmbot
Copy link
Collaborator

Hello Robert Toyonaga, thanks for contributing a PR to our project!

We use the Oracle Contributor Agreement to make the copyright of contributions clear. We don't have a record of you having signed this yet, based on your email address rtoyonag -(at)- redhat -(dot)- com. You can sign it at that link.

If you think you've already signed it, please comment below and we'll check.

@graalvmbot
Copy link
Collaborator

Robert Toyonaga has signed the Oracle Contributor Agreement (based on email address rtoyonag -(at)- redhat -(dot)- com) so can contribute to this repository.

@@ -255,8 +257,27 @@ private static void slowPathMonitorEnter(Object obj) {
@RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED)
@Override
public void monitorEnter(Object obj) {
long startTicks = com.oracle.svm.core.jfr.JfrTicks.elapsedTicks();
Copy link
Member

Choose a reason for hiding this comment

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

ad 2. you are right, I thought that HotSpot is more strict. So, the current solution is good enough.

import org.graalvm.nativeimage.CurrentIsolate;
import com.oracle.svm.core.jfr.SubstrateJVM;

public class OwnedReentrantLock extends ReentrantLock {
Copy link
Member

Choose a reason for hiding this comment

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

JavaMonitor might be a better name.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

renamed!

currOwnerTid = SubstrateJVM.get().getThreadId(CurrentIsolate.getCurrentThread());
}

public boolean tryLockOwnedLock() {
Copy link
Member

Choose a reason for hiding this comment

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

You could rename this method to enter or monitorenter.
If you move the call to JavaMonitorEnterEvent.emit(...); into this method as well, then it should be sufficient to store the latest owner in a single field.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

renamed and moved call to emit event

@@ -192,8 +193,10 @@ public class MultiThreadedMonitorSupport extends MonitorSupport {
* Secondary storage for monitor slots. Synchronized to prevent concurrent access and
* modification.
*/
private final Map<Object, ReentrantLock> additionalMonitors = new WeakIdentityHashMap<>();
private final Map<Object, OwnedReentrantLock> additionalMonitors = new WeakIdentityHashMap<>();
private final Map<Object, Long> monitorOwners = new WeakIdentityHashMap<>();
Copy link
Member

Choose a reason for hiding this comment

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

The fields monitorOwners and monitorOwnersLock are unused.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed!

Copy link
Member

@christianhaeubl christianhaeubl left a comment

Choose a reason for hiding this comment

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

Thanks, I will try to merge this change in the next couple days.

@graalvmbot graalvmbot merged commit 3e5753c into oracle:master Jun 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants