Small lib for recovering stack trace in exceptions thrown in Kotlin coroutines

Overview

Maven Central

Stacktrace-decoroutinator

Library for recovering stack trace in exceptions thrown in Kotlin coroutines.

Supports JVM(not Android) versions 1.8 or higher.

To enable it you should call method DocoroutinatorRuntime.enableDecoroutinatorRuntime() before creating any coroutine.

Usage example:

import dev.reformator.stacktracedecoroutinator.util.DocoroutinatorRuntime
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

suspend fun rec(depth: Int) {
    if (depth == 0) {
        delay(100)
        throw Exception("exception in ${System.currentTimeMillis()}")
    }
    rec(depth - 1)
}

fun main() {
    DocoroutinatorRuntime().enableDecoroutinatorRuntime() // enable stacktrace-decoroutinator runtime

    try {
        runBlocking {
            rec(10)
        }
    } catch (e: Exception) {
        e.printStackTrace() // print full stack trace with 10 recursive calls
    }
}

Available on Maven Central

Comments
  • JVM 11+ support

    JVM 11+ support

    Does this library work with JVM 11, 17, etc? For example, with corretto-17.0.3 and stacktrace-decoroutinator-jvm:2.2.1 I get

    Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.findLoadedClass(java.lang.String) accessible: module java.base does not "opens java.lang" to unnamed module @1c20c684
    	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
    	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
    	at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
    	at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
    	at dev.reformator.stacktracedecoroutinator.utils.Utils_jvmKt.getClassIfLoaded(utils-jvm.kt:11)
    	at dev.reformator.stacktracedecoroutinator.runtime.DecoroutinatorRuntime.getState(runtime-jvm.kt:42)
    	at dev.reformator.stacktracedecoroutinator.runtime.DecoroutinatorRuntime.load(runtime-jvm.kt:52)
    	at dev.reformator.stacktracedecoroutinator.runtime.DecoroutinatorRuntime.load$default(runtime-jvm.kt:51)
    	at MainKt.main(Main.kt:14)
    	at MainKt.main(Main.kt)
    

    when I launch the example from README. With 1.8 it works fine.

    opened by ov7a 9
  • Jacoco interference

    Jacoco interference

    When using Jacoco, methods are instrumented to call $jacocoInit first. As a consequence the first instructions of suspend methods are not ALOAD (25) and INSTANCEOF (193) anymore. The method MethodNode.getDebugMetadataInfo() in classTransformer.kt assumes this.

    The following code searches for two consecutive ALOAD/INSTANCEOF instructions with the same conditions as previously. It replaces the second part of the body starting with "val firstInstructions: ".

    return instructions.asSequence().filter {
            val next = it.next
            it is VarInsnNode && it.opcode == Opcodes.ALOAD && it.`var` == continuationIndex
                    && next != null && next is TypeInsnNode && next.opcode == Opcodes.INSTANCEOF
        }
            .firstOrNull()?.let {
                val continuationClassName = (it.next as TypeInsnNode).desc.replace('/', '.')
                decoroutinatorJvmAgentRegistry.metadataInfoResolver.getDebugMetadataInfo(continuationClassName)
            }
    
    opened by p4654545 3
  • Application class implementation is redundant

    Application class implementation is redundant

    It's not really convenient that you bring the default application impl with your android library. It's very rare for real-life project to don't have a chosen way of init already, usually via it's own Application class or via jetpack app-startup.

    Not only it's unnecessary and mostly useless piece of code, it also forces usage of tricks then this library is used in your manifest - adding obscure tools:replace="android:name" in app AndroidManifest.xml

    opened by dant3 2
  • Make getDebugMetadataInfo robust against extra instructions at the start of a method

    Make getDebugMetadataInfo robust against extra instructions at the start of a method

    Jacoco (and other instrumenters) may add instructions at the start of a method. Jacoco typically adds a call to $jacocoInit.

    This PR changes the getDebugMetadataInfo logic such that it finds the first occurrence of the required instruction pair.

    I made sure that Jacoco only instruments the new class JacocoInstrumentedMethodTest in runtime-test-jvm.kt.

    opened by p4654545 1
  • IllegalStateException:

    IllegalStateException: "Different file names for class..."

    This is the exception: Caused by: java.lang.IllegalStateException: different file names for class [our-namespace.InterestProvider$createFlow$$inlined$transform$1]: [InterestProvider.kt] and [Emitters.kt] at dev.reformator.stacktracedecoroutinator.jvmagentcommon.DecoroutinatorJvmAgentStacktraceMethodHandleRegistry.getStacktraceMethodHandles$lambda-5(methodHandleRegistry-jvm-agent-common.kt:31)

    It seems that Asm(?) has assigned "Emitters.kt" as the fileName of this particular ClassNode. As a result ClassNode.transform (classTransformer.kt) calls buildMarkerAnnotation with "Emitters.kt" as the filename, while clearly the filename should be InterestProvider.kt. The class "InterestProvider$createFlow$$inlined$transform$1" is generated inside InterestProvider.class. When decompiling InterestProvider.class I can see that the DebugMetaData information of "InterestProvider$createFlow$$inlined$transform$1" has the right filename: "InterestProvider.kt".

    Adding "val filename: String" to DebugMetaDataInfo and replacing sourceFile in this line (classTransformer.kt):

    visibleAnnotations.add(buildMarkerAnnotation(sourceFile, suspendFuncName2LineNumbers))

    with

    getDebugMetadataInfo()?.filename ?: sourceFile

    results in Decoroutinator not complaining anymore when instrumenting our codebase.

    Unfortunately I was not able to create a reproduction scenario and could not determine why Asm comes up with "Emitters.kt" as the source filename.

    opened by p4654545 1
  • Exclude instrumented BaseContinuationImpl from the classpath

    Exclude instrumented BaseContinuationImpl from the classpath

    It would be better to exclude the instrumented class kotlin.coroutines.jvm.internal.BaseContinuationImpl from the classpath. And load it on demand (in DecoroutinatorRuntime.load() method).

    That will allow to completely eliminate the effect of SD if it's required.

    opened by Anamorphosee 1
  • Provide no-op variant for android builds

    Provide no-op variant for android builds

    Great library!

    Have you considered creating no-op version of it so we can do

    debugImplementation("realVersion") 
    releaseImplementation("noopVersion")
    

    It's pretty standard practice on android to avoid bringing unnecessary libraries to release build

    opened by jakoss 1
  • Add a Gitter chat badge to README.md

    Add a Gitter chat badge to README.md

    Anamorphosee/stacktrace-decoroutinator now has a Chat Room on Gitter

    @Anamorphosee has just created a chat room. You can visit it here: https://gitter.im/stacktrace-decoroutinator/community.

    This pull-request adds this badge to your README.md:

    Gitter

    If my aim is a little off, please let me know.

    Happy chatting.

    PS: Click here if you would prefer not to receive automatic pull-requests from Gitter in future.

    opened by gitter-badger 0
  • NullPointerException on Android

    NullPointerException on Android

    Hey @Anamorphosee, I'm running into this crash when trying to use SD on Android:

    Caused by: java.lang.NullPointerException: it must not be null
            at dev.reformator.stacktracedecoroutinator.runtime.DecoroutinatorRuntime.load(runtime-android.kt:38)
            at dev.reformator.stacktracedecoroutinator.runtime.DecoroutinatorRuntime.load$default(runtime-android.kt:24)
    

    The android artifact isn't an AAR so I wasn't able to step into the sources to debug what exactly is null. I was testing on Android 13.

    opened by saket 1
  • Android API 26 issue

    Android API 26 issue

    On Android API 26 got exception below. Seems it somehow related to the way SD replaces implementation of class BaseContinuationImpl at runtime.

    stack trace
    java.lang.IncompatibleClassChangeError: The method 'java.lang.Class java.lang.Object.getClass()' was expected to be of type interface but instead was found to be of type virtual (declaration of 'dev.reformator.stacktracedecoroutinator.common.DecoroutinatorContinuationStacktraceElementRegistryImpl' appears in /data/app/dev.reformator.stacktracedecoroutinator.test-FP9cAHXGzdvvqHD-T2lQ1w==/base.apk:classes6.dex)
    	at dev.reformator.stacktracedecoroutinator.common.DecoroutinatorContinuationStacktraceElementRegistryImpl.lambda$getStacktraceElements$2(DecoroutinatorContinuationStacktraceElementRegistryImpl.java:66)
    	at dev.reformator.stacktracedecoroutinator.common.DecoroutinatorContinuationStacktraceElementRegistryImpl$$ExternalSyntheticLambda1.apply(Unknown Source:4)
    	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:194)
    	at java.util.Iterator.forEachRemaining(Iterator.java:116)
    	at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
    	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
    	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:235)
    	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:501)
    	at dev.reformator.stacktracedecoroutinator.common.DecoroutinatorContinuationStacktraceElementRegistryImpl.getStacktraceElements(DecoroutinatorContinuationStacktraceElementRegistryImpl.java:79)
    	at dev.reformator.stacktracedecoroutinator.stdlib.StdlibKt.decoroutinatorResumeWith(stdlib.kt:28)
    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(continuation-stdlib.kt:18)
    	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
    	at kotlinx.coroutines.JavaTool.EventLoopProcessNextEvent(JavaTool.java:78)
    	at dev.reformator.stacktracedecoroutinator.performancetest.Performance_test_androidKt.BlockingCoroutineJoinBlocking(performance-test-android.kt:332)
    	at kotlinx.coroutines.JavaTool.createAndJoinBlockingCoroutine(JavaTool.java:53)
    	at dev.reformator.stacktracedecoroutinator.performancetest.Performance_test_androidKt.runBlocking(performance-test-android.kt:306)
    	at dev.reformator.stacktracedecoroutinator.performancetest.Performance_test_androidKt.runBlocking$default(performance-test-android.kt:281)
    	at dev.reformator.stacktracedecoroutinator.performancetest.PerformanceTest.resumeWithDepth(performance-test-android.kt:277)
    	at dev.reformator.stacktracedecoroutinator.performancetest.PerformanceTest.depth10(performance-test-android.kt:231)
    	at java.lang.reflect.Method.invoke(Native Method)
    	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    	at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:162)
    	at org.junit.runners.Suite.runChild(Suite.java:128)
    	at org.junit.runners.Suite.runChild(Suite.java:27)
    	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    	at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
    	at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
    	at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:444)
    	at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2074)
    
    opened by Anamorphosee 0
  • Make minSdk lower in android library

    Make minSdk lower in android library

    minSdk = 26 covers 80% of the devices according to play market stats. While it might work for some projects, it will not work for others. Is there a specific reason why it has to be 26? Looking at the code, I guess it can be easily lowered to something like 21 which would make this library easier to use.

    Currently you have to add a pesky

        <uses-sdk
            android:minSdkVersion="24"
            tools:overrideLibrary="dev.reformator.stacktracedecoroutinator"
            />
    

    in order to use this library in android project which needs lower minSdk

    opened by dant3 2
Releases(v2.3.5)
Owner
null
Small Kafka Playground to play around with Test Containers, and KotlinX Coroutines bindings while reading Kafka Definite Guide V2

KafkaPlayground Small playground where I'm playing around with Kafka in Kotlin and the Kafka SDK whilst reading the Kafka book Definite Guide from Con

Simon Vergauwen 34 Dec 30, 2022
CryptoMovies is a small app that show modern Android developement: with Hilt, Coroutines, Flow, Jetpack and Material Design 3

CryptoMovies is a small app that show modern Android developement: with Hilt, Coroutines, Flow, Jetpack and Material Design 3.

Leonardo Pirro 7 Sep 2, 2022
Recycler-coroutines - RecyclerView Auto Add Data Using Coroutines

Sample RecyclerView Auto Add With Coroutine Colaborator Very open to anyone, I'l

Faisal Amir 8 Dec 1, 2022
📒 NotyKT is a complete 💎Kotlin-stack (Backend + Android) 📱 application built to demonstrate the use of Modern development tools with best practices implementation🦸.

NotyKT ??️ NotyKT is the complete Kotlin-stack note taking ??️ application ?? built to demonstrate a use of Kotlin programming language in server-side

Shreyas Patil 1.4k Jan 4, 2023
Android Clean Architecture in Rorty is a sample project that presents modern, approach to Android application development using Kotlin and latest tech-stack.

Android Clean Architecture in Rorty is a sample project that presents modern, approach to Android application development using Kotlin and latest tech-stack.

Mr.Sanchez 176 Jan 4, 2023
This lib implements the most common CoroutineScopes used in Android apps.

AndroidCoroutineScopes Starting from 0.26.0 release we should define a scope for new coroutines (docs). To avoid this boilerplate code I've created th

Adriel Café 15 Oct 3, 2022
Account-lib - A suite of libraries to facilitate the usage of account-sdk

Usage Clone this repository (skip this step if the repo is on your local machine). The default branch is fine. git clone https://github.com/AFBlockcha

null 0 May 24, 2022
A light lib that helps and centralize logs in your application.

BadgeLog (Kotlin version) For the iOS swift version, see this page BadgeLog is an Android Kotlin library that helps you manage logs within your applic

Daniele 1 Feb 8, 2022
An android lib that provides most commonly used utility function

Awesome-Utility An android lib that provides most commonly used utility function. It can help Android developers with supporting common utility functi

Ameer Hamza 2 Apr 5, 2022
LanServers - A small plugin written in Kotlin that runs on all major Minecraft Servers

LanServers This is a small plugin written in Kotlin that runs on all major Minec

Redstonecrafter0 6 Mar 12, 2022
Opinionated Redux-like implementation backed by Kotlin Coroutines and Kotlin Multiplatform Mobile

CoRed CoRed is Redux-like implementation that maintains the benefits of Redux's core idea without the boilerplate. No more action types, action creato

Kittinun Vantasin 28 Dec 10, 2022
Maildroid is a small robust android library for sending emails using SMTP server

Maildroid ?? Maildroid is a small robust android library for sending emails using SMTP server ?? Key Features • Add to your project • Documentation •

Nedim 174 Dec 22, 2022
Permissionmanager is a small wrapper for handling permission requests.

Permissionmanager Permissionmanager is a small wrapper for handling permission requests. Installation Add jitpack to your repositories in Project buil

Thomas Cirksena 11 Nov 17, 2020
A collection of small utility functions to make it easier to deal with some otherwise nullable APIs on Android.

requireKTX requireKTX is a collection of small utility functions to make it easier to deal with some otherwise nullable APIs on Android, using the sam

Márton Braun 82 Oct 1, 2022
💫 Small microservice to handle state changes of Kubernetes pods and post them to Instatus or Statuspages

?? Kanata Small microservice to handle state changes of Kubernetes pods and post them to Instatus or Statuspages ?? Why? I don't really want to implem

Noel 4 Mar 4, 2022
A small DSL to make building a conversation in Bukkit easy.

Konversation Konversation provides a simple builder to construct chains of prompts to be used in the conversation API present in Bukkit. Bukkit only p

Tim Hagemann 2 Dec 4, 2022
Small app to create icon sets for Linux, Windows, OSX, Android and IOS from a single PNG image

FXIconcreator Small app to create icon sets (multi resolution) for Linux, Windows, OSX from a single PNG image Reason for creating such an app was tha

null 18 Aug 4, 2022
Location-history-viewer - Small compose-desktop app to view data from google's location history

Google Location History Takeout Viewer This application provides a minimalistic

Chris Stelzmüller 3 Jun 23, 2022
Kamper - a small KMM/KMP library that provides performance monitoring for your app.

?? Kamper Kamper is a KMP/KMM library that implements a unified way to track application performances. The solution is based on plugin design patterns

S. Mellouk 31 Jun 10, 2022