Skip to content

Instantly share code, notes, and snippets.

@sav007
Last active April 21, 2021 17:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sav007/d5b703556ee96034503f1c923a612f4d to your computer and use it in GitHub Desktop.
Save sav007/d5b703556ee96034503f1c923a612f4d to your computer and use it in GitHub Desktop.
Track native module queue perf
override fun onCreate() {
reactNativeHost.reactInstanceManager.addReactInstanceEventListener {
val nativeModulesMessageQueueThread = ReactContext::class.java.getDeclaredField("mNativeModulesMessageQueueThread")
.apply { isAccessible = true }
.get(it) as MessageQueueThreadImpl
val messageHandlerField = MessageQueueThreadImpl::class.java.getDeclaredField("mHandler")
.apply { isAccessible = true }
val prevMessageHandler = messageHandlerField.get(nativeModulesMessageQueueThread) as MessageQueueThreadHandler
val exceptionHandler = MessageQueueThreadHandler::class.java.getDeclaredField("mExceptionHandler")
.apply { isAccessible = true }
.get(prevMessageHandler) as QueueThreadExceptionHandler
val newMessageHandler = PerfMessageQueueThreadHandler(
looper = prevMessageHandler.looper,
exceptionHandler = exceptionHandler
)
messageHandlerField.set(nativeModulesMessageQueueThread, newMessageHandler)
SystraceMessage::class.java.getDeclaredField("NOOP_BUILDER")
.apply { isAccessible = true }
.set(null, SystraceMessenger(newMessageHandler))
}
}
private fun startBugsnag() {
val bugsnagConfig = Configuration.load(this)
bugsnagConfig.enabledReleaseStages = setOf("release", "development", "snapshot")
bugsnagConfig.appVersion = AppContext(this).getBuildIdentifier()
bugsnagConfig.releaseStage = AppContext(this).getBuildFlavour()
Bugsnag.start(this, bugsnagConfig)
}
override fun startDistributionService() = Unit
protected open fun initializeFlipper(
context: Context,
reactInstanceManager: ReactInstanceManager
) = Unit
}
private class PerfMessageQueueThreadHandler(
looper: Looper,
exceptionHandler: QueueThreadExceptionHandler
) : MessageQueueThreadHandler(looper, exceptionHandler) {
var currentMessageStartTime = -1L
private var totalMessageCount = 0L
private var averageQueueTime = 0L
override fun dispatchMessage(msg: Message) {
val startTime = msg.obj as? Long
if (startTime != null) {
val elapsedTime = (System.currentTimeMillis() - startTime)
if (elapsedTime > 0) {
totalMessageCount++
averageQueueTime += (elapsedTime - averageQueueTime) / totalMessageCount
if (totalMessageCount % 10 == 0L) {
// println("!!! Average native message queue time: $averageQueueTime")
totalMessageCount = 0
averageQueueTime = 0
}
}
currentMessageStartTime = startTime
} else {
currentMessageStartTime = -1L
}
super.dispatchMessage(msg)
}
override fun sendMessageAtTime(msg: Message, uptimeMillis: Long): Boolean {
return super.sendMessageAtTime(msg.apply {
this.obj = System.currentTimeMillis()
}, uptimeMillis)
}
}
private class SystraceMessenger(val perfMessageQueueThreadHandler: PerfMessageQueueThreadHandler) : SystraceMessage.Builder() {
override fun flush() {
}
override fun arg(key: String?, value: Any?): SystraceMessage.Builder = apply {
if (key == "method" && value != null) {
if (value.toString().startsWith("CheckoutSDKModule.") && perfMessageQueueThreadHandler.currentMessageStartTime != -1L) {
val waitTime = System.currentTimeMillis() - perfMessageQueueThreadHandler.currentMessageStartTime
perfMessageQueueThreadHandler.currentMessageStartTime = -1L
println("!!! $value: wait time in queue: $waitTime ms")
} else {
println("!!! other native call: " + value)
}
}
}
override fun arg(key: String?, value: Int): SystraceMessage.Builder = apply {
}
override fun arg(key: String?, value: Long): SystraceMessage.Builder = apply {
}
override fun arg(key: String?, value: Double): SystraceMessage.Builder = apply {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment