Better exception handling wrt RMI method invocation

This commit is contained in:
nathan 2020-08-13 00:47:35 +02:00
parent 994fa8d196
commit e621bfb4e3
3 changed files with 47 additions and 40 deletions

View File

@ -31,36 +31,25 @@ internal class ListenerManager<CONNECTION: Connection>(private val logger: KLogg
*/
fun cleanStackTrace(throwable: Throwable) {
// NOTE: when we remove stuff, we ONLY want to remove the "tail" of the stacktrace, not ALL parts of the stacktrace
val reversedList = throwable.stackTrace.reversed().toMutableList()
// we have to remove kotlin stuff from the stacktrace
val reverseIter = reversedList.iterator()
while (reverseIter.hasNext()) {
val stackName = reverseIter.next().className
if (stackName.startsWith("kotlinx.coroutines") || stackName.startsWith("kotlin.coroutines")) {
// cleanup the stack elements which create the stacktrace
reverseIter.remove()
} else {
// done cleaning up the tail from kotlin
break
val stackTrace = throwable.stackTrace
var newEndIndex = stackTrace.size - 1
for (i in newEndIndex downTo 0) {
val stackName = stackTrace[i].className
if (i == newEndIndex) {
if (stackName.startsWith("kotlinx.coroutines.") ||
stackName.startsWith("kotlin.coroutines.") ||
stackName.startsWith("dorkbox.network.")) {
newEndIndex--
} else {
break
}
}
}
// remove dorkbox network stuff
while (reverseIter.hasNext()) {
val stackName = reverseIter.next().className
if (stackName.startsWith("dorkbox.network")) {
// cleanup the stack elements which create the stacktrace
reverseIter.remove()
} else {
// done cleaning up the tail from network
break
}
}
throwable.stackTrace = reversedList.reversed().toTypedArray()
// tailToChopIndex will also remove the VERY LAST CachedMethod or CachedAsmMethod access invocation (because it's offset by 1)
// NOTE: we want to do this!
throwable.stackTrace = stackTrace.copyOfRange(0, newEndIndex)
}
}
// initialize a emtpy arrays

View File

@ -78,7 +78,6 @@ internal class RmiClient(val isGlobal: Boolean,
// if we are ASYNC, then this method immediately returns
private suspend fun sendRequest(method: Method, args: Array<Any>): Any? {
// there is a STRANGE problem, where if we DO NOT respond/reply to method invocation, and immediate invoke multiple methods --
// the "server" side can have out-of-order method invocation. There are 2 ways to solve this
// 1) make the "server" side single threaded
@ -114,6 +113,28 @@ internal class RmiClient(val isGlobal: Boolean,
throw TimeoutException("Response timed out: ${method.declaringClass.name}.${method.name}")
}
is Exception -> {
// reconstruct the stack trace, so the calling method knows where the method invocation happened, and can trace the call
// this stack will ALWAYS run up to this method (so we remove from the top->down, to get to the call site)
val stackTrace = Exception().stackTrace
val myClassName = RmiClient::class.java.name
var newStartIndex = 0
for (element in stackTrace) {
newStartIndex++
if (element.className == myClassName && element.methodName == "invoke") {
// we do this 1 more time, because we want to remove the proxy invocation off the stack as well.
newStartIndex++
break
}
}
val newStack = Array<StackTraceElement>(result.stackTrace.size + stackTrace.size - newStartIndex) { stackTrace[0] }
result.stackTrace.copyInto(newStack)
stackTrace.copyInto(newStack, result.stackTrace.size, newStartIndex)
result.stackTrace = newStack
throw result
}
else -> {
@ -252,6 +273,15 @@ internal class RmiClient(val isGlobal: Boolean,
}
}
// trampoline so we can access suspend functions correctly and (if suspend) get the coroutine connection parameter)
private fun invokeSuspendFunction(continuation: Continuation<*>, suspendFunction: suspend () -> Any?): Any? {
return try {
SuspendFunctionAccess.invokeSuspendFunction(suspendFunction, continuation)
} catch (e: InvocationTargetException) {
throw e.cause!!
}
}
override fun hashCode(): Int {
val prime = 31
var result = 1
@ -276,12 +306,4 @@ internal class RmiClient(val isGlobal: Boolean,
return rmiObjectId == other.rmiObjectId
}
private fun invokeSuspendFunction(continuation: Continuation<*>, suspendFunction: suspend () -> Any?): Any? {
return try {
SuspendFunctionAccess.invokeSuspendFunction(suspendFunction, continuation)
} catch (e: InvocationTargetException) {
throw e.cause!!
}
}
}

View File

@ -358,11 +358,7 @@ internal class RmiSupport(logger: KLogger,
result.initCause(null)
}
// only remove stuff if our logger is NOT on trace (so normal logs will not show extra info, trace will show extra info)
if (!logger.isTraceEnabled) {
ListenerManager.cleanStackTrace(result as Throwable)
}
ListenerManager.cleanStackTrace(result as Throwable)
logger.error("Error invoking method: ${cachedMethod.method.declaringClass.name}.${cachedMethod.method.name}", result)
}