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

Fix hermes profiler #34129

Closed

Conversation

janicduplessis
Copy link
Contributor

@janicduplessis janicduplessis commented Jul 5, 2022

Summary

The hermes profiler doesn't work currently, I tracked down the problem to a couple of things.

  • Need to call registerForProfiling to enable profiling for a specific runtime. I added the call at the same place where we enable the debugger.
  • runInExecutor didn't work and call its callback. Not sure exactly why, but using executor_->add like we do in a lot of other places to run code on the executor works.
  • GetHeapUsageRequest seems to cause some deadlocks. JS contexts were not detected reliably, I suspect this is related to deadlocks when trying to run on inspector executor. GetHeapUsageRequest doesn't actually need any data from the inspector so there is no need to run it on that queue. To fix it I moved the call to use runInExecutor instead.

Changelog

[General] [Fixed] - Fix hermes profiler

Test Plan

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Contributor A React Native contributor. labels Jul 5, 2022
@facebook-github-bot facebook-github-bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 5, 2022
@analysis-bot
Copy link

analysis-bot commented Jul 5, 2022

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 7,831,006 -1,430
android hermes armeabi-v7a 7,217,268 -1,371
android hermes x86 8,139,775 -1,654
android hermes x86_64 8,121,728 -1,451
android jsc arm64-v8a 9,696,493 -807
android jsc armeabi-v7a 8,451,913 -661
android jsc x86 9,647,125 -886
android jsc x86_64 10,244,951 -876

Base commit: 22a067b
Branch: main

@analysis-bot
Copy link

analysis-bot commented Jul 5, 2022

Platform Engine Arch Size (bytes) Diff
ios - universal n/a --

Base commit: 303aaf8
Branch: main

Copy link
Contributor

@cortinico cortinico left a comment

Choose a reason for hiding this comment

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

Thanks for sending this over @janicduplessis 👌 I'd like to get a pass from @neildhar @jpporto

resp->id = req.id;

inspector_
->executeIfEnabled(
Copy link
Contributor

Choose a reason for hiding this comment

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

executeIfEnabled() ensures that the JS VM is not running when making calls to it -- it therefore serializes accesses to the VM. I am not sure it is safe to make this change. @neildhar, any thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think in this case we want it to run if the js vm is running since this command gets called periodically by devtools to get the current memory usage.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had a quick look and it seems fine to call from any thread since this info comes from the GC and uses a lock.

Copy link
Contributor

Choose a reason for hiding this comment

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

getHeapUsage() is only synchronized with the GC, not with the VM execution. Therefore it is not safe to be invoked with the VM running. You could use onPause/onResume to determine whether to invoke getHeapUsage(), or whether return a cached value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it would be nice to be able to run getHeapUsage while the VM is running, it is used to display realtime memory usage in chrome devtools, so requiring that the VM is paused kind of defeats the purpose of this.

image

Is there any info coming from the VM that needs synchronization? Also can anything bad happen if we execute this on a running VM, except from potential wrong values, which I think is fine in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could also just disable this for now if we're not comfortable landing this change. This feature is not very important compared to be able to profile.

@@ -221,6 +221,12 @@ std::unique_ptr<JSExecutor> HermesExecutorFactory::createJSExecutor(
decoratedRuntime, delegate, jsQueue, timeoutInvoker_, runtimeInstaller_);
}

::hermes::vm::RuntimeConfig HermesExecutorFactory::defaultRuntimeConfig() {
return ::hermes::vm::RuntimeConfig::Builder()
.withEnableSampleProfiling(true)
Copy link
Contributor

Choose a reason for hiding this comment

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

This enables the sampling profiler for all RN users by default. I don't know RN enough, but how is the HermesExecutorFactory create? You need to pipe the options to that call, and not set it here by default...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this would enable it by default, which I think is reasonable. Do you know if there’s any overhead in enabling it? Should we do it only for debug builds?

It would still be possible to change by passing a hermes config when creating the HermesExecutorFactory. This is done in the user’s AppDelegate.mm for iOS (https://github.com/facebook/react-native/blob/main/packages/rn-tester/RNTester/AppDelegate.mm#L195) and Android also supports passing in a JavaScriptExecutor factory (

public ReactInstanceManagerBuilder setJavaScriptExecutorFactory(
), although the Java binding doesn't seem to expose any options except heap size (https://github.com/facebook/react-native/blob/8bd3edec88148d0ab1f225d2119435681fbbba33/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/RuntimeConfig.java). The android implementation also seems to enable sample profiler by default in a way that's not really configurable ( ).

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem is that the profiler machinery is thread-affined. To be honest we tried to change this several times but we couldn't find a solution :(

I understand your frustration -- we REALLY want to enable usage of the sampling profiler more broadly, but enabling it by default for all users could lead to usability issues -- incorrect stacks, runtime crashes etc.

I think at this point I'll defer to the RN folks on what they want to do. @cortinico?

Copy link
Contributor Author

@janicduplessis janicduplessis Jul 6, 2022

Choose a reason for hiding this comment

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

Would it be possible to enable the profiler only when devtools are connected? I know chrome devtools send Profiler.enable event when connected. Can registerForProfiling / unregisterForProfiling be called on a running VM?

Copy link
Contributor

Choose a reason for hiding this comment

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

registerForProfiling/unregisterForProfiling must be invoked in the thread running JS (i.e., the "VM" thread), so the VM will be stopped by necessity.

Is there a way to enable this for iOS in a similar manner than it is currently done for Android?

Copy link
Contributor

Choose a reason for hiding this comment

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

With that being said, someone from RN should have the final say here as to how they want this done. @cortinico

I think I'm sadly not the right person to make this call.

I understand your frustration -- we REALLY want to enable usage of the sampling profiler more broadly, but enabling it by default for all users could lead to usability issues -- incorrect stacks, runtime crashes etc.

My 2 cents here, I won't enable sampling profiler for all the users, but I'll wrap it around a boolean guard that users can control. I understand the value of this, but I think the set of people that will actually use a sampling profiler are a niche.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From what I can tell, the sampling profiler has been enabled for all users before, and still is for android. It is no longer enabled by default since a recent change in hermes.

I haven't tried to see if it works, but would a better solution be to enable profiler when devtools connect?

Copy link
Contributor

Choose a reason for hiding this comment

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

The current sampling profiler API requires (un)registerForProfiling() to be called from the thread running the VM, so running it from the inspector thread is wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I imagine it would be possible to run that code on the JS VM thread, not sure if some other methods already do that.

Copy link
Contributor

Choose a reason for hiding this comment

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

What's the consensus here. I would like to drive this forward and try to merge it. @jpporto I believe this is fine on RN's end. Are we fine merging it?

@facebook-github-bot
Copy link
Contributor

@philIip has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@kelset kelset added the Debugger Issues related to React Native DevTools and the New Debugger label Jul 22, 2022
@cortinico
Copy link
Contributor

I had a discussion with @jpporto. This is safe to land. We're a bit afraid this might lead to crashes internally due to how we call EnableSampleProfiling. If that is the case, we might have to revert this.

@janicduplessis
Copy link
Contributor Author

Sounds good, I’m fine with exploring other solutions, or trying to enable it only for open source if it causes an issue internally.

@facebook-github-bot
Copy link
Contributor

@philIip has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@react-native-bot
Copy link
Collaborator

This pull request was successfully merged by @janicduplessis in 81564c1.

When will my fix make it into a release? | Upcoming Releases

@react-native-bot react-native-bot added the Merged This PR has been merged. label Aug 31, 2022
@janicduplessis janicduplessis deleted the @janic/fix-inspector branch August 31, 2022 12:57
dmytrorykun pushed a commit that referenced this pull request Sep 14, 2022
Summary:
The hermes profiler doesn't work currently, I tracked down the problem to a couple of things.

- Need to call `registerForProfiling` to enable profiling for a specific runtime. I added the call at the same place where we enable the debugger.
- `runInExecutor` didn't work and call its callback. Not sure exactly why, but using `executor_->add` like we do in a lot of other places to run code on the executor works.
- `GetHeapUsageRequest` seems to cause some deadlocks. JS contexts were not detected reliably, I suspect this is related to deadlocks when trying to run on inspector executor. `GetHeapUsageRequest` doesn't actually need any data from the inspector so there is no need to run it on that queue. To fix it I moved the call to use `runInExecutor` instead.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[General] [Fixed] - Fix hermes profiler

Pull Request resolved: #34129

Reviewed By: cortinico

Differential Revision: D37669469

Pulled By: philIip

fbshipit-source-id: 6cf3b2857ac60b0a1518837b9c56b9c093ed222f
Titozzz pushed a commit that referenced this pull request Sep 26, 2022
Summary:
The hermes profiler doesn't work currently, I tracked down the problem to a couple of things.

- Need to call `registerForProfiling` to enable profiling for a specific runtime. I added the call at the same place where we enable the debugger.
- `runInExecutor` didn't work and call its callback. Not sure exactly why, but using `executor_->add` like we do in a lot of other places to run code on the executor works.
- `GetHeapUsageRequest` seems to cause some deadlocks. JS contexts were not detected reliably, I suspect this is related to deadlocks when trying to run on inspector executor. `GetHeapUsageRequest` doesn't actually need any data from the inspector so there is no need to run it on that queue. To fix it I moved the call to use `runInExecutor` instead.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[General] [Fixed] - Fix hermes profiler

Pull Request resolved: #34129

Reviewed By: cortinico

Differential Revision: D37669469

Pulled By: philIip

fbshipit-source-id: 6cf3b2857ac60b0a1518837b9c56b9c093ed222f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Contributor A React Native contributor. Debugger Issues related to React Native DevTools and the New Debugger Merged This PR has been merged. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants