Debug Annotation Processor Kapt

Hey,
does anyone have experience with debugging custom annotation processors? I’m not able to figure out how to do it.
Based on this SO thread I started gradle using

./gradlew --no-daemon -Dorg.gradle.debug=true -Dkotlin.daemon.jvm.options="-Xdebug,-Xrunjdwp:transport=dt_socket\,address=5005\,server=y\,suspend=n" :clean assemble

Also it looks like I am able to connect to it using idea’s remote debug configuration.
The problem I have is that it does not seem to hit my breakpoint.

Any help is appreciated. I really don’t want to have to use print statements for debugging :wink:

By default all Kotlin tasks run in Kotlin Compile Daemon, which is a separate process.

This argument:

-Dkotlin.daemon.jvm.options="-Xdebug,-Xrunjdwp:transport=dt_socket\,address=5005\,server=y\,suspend=n"

tells Kotlin to start a daemon process, which will listen on 5005 port for a debugger.

org.gradle.debug=true tells Gradle to start a process, which will listen on 5005 port for a debugger.

Two processes cannot listen on the same port, so you should specify a different port for Kotlin daemon.
Alternatively you can simply omit -Dorg.gradle.debug=true, since KAPT runs APs in Kotlin daemon.

If you’re using Kotlin 1.3.20, you can also add kapt.use.worker.api=true to Gradle properties, and debug Gradle process instead. Note that only with 1.3.20 kapt.use.worker.api runs KAPT in-process (before this option used Gradle Worker Processes by default).

3 Likes

hi @tsvetkov,
what is the difference between kapt.user.worker.api= true compared to kotlin.parallel.tasks.in.project=true ?
Previously, I only added kotlin.parallel.tasks.in.project=true, but when I added both of them, I got extremely high GC time. Or maybe I need to increase the max heap size for kotlin processes ?
I also noticed the task become: org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask
from org.jetbrains.kotlin.gradle.internal.KaptKotlincTask. Do you mind to explain what is the differences between those two ? Appreciate your help.
Thanks,
Doni

Hi @doniwinata03,

Gradle Worker API allows plugins to submit isolated (i.e. they don’t have access to Gradle model) units of work, that can be executed in parallel. Also Worker API allows (but not requires) plugin developers to use Worker daemon processes, which are managed by Gradle.

By default Kotlin runs the compiler and KAPT in Kotlin Compile Daemon.

KAPT runs in two stages:

  1. Stubs generation, which generates .java stub files from Kotlin source files.
  2. Actual annotation processing, which executes annotation processors on stubs and java sources.

kapt.use.worker.api=true does two things:

  1. It uses Worker API for the second stage (annotation processing), so (non stubs generating) KAPT tasks can run in parallel.
  2. Also it moves the second stage (annotation processing) out of Kotlin Compile Daemon (mostly to avoid affecting non-KAPT compilation). Before 1.3.20 annotation processing was run using Gradle Worker processes, but it lead to increased memory consumption (because a Worker process cannot be used concurrently, see Concurrently use worker process when work is thread safe · Issue #8220 · gradle/gradle · GitHub). Since 1.3.20 kapt.use.worker.api runs annotation processing in the main Gradle process.

kotlin.parallel.tasks.in.project=true was added later. It simply uses Worker API for compileKotlin/compileKotlin2Js/compileKotlinCommon tasks, so these tasks can run in parallel. This mode does not use Worker processes, compilation is still performed in Kotlin Compile Daemon.
The name of the property might be a little confusing, I’ve chosen it to highlight the reason a user might want to enable it:

  • Without Worker API it was possible to execute tasks from different projects (one task per project) in parallel (via --parallel command line flag or by specifying org.gradle.parallel=true).
  • Only Worker API allows for parallel execution of independent tasks from the same project.

In all cases parallel execution may increase memory usage (because compiler’s threads don’t share any data). kapt.use.worker.api may increase memory usage in main Gradle process, kotlin.parallel.tasks.in.project may increase memory usage in Kotlin Compiler Daemon process.

By default Kotlin Daemon uses the same amount of memory as main Gradle process (which is controlled by org.gradle.jvmargs=-Xmx<MAX_HEAP_SIZE> project property). Kotlin Daemon’s heap size can be controlled by kotlin.daemon.jvm.options system property.

E.g. to set main Gradle process heap size to 768MB, and Kotlin Daemon heap size to 2GB, add the following to gradle.properties:

org.gradle.jvmargs=-Xmx768m -Dkotlin.daemon.jvm.options=-Xmx2g

You can use the standard jvisualvm utility from your JDK installation to determine the optimal heap size.

2 Likes

Hello all,

I’m having the same initial issue that @Wasabi375 reported.

I also tried with -Dorg.gradle.debug=true and kapt.use.worker.api=true on gradle.properties but with no luck.

What I’m currently doing is:
1 - Running
./gradlew --no-daemon -Dkotlin.daemon.jvm.options="-Xdebug,-Xrunjdwp:transport=dt_socket\,address=5005\,server=y\,suspend=n" :clean assemble

2 - Attaching a Remote Configuration as soon as I enter the above command on AS terminal

  • Debugger mode: Attach to remote JVM
  • Host: localhost
  • Port: 5005:
  • Command line arguments for remote JVM: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
  • Use module classpath: app (I’ve got two more modules, annotation and processor that contains the kapt logic)

Moreover, I’m not sure if it’s relevant but on the terminal I can see this:

Task :app:kaptDebugKotlin
Could not connect to Kotlin compile daemon
Could not connect to kotlin daemon. Using fallback strategy.

Thank you.

I only managed to make this work by adding kapt.use.worker.api=true to my gradle.properties file, even if I wasn’t starting the org.gradle.debug process I wasn’t able to attach the debugger.

If someone has the same error, I’ve written a small step-by-step process :slightly_smiling_face::

1 Like

@cafonsomota you may want to change suspend=n to suspend=y, that way you can take your time attaching :wink:

Also for my case adding -Dkotlin.compiler.execution.strategy="in-process" ensures kapt runs in-process so the other debug command line options apply.