Add message when traversal has error

Summary: By sending a message to the desktop we can report to log view and inform the user what happened

Reviewed By: lblasa

Differential Revision: D50369853

fbshipit-source-id: b4852d736232477261bfdf6f94c9395ce29cceaf
This commit is contained in:
Luke De Feo
2023-10-19 03:33:42 -07:00
committed by Facebook GitHub Bot
parent da903b8bc5
commit ee7f12ef85
7 changed files with 62 additions and 25 deletions

View File

@@ -13,10 +13,12 @@ import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent
import com.facebook.flipper.plugins.uidebugger.model.FrameworkEventMetadata import com.facebook.flipper.plugins.uidebugger.model.FrameworkEventMetadata
import com.facebook.flipper.plugins.uidebugger.model.TraversalError
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverManager import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverManager
import com.facebook.flipper.plugins.uidebugger.scheduler.SharedThrottle import com.facebook.flipper.plugins.uidebugger.scheduler.SharedThrottle
import com.facebook.flipper.plugins.uidebugger.traversal.PartialLayoutTraversal import com.facebook.flipper.plugins.uidebugger.traversal.PartialLayoutTraversal
import kotlinx.serialization.json.Json
interface ConnectionListener { interface ConnectionListener {
fun onConnect() fun onConnect()
@@ -34,8 +36,7 @@ class UIDContext(
private val pendingFrameworkEvents: MutableList<FrameworkEvent> private val pendingFrameworkEvents: MutableList<FrameworkEvent>
) { ) {
val layoutTraversal: PartialLayoutTraversal = val layoutTraversal: PartialLayoutTraversal = PartialLayoutTraversal(this)
PartialLayoutTraversal(descriptorRegister, observerFactory)
val treeObserverManager = TreeObserverManager(this) val treeObserverManager = TreeObserverManager(this)
val sharedThrottle: SharedThrottle = SharedThrottle() val sharedThrottle: SharedThrottle = SharedThrottle()
@@ -45,6 +46,11 @@ class UIDContext(
synchronized(pendingFrameworkEvents) { pendingFrameworkEvents.add(frameworkEvent) } synchronized(pendingFrameworkEvents) { pendingFrameworkEvents.add(frameworkEvent) }
} }
fun onError(traversalError: TraversalError) {
connectionRef.connection?.send(
TraversalError.name, Json.encodeToString(TraversalError.serializer(), traversalError))
}
fun extractPendingFrameworkEvents(): List<FrameworkEvent> { fun extractPendingFrameworkEvents(): List<FrameworkEvent> {
synchronized(pendingFrameworkEvents) { synchronized(pendingFrameworkEvents) {
val copy = pendingFrameworkEvents.toList() val copy = pendingFrameworkEvents.toList()

View File

@@ -10,21 +10,21 @@ package com.facebook.flipper.plugins.uidebugger.model
import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.Id
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class InitEvent(val rootId: Id, val frameworkEventMetadata: List<FrameworkEventMetadata>) { class InitEvent(val rootId: Id, val frameworkEventMetadata: List<FrameworkEventMetadata>) {
companion object { companion object {
const val name = "init" const val name = "init"
} }
} }
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class MetadataUpdateEvent(val attributeMetadata: Map<MetadataId, Metadata> = emptyMap()) { class MetadataUpdateEvent(val attributeMetadata: Map<MetadataId, Metadata> = emptyMap()) {
companion object { companion object {
const val name = "metadataUpdate" const val name = "metadataUpdate"
} }
} }
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class FrameScanEvent( class FrameScanEvent(
val frameTime: Long, val frameTime: Long,
val nodes: List<Node>, val nodes: List<Node>,
val snapshot: Snapshot?, val snapshot: Snapshot?,
@@ -35,11 +35,23 @@ data class FrameScanEvent(
} }
} }
@kotlinx.serialization.Serializable data class Snapshot(val nodeId: Id, val data: String) @kotlinx.serialization.Serializable
class TraversalError(
val nodeName: String,
val errorType: String,
val errorMessage: String,
val stack: String
) {
companion object {
const val name = "traversalError"
}
}
@kotlinx.serialization.Serializable class Snapshot(val nodeId: Id, val data: String)
/** Separate optional performance statistics event */ /** Separate optional performance statistics event */
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class PerfStatsEvent( class PerfStatsEvent(
val txId: Long, val txId: Long,
val observerType: String, val observerType: String,
val nodesCount: Int, val nodesCount: Int,

View File

@@ -10,13 +10,13 @@ package com.facebook.flipper.plugins.uidebugger.model
import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.Id
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class FrameworkEventMetadata( class FrameworkEventMetadata(
val type: String, val type: String,
val documentation: String, val documentation: String,
) )
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class FrameworkEvent( class FrameworkEvent(
val treeId: Id, val treeId: Id,
val nodeId: Id, val nodeId: Id,
val type: String, val type: String,

View File

@@ -17,7 +17,7 @@ typealias MetadataId = Int
* identity, attributes, layout, documentation, or a custom type. * identity, attributes, layout, documentation, or a custom type.
*/ */
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class Metadata( class Metadata(
val id: MetadataId, val id: MetadataId,
val type: String, val type: String,
val namespace: String, val namespace: String,

View File

@@ -22,7 +22,7 @@ data class Bounds(val x: Int, val y: Int, val width: Int, val height: Int) {
} }
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class SpaceBox(val top: Int, val right: Int, val bottom: Int, val left: Int) { class SpaceBox(val top: Int, val right: Int, val bottom: Int, val left: Int) {
companion object { companion object {
fun fromRect(rect: Rect): SpaceBox { fun fromRect(rect: Rect): SpaceBox {
return SpaceBox(rect.top, rect.right, rect.bottom, rect.left) return SpaceBox(rect.top, rect.right, rect.bottom, rect.left)
@@ -31,7 +31,7 @@ data class SpaceBox(val top: Int, val right: Int, val bottom: Int, val left: Int
} }
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class Color(val r: Int, val g: Int, val b: Int, val a: Int) { class Color(val r: Int, val g: Int, val b: Int, val a: Int) {
companion object { companion object {
fun fromColor(color: Int): Color { fun fromColor(color: Int): Color {
val alpha: Int = (color shr 24) and 0xFF / 255 val alpha: Int = (color shr 24) and 0xFF / 255
@@ -48,20 +48,20 @@ data class Color(val r: Int, val g: Int, val b: Int, val a: Int) {
} }
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class Coordinate( class Coordinate(
@Serializable(with = NumberSerializer::class) val x: Number, @Serializable(with = NumberSerializer::class) val x: Number,
@Serializable(with = NumberSerializer::class) val y: Number @Serializable(with = NumberSerializer::class) val y: Number
) {} ) {}
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class Coordinate3D( class Coordinate3D(
@Serializable(with = NumberSerializer::class) val x: Number, @Serializable(with = NumberSerializer::class) val x: Number,
@Serializable(with = NumberSerializer::class) val y: Number, @Serializable(with = NumberSerializer::class) val y: Number,
@Serializable(with = NumberSerializer::class) val z: Number @Serializable(with = NumberSerializer::class) val z: Number
) {} ) {}
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
data class Size( class Size(
@Serializable(with = NumberSerializer::class) val width: Number, @Serializable(with = NumberSerializer::class) val width: Number,
@Serializable(with = NumberSerializer::class) val height: Number @Serializable(with = NumberSerializer::class) val height: Number
) {} ) {}

View File

@@ -25,6 +25,7 @@ import com.facebook.flipper.plugins.uidebugger.model.MetadataUpdateEvent
import com.facebook.flipper.plugins.uidebugger.model.Node import com.facebook.flipper.plugins.uidebugger.model.Node
import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent
import com.facebook.flipper.plugins.uidebugger.model.Snapshot import com.facebook.flipper.plugins.uidebugger.model.Snapshot
import com.facebook.flipper.plugins.uidebugger.model.TraversalError
import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
@@ -107,7 +108,19 @@ class TreeObserverManager(val context: UIDContext) {
val workerThreadStartTimestamp = System.currentTimeMillis() val workerThreadStartTimestamp = System.currentTimeMillis()
val nodes = batchedUpdate.updates.flatMap { it.deferredNodes.map { it.value() } } val nodes =
try {
batchedUpdate.updates.flatMap { it.deferredNodes.map { it.value() } }
} catch (exception: Exception) {
context.onError(
TraversalError(
"DeferredProcessing",
exception.javaClass.simpleName,
exception.message ?: "",
exception.stackTraceToString()))
return
}
val frameworkEvents = context.extractPendingFrameworkEvents() val frameworkEvents = context.extractPendingFrameworkEvents()
val snapshotUpdate = batchedUpdate.updates.find { it.snapshot != null } val snapshotUpdate = batchedUpdate.updates.find { it.snapshot != null }
val deferredComputationEndTimestamp = System.currentTimeMillis() val deferredComputationEndTimestamp = System.currentTimeMillis()

View File

@@ -9,11 +9,11 @@ package com.facebook.flipper.plugins.uidebugger.traversal
import android.util.Log import android.util.Log
import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.LogTag
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister import com.facebook.flipper.plugins.uidebugger.core.UIDContext
import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.Id
import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor
import com.facebook.flipper.plugins.uidebugger.model.Node import com.facebook.flipper.plugins.uidebugger.model.Node
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory import com.facebook.flipper.plugins.uidebugger.model.TraversalError
import com.facebook.flipper.plugins.uidebugger.util.Immediate import com.facebook.flipper.plugins.uidebugger.util.Immediate
import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred
@@ -24,12 +24,11 @@ import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred
* - The second item are any observable roots discovered. * - The second item are any observable roots discovered.
*/ */
class PartialLayoutTraversal( class PartialLayoutTraversal(
private val descriptorRegister: DescriptorRegister, private val context: UIDContext,
private val treeObserverFactory: TreeObserverFactory,
) { ) {
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
internal fun NodeDescriptor<*>.asAny(): NodeDescriptor<Any> = this as NodeDescriptor<Any> private fun NodeDescriptor<*>.asAny(): NodeDescriptor<Any> = this as NodeDescriptor<Any>
fun traverse(root: Any, parentId: Id?): Pair<List<MaybeDeferred<Node>>, List<Pair<Any, Id?>>> { fun traverse(root: Any, parentId: Id?): Pair<List<MaybeDeferred<Node>>, List<Pair<Any, Id?>>> {
@@ -47,12 +46,13 @@ class PartialLayoutTraversal(
try { try {
// If we encounter a node that has it own observer, don't traverse // If we encounter a node that has it own observer, don't traverse
if (node != root && treeObserverFactory.hasObserverFor(node)) { if (node != root && context.observerFactory.hasObserverFor(node)) {
observableRoots.add((node to parentId)) observableRoots.add((node to parentId))
continue continue
} }
val descriptor = descriptorRegister.descriptorForClassUnsafe(node::class.java).asAny() val descriptor =
context.descriptorRegister.descriptorForClassUnsafe(node::class.java).asAny()
val curId = descriptor.getId(node) val curId = descriptor.getId(node)
if (shallow.contains(node)) { if (shallow.contains(node)) {
@@ -82,13 +82,13 @@ class PartialLayoutTraversal(
var activeChildId: Id? = null var activeChildId: Id? = null
if (activeChild != null) { if (activeChild != null) {
val activeChildDescriptor = val activeChildDescriptor =
descriptorRegister.descriptorForClassUnsafe(activeChild.javaClass) context.descriptorRegister.descriptorForClassUnsafe(activeChild.javaClass)
activeChildId = activeChildDescriptor.getId(activeChild) activeChildId = activeChildDescriptor.getId(activeChild)
} }
val childrenIds = mutableListOf<Id>() val childrenIds = mutableListOf<Id>()
children.forEach { child -> children.forEach { child ->
val childDescriptor = descriptorRegister.descriptorForClassUnsafe(child.javaClass) val childDescriptor = context.descriptorRegister.descriptorForClassUnsafe(child.javaClass)
childrenIds.add(childDescriptor.getId(child)) childrenIds.add(childDescriptor.getId(child))
stack.add(Pair(child, curId)) stack.add(Pair(child, curId))
// If there is an active child then don't traverse it // If there is an active child then don't traverse it
@@ -117,6 +117,12 @@ class PartialLayoutTraversal(
}) })
} catch (exception: Exception) { } catch (exception: Exception) {
Log.e(LogTag, "Error while processing node ${node.javaClass.name} $node", exception) Log.e(LogTag, "Error while processing node ${node.javaClass.name} $node", exception)
context.onError(
TraversalError(
node.javaClass.simpleName,
exception.javaClass.simpleName,
exception.message ?: "",
exception.stackTraceToString()))
} }
} }