Native UI scan

Summary: Added scheduler to scan the Native UI every 500 ms to test, Also added instrumentation in a separate event with the timings of each stage visualised in a Data table on desktop which can be accessed with ctrl+I. Currently this instrumentation event is sent every time but it could be a config option controlled from the desktop in the future

Reviewed By: lblasa

Differential Revision: D39205313

fbshipit-source-id: ca034171db6b062396b4ef28028aaa663c4d852a
This commit is contained in:
Luke De Feo
2022-09-07 04:37:17 -07:00
committed by Facebook GitHub Bot
parent a5da6923eb
commit 41068d1c90
9 changed files with 273 additions and 99 deletions

View File

@@ -8,22 +8,23 @@
package com.facebook.flipper.plugins.uidebugger
import android.app.Application
import android.util.Log
import com.facebook.flipper.core.FlipperConnection
import com.facebook.flipper.core.FlipperPlugin
import com.facebook.flipper.plugins.uidebugger.core.ApplicationInspector
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
import com.facebook.flipper.plugins.uidebugger.core.ConnectionRef
import com.facebook.flipper.plugins.uidebugger.core.Context
import com.facebook.flipper.plugins.uidebugger.core.NativeScanScheduler
import com.facebook.flipper.plugins.uidebugger.model.InitEvent
import com.facebook.flipper.plugins.uidebugger.model.NativeScanEvent
import com.facebook.flipper.plugins.uidebugger.scheduler.Scheduler
import kotlinx.serialization.json.Json
val LogTag = "FlipperUIDebugger"
class UIDebuggerFlipperPlugin(val application: Application) : FlipperPlugin {
private val context: Context = Context(ApplicationRef(application))
private var connection: FlipperConnection? = null
private val context: Context = Context(ApplicationRef(application), ConnectionRef(null))
private val nativeScanScheduler = Scheduler(NativeScanScheduler(context))
override fun getId(): String {
return "ui-debugger"
@@ -31,33 +32,26 @@ class UIDebuggerFlipperPlugin(val application: Application) : FlipperPlugin {
@Throws(Exception::class)
override fun onConnect(connection: FlipperConnection) {
this.connection = connection
// temp solution, get from descriptor
val inspector = ApplicationInspector(context)
this.context.connectionRef.connection = connection
val rootDescriptor =
inspector.descriptorRegister.descriptorForClassUnsafe(context.applicationRef.javaClass)
context.descriptorRegister.descriptorForClassUnsafe(context.applicationRef.javaClass)
connection.send(
InitEvent.name,
Json.encodeToString(
InitEvent.serializer(), InitEvent(rootDescriptor.getId(context.applicationRef))))
try {
val nodes = inspector.traversal.traverse()
connection.send(
NativeScanEvent.name,
Json.encodeToString(NativeScanEvent.serializer(), NativeScanEvent(nodes)))
} catch (e: java.lang.Exception) {
Log.e(LogTag, e.message.toString(), e)
}
nativeScanScheduler.start()
}
@Throws(Exception::class)
override fun onDisconnect() {
this.connection = null
this.context.connectionRef.connection = null
this.nativeScanScheduler.stop()
}
override fun runInBackground(): Boolean {
return true
return false
}
}

View File

@@ -7,4 +7,13 @@
package com.facebook.flipper.plugins.uidebugger.core
class Context(val applicationRef: ApplicationRef) {}
import com.facebook.flipper.core.FlipperConnection
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
data class Context(
val applicationRef: ApplicationRef,
val connectionRef: ConnectionRef,
val descriptorRegister: DescriptorRegister = DescriptorRegister.withDefaults()
)
data class ConnectionRef(var connection: FlipperConnection?)

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.flipper.plugins.uidebugger.core
import android.os.Looper
import android.util.Log
import com.facebook.flipper.plugins.uidebugger.model.NativeScanEvent
import com.facebook.flipper.plugins.uidebugger.model.Node
import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent
import com.facebook.flipper.plugins.uidebugger.scheduler.Scheduler
import kotlinx.serialization.json.Json
data class ScanResult(
val txId: Long,
val scanStart: Long,
val scanEnd: Long,
val nodes: List<Node>
)
class NativeScanScheduler(val context: Context) : Scheduler.Task<ScanResult> {
val traversal = LayoutTraversal(context.descriptorRegister, context.applicationRef)
var txId = 0L
override fun execute(): ScanResult {
val start = System.currentTimeMillis()
val nodes = traversal.traverse()
val scanEnd = System.currentTimeMillis()
Log.d(
"LAYOUT_SCHEDULER",
Thread.currentThread().name +
Looper.myLooper() +
", produced: " +
{
nodes.count()
} +
" nodes")
return ScanResult(txId++, start, scanEnd, nodes)
}
override fun process(result: ScanResult) {
val serialized =
Json.encodeToString(
NativeScanEvent.serializer(), NativeScanEvent(result.txId, result.nodes))
val serializationEnd = System.currentTimeMillis()
context.connectionRef.connection?.send(
NativeScanEvent.name,
serialized,
)
val socketEnd = System.currentTimeMillis()
context.connectionRef.connection?.send(
PerfStatsEvent.name,
Json.encodeToString(
PerfStatsEvent.serializer(),
PerfStatsEvent(
result.txId,
result.scanStart,
result.scanEnd,
serializationEnd,
socketEnd,
result.nodes.size)))
}
}

View File

@@ -15,8 +15,23 @@ data class InitEvent(val rootId: String) {
}
@kotlinx.serialization.Serializable
data class NativeScanEvent(val nodes: List<Node>) {
data class NativeScanEvent(val txId: Long, val nodes: List<Node>) {
companion object {
const val name = "nativeScan"
}
}
/** Separate optional performance statistics event */
@kotlinx.serialization.Serializable
data class PerfStatsEvent(
val txId: Long,
val start: Long,
val scanComplete: Long,
val serializationComplete: Long,
val socketComplete: Long,
val nodesCount: Int
) {
companion object {
const val name = "perfStats"
}
}