diff --git a/src/dorkbox/network/connection/ListenerManager.kt b/src/dorkbox/network/connection/ListenerManager.kt index fa8d18f2..ab841274 100644 --- a/src/dorkbox/network/connection/ListenerManager.kt +++ b/src/dorkbox/network/connection/ListenerManager.kt @@ -31,36 +31,25 @@ internal class ListenerManager(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 diff --git a/src/dorkbox/network/rmi/RmiClient.kt b/src/dorkbox/network/rmi/RmiClient.kt index 00cad84c..e4f72fe1 100644 --- a/src/dorkbox/network/rmi/RmiClient.kt +++ b/src/dorkbox/network/rmi/RmiClient.kt @@ -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? { - // 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(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!! - } - } } diff --git a/src/dorkbox/network/rmi/RmiSupport.kt b/src/dorkbox/network/rmi/RmiSupport.kt index 913737b8..1f906784 100644 --- a/src/dorkbox/network/rmi/RmiSupport.kt +++ b/src/dorkbox/network/rmi/RmiSupport.kt @@ -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) }