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

Enable debugging on Reanimated's runtime using Chrome DevTools #3526

Merged
merged 84 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
5e28bd2
Preparation work
Kwasow Jul 21, 2022
3f8215b
Fixed prepareHermes
Kwasow Jul 21, 2022
51bfb8f
Building hermes-inspector from source
Kwasow Jul 21, 2022
ff3fbaa
Replaced libhermes-inspector dependency with libhermes-executor
Kwasow Jul 22, 2022
320b039
Reverted change in xcode project
Kwasow Jul 25, 2022
dd57719
Added some source maps to wokrlets
Kwasow Jul 26, 2022
2729412
Removed old hermes initialization code
Kwasow Jul 28, 2022
9e9cbbf
IT STOPPED 🛑
Kwasow Aug 11, 2022
db298ca
Cleanup on Android, some changes on iOS
Kwasow Aug 11, 2022
571b65a
Merged main
Kwasow Aug 11, 2022
fb096be
Removed hermes debugger from release builds
Kwasow Aug 11, 2022
efed1e8
M
Kwasow Aug 17, 2022
24754c8
Moved closure prepending to transform
Kwasow Aug 17, 2022
ce0b2e2
Removed useless code
Kwasow Aug 18, 2022
acf077a
Fixed off by one line mapping issues. Optimized release bundle
Kwasow Aug 18, 2022
29491b2
Removed redundant console.log
Kwasow Aug 19, 2022
e579f72
Fixed release builds on Android (cmake part only)
Kwasow Aug 19, 2022
4adca84
Added some support for hermes debugging on iOS
Kwasow Aug 22, 2022
d288c66
Fixed app reloads on iOS
Kwasow Aug 23, 2022
bcc5d84
Partially fixed release builds on Android
Kwasow Aug 23, 2022
aa0cf3c
Added error handling on iOS
Kwasow Aug 23, 2022
55306a2
Fixed header search paths on iOS
Kwasow Aug 24, 2022
c116722
Added support for older React Native versions
Kwasow Aug 24, 2022
a530416
Prevented crashes on reload on Android (but still doesn't work)
Kwasow Aug 24, 2022
403384f
Merged main
Kwasow Aug 29, 2022
6bb62ac
Updated plugin tests
Kwasow Aug 29, 2022
6c33122
Fixed reloads on Android
Kwasow Aug 29, 2022
e4b9116
Moved hermes debugger logic to Common folder
Kwasow Aug 30, 2022
bec2b88
Fixed release builds on Android
Kwasow Aug 30, 2022
b45cc81
Code cleanup
Kwasow Aug 30, 2022
1e6a9f9
Fixing iOS
Kwasow Aug 30, 2022
63ce88a
Added important comment
Kwasow Aug 31, 2022
3219a5a
Added missing code on iOS
Kwasow Aug 31, 2022
b2c0061
Minor code improvements
Kwasow Aug 31, 2022
c3588cf
Added support for Flipper in babel plugin
Kwasow Sep 1, 2022
35853fd
Fixed cpp lint
Kwasow Sep 1, 2022
1303b2e
Fixed java lint
Kwasow Sep 1, 2022
740a334
Fixed babel plugin tests
Kwasow Sep 1, 2022
70b4af5
Fixed Example app build
Kwasow Sep 1, 2022
12ed55e
Fixed example apps build
Kwasow Sep 1, 2022
bd995a4
This PoC allows for a reload, but crashes on interaction with debugge…
Kwasow Sep 2, 2022
87aed5e
Removed patch hack
Kwasow Sep 5, 2022
ca68a49
Merge main
Kwasow Sep 12, 2022
78d0a31
Improved runtime initialization on iOS
Kwasow Sep 12, 2022
94ab7d4
Fixed Android CI
Kwasow Sep 12, 2022
1957ccc
Added patch from facebook/react-native#34489
Kwasow Sep 12, 2022
69dbbeb
Simplified implementation
Kwasow Sep 13, 2022
9fc0aa7
Updated initizalization on Android
Kwasow Sep 13, 2022
db7b40f
Removed unnecessary patch
Kwasow Sep 13, 2022
88dfdc9
Added patch for metro-inspector-proxy
Kwasow Sep 13, 2022
7618832
Fixed some of the tvOS build issues
Kwasow Sep 13, 2022
e5c72ab
Fixed Android build
Kwasow Sep 13, 2022
916e786
Added patch for react-native-gesture-handler
Kwasow Sep 13, 2022
58e9e7b
Fixed reloads while debugger connected
Kwasow Sep 13, 2022
5ba13ce
Fixed tvOS build
Kwasow Sep 14, 2022
c57bf23
save
Kwasow Sep 14, 2022
4751025
Fixed Android build
Kwasow Sep 14, 2022
2e98fdd
Fixed tvOS build and improved implementation
Kwasow Sep 14, 2022
86be060
Reverted yarn.lock, which was commited by mistake
Kwasow Sep 14, 2022
f72b23a
Improvements after self-review
Kwasow Sep 14, 2022
a73a124
More improvements and explanations
Kwasow Sep 14, 2022
98af8dc
save
Kwasow Sep 15, 2022
105b7ca
Added documentation to code
Kwasow Sep 15, 2022
c890ccc
Improved comments
Kwasow Sep 15, 2022
95b10dc
Fixed JSC on iOS
Kwasow Sep 15, 2022
98840bb
Fixed incorrect enabling of hermes debugger
Kwasow Sep 15, 2022
c2d5eef
Fixed build issues
Kwasow Sep 15, 2022
8c99701
Added patches to example app
Kwasow Sep 16, 2022
bf706af
Added patch-package to example app
Kwasow Sep 16, 2022
cfd0458
Fixed patch-package warnings
Kwasow Sep 16, 2022
ccd0c38
Enabled flipper in Example app
Kwasow Sep 16, 2022
ba6438c
We want it like this
Kwasow Sep 19, 2022
176ccc0
Removed tickleJS
Kwasow Sep 20, 2022
25a740a
Updated known issues
Kwasow Sep 20, 2022
628813f
Added explainatory comments:
Kwasow Sep 20, 2022
2fcf0fd
Removed unnecessary reference
Kwasow Sep 20, 2022
9e50fa4
Improvements after review (part 1)
Kwasow Sep 20, 2022
1d68efe
Improvements after review (part 2)
Kwasow Sep 21, 2022
df8498a
Improvements after review (part 3)
Kwasow Sep 21, 2022
fa4f39d
Improvements after review (part 4)
Kwasow Sep 21, 2022
873678f
Merged main
Kwasow Sep 21, 2022
c7cc522
Final updates
Kwasow Sep 21, 2022
fb92063
Improvements after review
Kwasow Sep 21, 2022
77ead21
Final changes
Kwasow Sep 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions Common/cpp/ReanimatedRuntime/ReanimatedHermesRuntime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "ReanimatedHermesRuntime.h"
Kwasow marked this conversation as resolved.
Show resolved Hide resolved

// Only include this file in Hermes-enabled builds as some platforms (like tvOS)
// don't support hermes and it causes the compilation to fail.
#if JS_RUNTIME_HERMES

#include <cxxreact/MessageQueueThread.h>
#include <jsi/decorator.h>
#include <jsi/jsi.h>

#include <memory>
#include <utility>

#if __has_include(<reacthermes/HermesExecutorFactory.h>)
#include <reacthermes/HermesExecutorFactory.h>
#else // __has_include(<hermes/hermes.h>) || ANDROID
#include <hermes/hermes.h>
#endif

#include <hermes/inspector/RuntimeAdapter.h>
#include <hermes/inspector/chrome/Registration.h>

namespace reanimated {

using namespace facebook;
using namespace react;

#if HERMES_ENABLE_DEBUGGER

class HermesExecutorRuntimeAdapter
: public facebook::hermes::inspector::RuntimeAdapter {
public:
HermesExecutorRuntimeAdapter(
facebook::hermes::HermesRuntime &hermesRuntime,
std::shared_ptr<MessageQueueThread> thread)
: hermesRuntime_(hermesRuntime), thread_(std::move(thread)) {}

virtual ~HermesExecutorRuntimeAdapter() {
// This is required by iOS, because there is an assertion in the destructor
// that the thread was indeed `quit` before
thread_->quitSynchronous();
}

facebook::jsi::Runtime &getRuntime() override {
return hermesRuntime_;
}

facebook::hermes::debugger::Debugger &getDebugger() override {
return hermesRuntime_.getDebugger();
}

// This is not empty in the original implementation, but we decided to tickle
// the runtime by running a small piece of code on every frame as using this
// required us to hold a refernce to the runtime inside this adapter which
// caused issues while reloading the app.
void tickleJs() override {}
Kwasow marked this conversation as resolved.
Show resolved Hide resolved

public:
facebook::hermes::HermesRuntime &hermesRuntime_;
std::shared_ptr<MessageQueueThread> thread_;
};

#endif // HERMES_ENABLE_DEBUGGER

ReanimatedHermesRuntime::ReanimatedHermesRuntime(
std::unique_ptr<facebook::hermes::HermesRuntime> runtime,
std::shared_ptr<MessageQueueThread> jsQueue)
: jsi::WithRuntimeDecorator<ReanimatedReentrancyCheck>(
*runtime,
reentrancyCheck_),
runtime_(std::move(runtime)) {
#if HERMES_ENABLE_DEBUGGER
auto adapter =
std::make_unique<HermesExecutorRuntimeAdapter>(*runtime_, jsQueue);
facebook::hermes::inspector::chrome::enableDebugging(
std::move(adapter), "Reanimated Runtime");
#else
// This is required by iOS, because there is an assertion in the destructor
// that the thread was indeed `quit` before
jsQueue->quitSynchronous();
#endif
}

ReanimatedHermesRuntime::~ReanimatedHermesRuntime() {
#if HERMES_ENABLE_DEBUGGER
// We have to disable debugging before the runtime is destroyed.
facebook::hermes::inspector::chrome::disableDebugging(*runtime_);
#endif
}

} // namespace reanimated

#endif // JS_RUNTIME_HERMES
125 changes: 125 additions & 0 deletions Common/cpp/ReanimatedRuntime/ReanimatedHermesRuntime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#pragma once

// JS_RUNTIME_HERMES is only set on Android so we have to check __has_include
// on iOS.
#if __APPLE__ && \
(__has_include( \
<reacthermes/HermesExecutorFactory.h>) || __has_include(<hermes/hermes.h>))
#define JS_RUNTIME_HERMES 1
#endif

// Only include this file in Hermes-enabled builds as some platforms (like tvOS)
// don't support hermes and it causes the compilation to fail.
#if JS_RUNTIME_HERMES

#include <cxxreact/MessageQueueThread.h>
#include <jsi/decorator.h>
#include <jsi/jsi.h>

#include <memory>
#include <thread>

#if __has_include(<reacthermes/HermesExecutorFactory.h>)
#include <reacthermes/HermesExecutorFactory.h>
#else // __has_include(<hermes/hermes.h>) || ANDROID
#include <hermes/hermes.h>
#endif

namespace reanimated {

using namespace facebook;
using namespace react;

// ReentrancyCheck is copied from React Native
// from ReactCommon/hermes/executor/HermesExecutorFactory.cpp
// https://github.com/facebook/react-native/blob/main/ReactCommon/hermes/executor/HermesExecutorFactory.cpp
struct ReanimatedReentrancyCheck {
// This is effectively a very subtle and complex assert, so only
// include it in builds which would include asserts.
#ifndef NDEBUG
Kwasow marked this conversation as resolved.
Show resolved Hide resolved
ReanimatedReentrancyCheck() : tid(std::thread::id()), depth(0) {}

void before() {
std::thread::id this_id = std::this_thread::get_id();
std::thread::id expected = std::thread::id();

// A note on memory ordering: the main purpose of these checks is
// to observe a before/before race, without an intervening after.
// This will be detected by the compare_exchange_strong atomicity
// properties, regardless of memory order.
//
// For everything else, it is easiest to think of 'depth' as a
// proxy for any access made inside the VM. If access to depth
// are reordered incorrectly, the same could be true of any other
// operation made by the VM. In fact, using acquire/release
// memory ordering could create barriers which mask a programmer
// error. So, we use relaxed memory order, to avoid masking
// actual ordering errors. Although, in practice, ordering errors
// of this sort would be surprising, because the decorator would
// need to call after() without before().

if (tid.compare_exchange_strong(
expected, this_id, std::memory_order_relaxed)) {
// Returns true if tid and expected were the same. If they
// were, then the stored tid referred to no thread, and we
// atomically saved this thread's tid. Now increment depth.
assert(depth == 0 && "No thread id, but depth != 0");
++depth;
} else if (expected == this_id) {
// If the stored tid referred to a thread, expected was set to
// that value. If that value is this thread's tid, that's ok,
// just increment depth again.
assert(depth != 0 && "Thread id was set, but depth == 0");
++depth;
} else {
// The stored tid was some other thread. This indicates a bad
// programmer error, where VM methods were called on two
// different threads unsafely. Fail fast (and hard) so the
// crash can be analyzed.
__builtin_trap();
}
}

void after() {
assert(
tid.load(std::memory_order_relaxed) == std::this_thread::get_id() &&
"No thread id in after()");
if (--depth == 0) {
// If we decremented depth to zero, store no-thread into tid.
std::thread::id expected = std::this_thread::get_id();
bool didWrite = tid.compare_exchange_strong(
expected, std::thread::id(), std::memory_order_relaxed);
assert(didWrite && "Decremented to zero, but no tid write");
}
}

std::atomic<std::thread::id> tid;
// This is not atomic, as it is only written or read from the owning
// thread.
unsigned int depth;
#endif // NDEBUG
};

// This is in fact a subclass of jsi::Runtime! WithRuntimeDecorator is a
// template class that is a subclass of DecoratedRuntime which is also a
// template class that then inherits its template, which in this case is
// jsi::Runtime. So the inheritance is: ReanimatedHermesRuntime ->
// WithRuntimeDecorator -> DecoratedRuntime -> jsi::Runtime You can find out
// more about this in ReactCommon/jsi/jsi/Decorator.h or by following this link:
// https://github.com/facebook/react-native/blob/main/ReactCommon/jsi/jsi/decorator.h
class ReanimatedHermesRuntime
: public jsi::WithRuntimeDecorator<ReanimatedReentrancyCheck> {
public:
ReanimatedHermesRuntime(
std::unique_ptr<facebook::hermes::HermesRuntime> runtime,
std::shared_ptr<MessageQueueThread> jsQueue);
~ReanimatedHermesRuntime();

private:
std::shared_ptr<facebook::hermes::HermesRuntime> runtime_;
ReanimatedReentrancyCheck reentrancyCheck_;
};

} // namespace reanimated

#endif // JS_RUNTIME_HERMES
50 changes: 50 additions & 0 deletions Common/cpp/ReanimatedRuntime/ReanimatedRuntime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "ReanimatedRuntime.h"

#include <cxxreact/MessageQueueThread.h>
#include <jsi/jsi.h>

#include <memory>
#include <utility>

#if JS_RUNTIME_HERMES
#include "ReanimatedHermesRuntime.h"
#elif JS_RUNTIME_V8
#include <v8runtime/V8RuntimeFactory.h>
#else
#include <jsi/JSCRuntime.h>
Kwasow marked this conversation as resolved.
Show resolved Hide resolved
#endif

namespace reanimated {

using namespace facebook;
using namespace react;

std::shared_ptr<jsi::Runtime> ReanimatedRuntime::make(
std::shared_ptr<MessageQueueThread> jsQueue) {
#if JS_RUNTIME_HERMES
std::unique_ptr<facebook::hermes::HermesRuntime> runtime =
facebook::hermes::makeHermesRuntime();

// We don't call `jsQueue->quitSynchronous()` here, since it will be done
// later in ReanimatedHermesRuntime

return std::make_shared<ReanimatedHermesRuntime>(std::move(runtime), jsQueue);
#elif JS_RUNTIME_V8
// This is required by iOS, because there is an assertion in the destructor
// that the thread was indeed `quit` before.
jsQueue->quitSynchronous();

auto config = std::make_unique<rnv8::V8RuntimeConfig>();
config->enableInspector = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

fwiw, if you want to enable debugging on v8, you could also set this flag as true.

config->appName = "reanimated";
return rnv8::createSharedV8Runtime(runtime_, std::move(config));
#else
Kwasow marked this conversation as resolved.
Show resolved Hide resolved
// This is required by iOS, because there is an assertion in the destructor
// that the thread was indeed `quit` before
jsQueue->quitSynchronous();
Kwasow marked this conversation as resolved.
Show resolved Hide resolved

return facebook::jsc::makeJSCRuntime();
#endif
}

} // namespace reanimated
27 changes: 27 additions & 0 deletions Common/cpp/ReanimatedRuntime/ReanimatedRuntime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

// JS_RUNTIME_HERMES is only set on Android so we have to check __has_include
// on iOS.
#if __APPLE__ && \
(__has_include( \
<reacthermes/HermesExecutorFactory.h>) || __has_include(<hermes/hermes.h>))
#define JS_RUNTIME_HERMES 1
#endif

#include <cxxreact/MessageQueueThread.h>
#include <jsi/jsi.h>

#include <memory>

namespace reanimated {

using namespace facebook;
using namespace react;

class ReanimatedRuntime {
Kwasow marked this conversation as resolved.
Show resolved Hide resolved
public:
static std::shared_ptr<jsi::Runtime> make(
std::shared_ptr<MessageQueueThread> jsQueue);
};

} // namespace reanimated
Loading