From 719262fcdc78fce03dc9e614bbce51c301f9d12c Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Wed, 8 Feb 2023 16:50:30 +0300 Subject: [PATCH] [utbot-logs] Complete logs redesign: 1. Added Logger model to rd. 2. InstrumentedProcess is now sending all logs to EngineProcess, which then writes to its own file. 3. Removed UtSettings.instrumentedProcessLogLevel 4. Auxiliary log functions 5. UtRdLoggers redesigned to have the same logging pattern 6. From now log4j2.xml is responsible for instrumentation process log configuration 7. Added summarization start/end log notifications and instrumented process starting messages. 8. Design doc for logs, clarifying comments and refactoring. 9. Changed `bracket` to `logMeasure`. its signature from plain string message to lambda to remove extra interpolation when message log level is not supported/ --- build.gradle.kts | 1 + docs/InterProcessLogging.md | 229 +++++++++ docs/RD for UnitTestBot.md | 2 +- docs/contributing/InterProcessDebugging.md | 4 +- docs/contributing/InterProcessLogging.md | 129 ----- .../utbot/cli/GenerateTestsAbstractCommand.kt | 6 +- .../main/kotlin/org/utbot/common/Logging.kt | 38 +- .../kotlin/org/utbot/framework/UtSettings.kt | 6 - .../framework/plugin/api/UtExecutionResult.kt | 21 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 8 +- .../kotlin/org/utbot/engine/pc/UtSolver.kt | 6 +- .../codegen/tree/CgMethodConstructor.kt | 28 -- .../framework/plugin/api/TestCaseGenerator.kt | 4 +- .../GenerateTestsAndSarifReportFacade.kt | 6 +- .../framework/process/EngineProcessMain.kt | 133 +++-- .../generated/EngineProcessModel.Generated.kt | 92 ++-- .../generated/EngineProcessRoot.Generated.kt | 2 +- .../RdInstrumenterAdapter.Generated.kt | 4 +- .../RdSourceFindingStrategy.Generated.kt | 4 +- .../plugin/GenerateTestsAndSarifReportTask.kt | 274 +++++------ .../utbot/instrumentation/ConcreteExecutor.kt | 27 +- .../agent/DynamicClassTransformer.kt | 2 +- .../execution/phases/ExecutionPhase.kt | 7 +- .../process/InstrumentedProcessMain.kt | 84 ++-- .../process/InstrumentedProcessRunner.kt | 21 +- .../instrumentation/rd/InstrumentedProcess.kt | 32 +- .../InstrumentedProcessModel.Generated.kt | 13 +- .../intellij/plugin/process/EngineProcess.kt | 31 +- utbot-intellij/src/main/resources/log4j2.xml | 35 +- .../main/kotlin/org/utbot/contest/Contest.kt | 23 +- .../org/utbot/contest/ContestEstimator.kt | 4 +- .../utbot/monitoring/StatisticsMonitoring.kt | 4 +- .../plugin/GenerateTestsAndSarifReportMojo.kt | 456 +++++++++--------- utbot-rd/build.gradle | 9 + .../kotlin/org/utbot/rd/ClientProcessUtil.kt | 19 + .../org/utbot/rd/ProcessWithRdServer.kt | 8 + .../src/main/kotlin/org/utbot/rd/UtRdUtil.kt | 19 +- .../rd/generated/LoggerModel.Generated.kt | 177 +++++++ .../rd/generated/LoggerRoot.Generated.kt | 58 +++ .../SynchronizationModel.Generated.kt | 26 +- .../org/utbot/rd/loggers/UtRdConsoleLogger.kt | 5 +- .../rd/loggers/UtRdConsoleLoggerFactory.kt | 4 + .../org/utbot/rd/loggers/UtRdKLogger.kt | 31 +- .../utbot/rd/loggers/UtRdKLoggerFactory.kt | 7 +- .../org/utbot/rd/loggers/UtRdLogUtil.kt | 27 ++ .../org/utbot/rd/loggers/UtRdRemoteLogger.kt | 62 +++ .../rd/loggers/UtRdRemoteLoggerFactory.kt | 17 + .../org/utbot/rd/models/EngineProcessModel.kt | 1 - .../rd/models/InstrumentedProcessModel.kt | 5 - .../rdgen/org/utbot/rd/models/LoggerModel.kt | 23 + .../utbot/rd/models/SynchronizationModel.kt | 6 + .../examples/SummaryTestCaseGeneratorTest.kt | 6 +- .../kotlin/org/utbot/summary/Summarization.kt | 55 ++- .../testing/TestCodeGeneratorPipeline.kt | 4 +- .../utbot/testing/UtValueTestCaseChecker.kt | 4 +- 55 files changed, 1405 insertions(+), 904 deletions(-) create mode 100644 docs/InterProcessLogging.md delete mode 100644 docs/contributing/InterProcessLogging.md create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerModel.Generated.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerRoot.Generated.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdLogUtil.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdRemoteLogger.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdRemoteLoggerFactory.kt create mode 100644 utbot-rd/src/main/rdgen/org/utbot/rd/models/LoggerModel.kt diff --git a/build.gradle.kts b/build.gradle.kts index 78612e3a6d..d46b7efca3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -57,6 +57,7 @@ allprojects { // this is useful if you debug in docker // testLogging.showStandardStreams = true // testLogging.showStackTraces = true + // set heap size for the test JVM(s) minHeapSize = "128m" maxHeapSize = "3072m" diff --git a/docs/InterProcessLogging.md b/docs/InterProcessLogging.md new file mode 100644 index 0000000000..dd5207fa1d --- /dev/null +++ b/docs/InterProcessLogging.md @@ -0,0 +1,229 @@ +# Interprocess logging + +This document described how logging is performed across all 3 different processes: IDEA, Engine and Instrumented. + +## Architecture + +All logging relies on log4j2. +When UtBot is used as IDEA plugin - [`utbot-intellij/log4j2.xml`](../utbot-intellij/src/main/resources/log4j2.xml) +is used as configuration file. +In other cases(`ContestEstimator`, `Gradle/Maven` tasks, `CLI`, tests) it searches for the first `log4j2.xml` in resources in classpath. + +### IDE process +IDEA part of UtBot write logs to `idea.log`. +Log configuration file is used as is, so if you want to configure logs in IDEA part - use it straight. +If you want to change log configuration in already built plugin - +use `Help > Diagnostic Tools > Debug Log Settings...` to change `log4j2.xml` configuration for plugin. + + +### UtBot plugin + + +UtBot plugin creates log directory `org.utbot.intellij.plugin.process.EngineProcessKt.engineProcessLogDirectory` +where places log files. + +### Engine process +Things are a bit more complicated here. There are cases - Engine process started from IDEA, and Engine process started separately. + +#### Engine process started from IDEA + +As plugin does not support multiple generation processes at a time - logs from any Engine process are written to the same file. + +Default log file directory is `%user_temp%/UtBot/rdEngineProcessLogs`. + +[`utbot-intellij/log4j2.xml`](../utbot-intellij/src/main/resources/log4j2.xml) is copied in +UtBot temporary directory - `org.utbot.intellij.plugin.process.EngineProcessKt.engineProcessLogConfigurationsDirectory`, +and then provided to Engine process via following CLI switch: + ``` + -Dlog4j2.configurationFile=%configuration_file% + ``` + +where `%configuration_file%` will be either: +1. Modified copy of [`utbot-intellij/log4j2.xml`](../utbot-intellij/src/main/resources/log4j2.xml) at UtBot temp directory. + + More precisely, there are 2 appenders in configuration file: + ```xml + + + + + ``` + By default `IdeaAppender` is used everywhere in file, which is used in IDEA plugin. + + When working as Engine process - temporary `log4j2.`xml would be created, in which + substring `ref="IdeaAppender"` will be replaced with `ref="EngineProcessAppender"`, + thus changing all appenders and log pattern, but preserving same categories and log levels for loggers. + + After that, logs will be written by `RollingFileAppender` in `utbot-engine-current.log` file with rolling over + after file reaches 20MB size. Previous log files are named `utbot-engine-%i.log`. Log file with + maximal index is the last one rolled. For example, `utbot-engine-1.log` is created earlier than `utbot-engine-10.log`. + + In IDEA log following lines are printed each time Engine process started: + ``` + | UtBot - EngineProcess | Engine process started with PID = 4172 + | UtBot - EngineProcess | Engine process log directory - C:\Users\user_name\AppData\Local\Temp\UTBot\rdEngineProcessLogs + | UtBot - EngineProcess | Engine process log file - C:\Users\user_name\AppData\Local\Temp\UTBot\rdEngineProcessLogs\utbot-engine-current.log + ``` + +2. Path from `UtSettings.engineProcessLogConfigFile`. + + This option allows to provide path to external Log4j2 configuration file instead of [`utbot-intellij/log4j2.xml`](../utbot-intellij/src/main/resources/log4j2.xml). + At `~/.utbot/settings.properties` you can set path to custom configuration file, + which would apply for engine process, for example: + ``` + engineProcessLogConfigFile=C:\wrk\UTBotJava\engineProcessLog4j2.xml + ``` + This allows you to configure logs for Engine process even for already built plugin, + you need only to restart IDE. + +#### Engine process started separately + +This is the case for `ContestEstimator`, `Gradle/Maven` tasks, `CLI`, tests, etc. +Configuration is taken from `log4j2.xml` in resources from the first `log4j2.xml` in resources in classpath. + +### Instrumented process + +Instrumented process sends its logs to the parent Engine process. +Logs are sent via corresponding RD model: `org.utbot.rd.models.LoggerModel`. +See `org.utbot.instrumentation.rd.InstrumentedProcess.Companion.invoke` and +`org.utbot.instrumentation.process.InstrumentedProcessMainKt.main`. + +## RD logs +Rd has its own logging system, based on `com.jetbrains.rd.util.Logger` interface. It is convenient to use +RD logging as default logging system in instrumented process because log4j2 classes in utbot would be confused +at concrete execution with log4j2 classes in tested project - we will have duplicated versions of log4j2 libs, +this would break instrumentation and coverage statistics. + +You should always override default RD logging strategy as by default it writes to stdout/stderr - use `com.jetbrains.rd.util.Logger.Companion.set` method to provide custom +`com.jetbrains.rd.util.ILoggerFactory`. Already created loggers will be automatically reinstantiated to obtain +new logger from provided factory. You can obtain logger via `com.jetbrains.rd.util.getLogger` function. +Check `EngineProcessMain` for RD logging example. + +For available RD factories see `org.utbot.rd.loggers` package - it contains useful implemented factories, +which log message in the same format as described in `utbot-intellij/src/main/resources/log4j2.xml`. + +## Implementation details + +### Additivity + +Sometimes same log entry might be written to log multiple times. At log you will see something like: +``` +13:55:41.204 | INFO | AnalyticsConfigureUtil | PathSelectorType: INHERITORS_SELECTOR +13:55:41.204 | INFO | AnalyticsConfigureUtil | PathSelectorType: INHERITORS_SELECTOR +``` + +This is because of loggers *additivity* - their full names defines tree structure, and events from children +are visible for parents. For example, following `log4j2.xml` configuration in IDEA will produce such problem: +```xml +... + + + + + + + + +... +``` + +This happens because `org.utbot` logger is parent for `org.utbot.intellij`, and all events from +`org.utbot.intellij` are also transferred to parent. This is called `additivity`. + +The solution is to manually add ```additivity="false"``` tag to all loggers: +```xml +... + + + + + + + + +... +``` + +Consider this problem when you manually configure log level and appender for logger. + +More information is available [here](https://logging.apache.org/log4j/2.x/manual/configuration.html#Additivity). + +### Useful +See auxiliary methods to work with logs at `UtRdLogUtil.kt` and `Logging.kt`. +If you want to trace how long execution took - use `org.utbot.common.LoggingKt.logMeasure` +method with corresponding log level scope. + +In the Engine process log entries from Instrumented process are logged by `org.utbot.instrumentation.rd.InstrumentedProcessKt.rdLogger`. + +## How to use log4j2 loggers + +See related document - [How to use loggers](../HowToUseLoggers.md). + +## Miscellaneous + +### Performance considerations + +`Debug` level is preferred in the most cases for developing. `Info` is sufficient for release. + +`Trace` log level for RD loggers(for ex. if you specify `Trace` for all loggers, or as default level for root logger) +will enable logging all technical send/receive event from protocol, +causing ~50mb additional logs per generation and ***heavily*** polluting log. This might be useful +when troubleshooting inter-process communication, but in all other cases prefer `Debug` level or +specify `Trace` level per logger explicitly. + +If your `Debug` level log message requires heavy string interpolation - wrap it in lambda, for example: +```kotlin +val someVeryBigDataStructure = VeryBigDataStructure() + +logger.debug("data structure representation - $someVeryBigDataStructure") // <---- interpolation +``` +In that code even though message uses `Debug` level, interpolation will always occur because +message is passed as a parameter, which are evaluated at call site. +In case logger is configured to `Info` level or higher - this means message will be built, but not logged, +resulting in unnecessary work, possibly causing performance issue. +Consider using lambdas: +```kotlin +// message will be created only if debug log level is available +logger.debug { "data structure representation - $someVeryBigDataStructure"} +``` + +Although now logs are sent from one process to another - performance penalties were not noticed. +Additional performance can be achieved playing with `bufferedIO` and `immediateFlush` properties in `log4j2.xml`. +For example, you can make following changes in `utbot-intellij`: +```xml + +``` + +This will reduce number of IO operations and use log4j2 buffer more efficiently. The cost it that +when process terminates - log4j2 terminates logging service before buffer is flushed, and +you will lose last portion of logs. This might be undesired behaviour in tests and debugging, +but probably acceptable in release. + +### Docker and Gradle + +To see logs in Gradle from console, Docker and CI - add following `build.gradle.kts`: +```kotlin +allprojects { + tasks { + withType { + testLogging.showStandardStreams = true + testLogging.showStackTraces = true + } + } +} +``` + +## Links + +Related topics: +1. [Multiprocess architecture](RD%20for%20UnitTestBot.md) +2. [Inter process debugging](./contributing/InterProcessDebugging.md) + +Log4j2: +2. [Architecture](https://logging.apache.org/log4j/2.x/manual/architecture.html) - overall log4j2 description. +2. [Layouts](https://logging.apache.org/log4j/2.x/manual/layouts.html) - how to format log messages. +UtBot uses `Pattern layout` everywhere. +3. [Appenders](https://logging.apache.org/log4j/2.x/manual/appenders.html) - about different ways to store log entries, +different storages for log entries and how to configure them. UtBot uses `Console`, `File` and `RollingFile` appenders. +4. [Configuration](https://logging.apache.org/log4j/2.x/manual/configuration.html) - what you can write in configuration file, +precise algorithm which file is used and many other useful info. It is **highly advised** to read `Additivity` part. \ No newline at end of file diff --git a/docs/RD for UnitTestBot.md b/docs/RD for UnitTestBot.md index c0827b9c96..3becab04da 100644 --- a/docs/RD for UnitTestBot.md +++ b/docs/RD for UnitTestBot.md @@ -228,7 +228,7 @@ Sometimes the _Instrumented process_ may unexpectedly die due to concrete execut - **Important**: do not add [`Rdgen`](https://mvnrepository.com/artifact/com.jetbrains.rd/rd-gen) as an implementation dependency — it breaks some JAR files as it contains `kotlin-compiler-embeddable`. 5. Logging & debugging: - - [Interprocess logging](./contributing/InterProcessLogging.md) + - [Interprocess logging](./InterProcessLogging.md) - [Interprocess debugging](./contributing/InterProcessDebugging.md) 6. Custom protocol marshaling types: do not spend time on it until `UtModels` get simpler, e.g. compatible with `kotlinx.serialization`. diff --git a/docs/contributing/InterProcessDebugging.md b/docs/contributing/InterProcessDebugging.md index a104cef082..864a14e865 100644 --- a/docs/contributing/InterProcessDebugging.md +++ b/docs/contributing/InterProcessDebugging.md @@ -35,7 +35,7 @@ To debug the _Engine process_ and the _Instrumented process_, you need to enable * **Discouraged**: you can change the options in the source file, but this will involve moderate project recompilation. -4. You can set additional options for the Java Debug Wire Protocol (JDWP) agent if debugging is enabled: +3. You can set additional options for the Java Debug Wire Protocol (JDWP) agent if debugging is enabled: * `engineProcessDebugPort` and `instrumentedProcessDebugPort` are the ports for debugging. Default values: @@ -62,7 +62,7 @@ To debug the _Engine process_ and the _Instrumented process_, you need to enable "-agentlib:jdwp=transport=dt_socket,server=n,suspend=n,quiet=y,address=12345" ``` See `org.utbot.intellij.plugin.process.EngineProcess.Companion.debugArgument` for switch implementation. -5. For information about logs, refer to the [Interprocess logging](InterProcessLogging.md) guide. +4. For information about logs, refer to the [Interprocess logging](../InterProcessLogging.md) guide. ### Run configurations for debugging the Engine process diff --git a/docs/contributing/InterProcessLogging.md b/docs/contributing/InterProcessLogging.md deleted file mode 100644 index 7377b09bbb..0000000000 --- a/docs/contributing/InterProcessLogging.md +++ /dev/null @@ -1,129 +0,0 @@ -# Logging of UnitTestBot Java - -## Table of content -- [IDE and Engine processes logs](#ide-and-engine-processes-logs) -- [RD logs](#rd-logs) -- [Instrumented process logs](#instrumented-process-logs) -- [Useful & misc](#useful--misc) - -## IDE and Engine processes logs - -Logging for both IDE and engine processes are based on [`utbot-intellij/log4j2.xml`](../../utbot-intellij/src/main/resources/log4j2.xml). - -### IDE process -Log configuration file is used as is, so if you want to configure logs in IDEA part - use it straight. -If you want to change log configuration in already built plugin - -use `Help > Diagnostic Tools > Debug Log Settings...` to change `log4j2.xml` configuration for plugin. - -### Engine process -Things are a bit more complicated here. -[`utbot-intellij/log4j2.xml`](../../utbot-intellij/src/main/resources/log4j2.xml) is copied in -UtBot temporary directory - `org.utbot.common.FileUtilKt.getUtBotTempDirectory`, -and then provided to JVM via following CLI switch: - ``` - -Dlog4j2.configurationFile=%configuration_file% - ``` - -where `%configuration_file%` will be either: -1. Modified copy of [`utbot-intellij/log4j2.xml`](../../utbot-intellij/src/main/resources/log4j2.xml) at UtBot temp directory. - - More precisely, there are 2 appenders in configuration file: - ```xml - - - - - - - - - ``` - By default `IdeaAppender` is used everywhere in file. - Idea catches plugin stdout log and wraps with own format, so in IDE log only `%msg` is logged. - - When working as engine process - temporary `log4j2.`xml would be created, in which - substring `ref="IdeaAppender"` will be replaced with `ref="EngineProcessAppender"`, - thus changing all appenders and log pattern, but preserving same categories and log level. - -2. Path from `UtSettings.engineProcessLogConfigFile`. - - This option allows to provide path to external Log4j2 configuration file instead of [`utbot-intellij/log4j2.xml`](../../utbot-intellij/src/main/resources/log4j2.xml). - At `~/.utbot/settings.properties` you can set path to custom configuration file, - which would apply for engine process, for example: - ``` - engineProcessLogConfigFile=C:\wrk\UTBotJava\engineProcessLog4j2.xml - ``` - This allows you to configure logs even for already built plugin, - you need only to restart IDE. - -## RD logs - -RD has its own logging system with different interface for logging, -see `com.jetbrains.rd.util.Logger`. - -Obtain logger via global function `getLogger()` from `rd-core.jar`. -By default, logger writes to `stderr` messages with `Warn` or higher log level, -and stdout for others. - -You can set which logger you want RD to use via `com.jetbrains.rd.util.ILoggerFactory` interface. -To set factory use `Logger.set(Lifetime, ILoggerFactory)` method, -for example this code overrides RD logs with `KotlinLogging`: - -```kotlin -Logger.set(object: ILoggerFactory { - override fun getLogger(category: String): Logger { - return KotlinLogging.logger(category) - } -}) -``` - -There are already 2 factories: -1. `UtRdConsoleLoggeFactory` - allows to write stdin/stdout in a format compatible with IDEA `logj42` configuration. -2. `UtRdKLoggerFactory` - smart adapter from RD to KotlinLogger loggers. - -### Details -Setup logger factory before any RD logs occurred, as you might lose some at RD start when loggers are configured to stdout. -The only way to configure RD logs - programmatically. There are no configuration files and/or services. - -Rd logger dynamically reconfigures as new logger factories arrive, see `com.jetbrains.rd.util.SwitchLogger` for -more details. - -Although RD produce ___A LOT OF LOGS___ - for 2-3 test generation logs -file will contain ~800mb of text related to logs, nearly all RD logs has `Trace` log level. You `Trace` with care! - -## Instrumented process logs - -Instrumented process have different logging due to class mocking limitation: -in some cases we want to mock loggers, and for that we would need to mock static function like `getLogger` etc. -In that case if we use that logger in UtBot - we might get incorrect version which in fact is mock. - -Instead, you should use hand-made logging based on RD as described in [RD logs section](#rd-logs). - -To configure instrumented process log level - use `UtSettings.instrumentedProcessLogLevel` property, -for example add in settings.properties: - -```kotlin -instrumentedProcessLogLevel=Debug -``` - -## Useful & misc - -### Log4j2 - -Sometimes your log entries might duplicate when using log4j or similar. -One of the reason might be *additivity*, read [here](https://logging.apache.org/log4j/2.x/manual/configuration.html#Additivity) -about how to solve it. - -Also, log4j2 automatically reconfigures when detects changes in log file. Default check timeout - 30s. - -### Output files for processes - -In `idea.log` there will be a path to engine process log file: -``` -Engine process log file - %path-to-engine-process-log% -``` - -And similarly in engine process log file will be entry: -``` -Instrumented process log file: %path-to-instrumented-process-log% -``` \ No newline at end of file diff --git a/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt b/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt index 3b789d25a8..db5831a382 100644 --- a/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt +++ b/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt @@ -32,7 +32,7 @@ import org.utbot.framework.plugin.api.TestCaseGenerator import org.utbot.framework.plugin.api.TreatOverflowAsError import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.services.JdkInfoDefaultProvider -import org.utbot.summary.summarize +import org.utbot.summary.summarizeAll import java.io.File import java.net.URLClassLoader import java.nio.file.Files @@ -161,8 +161,8 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) : mockStrategy, chosenClassesToMockAlways, generationTimeout - ).map { - if (sourceCodeFile != null) it.summarize(searchDirectory, sourceCodeFile.toFile()) else it + ).let { + if (sourceCodeFile != null) it.summarizeAll(searchDirectory, sourceCodeFile.toFile()) else it } diff --git a/utbot-core/src/main/kotlin/org/utbot/common/Logging.kt b/utbot-core/src/main/kotlin/org/utbot/common/Logging.kt index 99d1b7a7a2..bb24c9142c 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/Logging.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/Logging.kt @@ -6,11 +6,11 @@ import java.time.format.DateTimeFormatter val timeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS") val dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy_HH-mm-ss") -class LoggerWithLogMethod(val logger: KLogger, val logMethod: (() -> Any?) -> Unit) +class LoggerWithLogMethod(val logMethod: (() -> Any?) -> Unit) -fun KLogger.info(): LoggerWithLogMethod = LoggerWithLogMethod(this, this::info) -fun KLogger.debug(): LoggerWithLogMethod = LoggerWithLogMethod(this, this::debug) -fun KLogger.trace(): LoggerWithLogMethod = LoggerWithLogMethod(this, this::trace) +fun KLogger.info(): LoggerWithLogMethod = LoggerWithLogMethod(this::info) +fun KLogger.debug(): LoggerWithLogMethod = LoggerWithLogMethod(this::debug) +fun KLogger.trace(): LoggerWithLogMethod = LoggerWithLogMethod(this::trace) fun elapsedSecFrom(startNano: Long): String { val elapsedNano = System.nanoTime() - startNano @@ -35,16 +35,22 @@ fun elapsedSecFrom(startNano: Long): String { * You can use [closingComment] to add some result-depending comment to "Finished:" message. Special "" comment * is added if non local return happened in [block] */ -inline fun LoggerWithLogMethod.bracket( - msg: String, +inline fun LoggerWithLogMethod.measureTime( + crossinline msgBlock: () -> String, crossinline closingComment: (Result) -> Any? = { "" }, block: () -> T ): T { - logMethod { "Started: $msg" } + var msg = "" + + logMethod { + msg = msgBlock() + "Started: $msg" + } + val startNano = System.nanoTime() var alreadyLogged = false - var res : Maybe = Maybe.empty() + var res: Maybe = Maybe.empty() try { // Note: don't replace this one with runCatching, otherwise return from lambda breaks "finished" logging. res = Maybe(block()) @@ -63,20 +69,28 @@ inline fun LoggerWithLogMethod.bracket( } } -inline fun KLogger.catchException(block: () -> T): T? { +inline fun KLogger.catchException(message: String = "Isolated", block: () -> T): T? { return try { block() } catch (e: Throwable) { - this.error(e) { "Isolated" } + this.error(message, e) null } } -inline fun KLogger.logException(block: () -> T): T { +inline fun KLogger.logException(message: String = "Exception occurred", block: () -> T): T { return try { block() } catch (e: Throwable) { - this.error("Exception occurred", e) + this.error(message, e) throw e } +} + +inline fun silent(block: () -> T): T? { + return try { + block() + } catch (_: Throwable) { + null + } } \ No newline at end of file diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 35800bbe2a..5c1c66a637 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -1,6 +1,5 @@ package org.utbot.framework -import com.jetbrains.rd.util.LogLevel import java.io.File import mu.KotlinLogging import org.utbot.common.AbstractSettings @@ -324,11 +323,6 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS * @see [org.utbot.instrumentation.process.InstrumentedProcessRunner.cmds] */ var runInstrumentedProcessWithDebug by getBooleanProperty(false) - - /** - * Log level for instrumented process. - */ - var instrumentedProcessLogLevel by getEnumProperty(LogLevel.Info) // endregion /** diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt index f2c50278ca..c269af107e 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt @@ -1,8 +1,6 @@ package org.utbot.framework.plugin.api import org.utbot.framework.plugin.api.visible.UtStreamConsumingException -import java.io.File -import java.util.LinkedList sealed class UtExecutionResult @@ -38,8 +36,11 @@ data class UtStreamConsumingFailure( /** * unexpectedFail (when exceptions such as NPE, IOBE, etc. appear, but not thrown by a user, applies both for function under test and nested calls ) + * * expectedCheckedThrow (when function under test or nested call explicitly says that checked exception could be thrown and throws it) + * * expectedUncheckedThrow (when there is a throw statement for unchecked exception inside of function under test) + * * unexpectedUncheckedThrow (in case when there is unchecked exception thrown from nested call) */ data class UtExplicitlyThrownException( @@ -60,25 +61,13 @@ data class UtTimeoutException(override val exception: TimeoutException) : UtExec * Indicates failure in concrete execution. * For now it is explicitly throwing by ConcreteExecutor in case instrumented process death. */ -class InstrumentedProcessDeathException(cause: Throwable, errorFile: File, val processStdout: List) : +class InstrumentedProcessDeathException(cause: Throwable) : Exception( buildString { appendLine() appendLine("----------------------------------------") appendLine("The instrumented process is dead") appendLine("Cause:\n${cause.message}") - appendLine("Last 1000 lines of the error log ${errorFile.absolutePath}:") - appendLine("----------------------------------------") - errorFile.useLines { lines -> - val lastLines = LinkedList() - for (line in lines) { - lastLines.add(line) - if (lastLines.size > 1000) { - lastLines.removeFirst() - } - } - lastLines.forEach { appendLine(it) } - } appendLine("----------------------------------------") }, cause @@ -105,4 +94,4 @@ inline fun UtExecutionResult.onFailure(action: (exception: Throwable) -> Unit): fun UtExecutionResult.exceptionOrNull(): Throwable? = when (this) { is UtExecutionFailure -> rootCauseException is UtExecutionSuccess -> null -} +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index f7fc5ca2cf..ef6202d621 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -8,7 +8,7 @@ import org.utbot.analytics.EngineAnalyticsContext import org.utbot.analytics.FeatureProcessor import org.utbot.analytics.Predictors import org.utbot.api.exception.UtMockAssumptionViolatedException -import org.utbot.common.bracket +import org.utbot.common.measureTime import org.utbot.common.debug import org.utbot.engine.MockStrategy.NO_MOCKS import org.utbot.engine.pc.* @@ -230,7 +230,7 @@ class UtBotSymbolicEngine( logger.trace { "executing $state concretely..." } - logger.debug().bracket("concolicStrategy<$methodUnderTest>: execute concretely") { + logger.debug().measureTime({ "concolicStrategy<$methodUnderTest>: execute concretely"} ) { val resolver = Resolver( hierarchy, state.memory, @@ -252,7 +252,7 @@ class UtBotSymbolicEngine( if (concreteExecutionResult.violatesUtMockAssumption()) { logger.debug { "Generated test case violates the UtMock assumption: $concreteExecutionResult" } - return@bracket + return@measureTime } val concreteUtExecution = UtSymbolicExecution( @@ -511,7 +511,7 @@ class UtBotSymbolicEngine( //It's possible that symbolic and concrete stateAfter/results are diverged. //So we trust concrete results more. try { - logger.debug().bracket("processResult<$methodUnderTest>: concrete execution") { + logger.debug().measureTime({ "processResult<$methodUnderTest>: concrete execution" } ) { //this can throw CancellationException val concreteExecutionResult = concreteExecutor.executeConcretely( diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt index 1297636146..7d98030f0c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt @@ -3,7 +3,7 @@ package org.utbot.engine.pc import org.utbot.analytics.IncrementalData import org.utbot.analytics.Predictors import org.utbot.analytics.learnOn -import org.utbot.common.bracket +import org.utbot.common.measureTime import org.utbot.common.md5 import org.utbot.common.trace import org.utbot.engine.Eq @@ -222,7 +222,7 @@ data class UtSolver constructor( val translatedAssumes = assumption.constraints.translate() - val statusHolder = logger.trace().bracket("High level check(): ", { it }) { + val statusHolder = logger.trace().measureTime({ "High level check(): " }, { it }) { Predictors.smtIncremental.learnOn(IncrementalData(constraints.hard, hardConstraintsNotYetAddedToZ3Solver)) { hardConstraintsNotYetAddedToZ3Solver.forEach { z3Solver.add(translator.translate(it) as BoolExpr) } @@ -255,7 +255,7 @@ data class UtSolver constructor( val assumptionsInUnsatCore = mutableListOf() while (true) { - val res = logger.trace().bracket("Low level check(): ", { it }) { + val res = logger.trace().measureTime({ "Low level check(): " }, { it }) { val constraintsToCheck = translatedSoft.keys + translatedAssumptions.keys z3Solver.check(*constraintsToCheck.toTypedArray()) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt index e3eff3556b..fba77160ec 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt @@ -1899,22 +1899,6 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte methodType = this@CgMethodConstructor.methodType val docComment = currentExecution?.summary?.map { convertDocToCg(it) }?.toMutableList() ?: mutableListOf() - // add JVM crash report path if exists - if (result is UtConcreteExecutionFailure) { - result.extractJvmReportPathOrNull()?.let { - val jvmReportDocumentation = CgDocRegularStmt(getJvmReportDocumentation(it)) - val lastTag = docComment.lastOrNull() - // if the last statement is a
 tag, put the path inside it
-                    if (lastTag == null || lastTag !is CgDocPreTagStatement) {
-                        docComment += jvmReportDocumentation
-                    } else {
-                        val tagContent = lastTag.content
-                        docComment.removeLast()
-                        docComment += CgDocPreTagStatement(tagContent + jvmReportDocumentation)
-                    }
-                }
-            }
-
             documentation = CgDocumentationComment(docComment)
             documentation = if (parameterized) {
                 CgDocumentationComment(text = null)
@@ -1970,18 +1954,6 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
         return CgSimpleRegion("Errors report for ${executable.name}", listOf(errorTestMethod))
     }
 
-    private fun getJvmReportDocumentation(jvmReportPath: String): String {
-        val pureJvmReportPath = jvmReportPath.substringAfter("# ")
-
-        // \n is here because IntellijIdea cannot process other separators
-        return PathUtil.toHtmlLinkTag(PathUtil.replaceSeparator(pureJvmReportPath), fileName = "JVM crash report") + "\n"
-    }
-
-    private fun UtConcreteExecutionFailure.extractJvmReportPathOrNull(): String? =
-        exception.processStdout.singleOrNull {
-            "hs_err_pid" in it
-        }
-
     private fun CgExecutableCall.wrapReflectiveCall() {
         +tryBlock {
             +this@wrapReflectiveCall
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt
index f3e04bd7b6..454c3e5865 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt
@@ -10,7 +10,7 @@ import kotlinx.coroutines.launch
 import kotlinx.coroutines.yield
 import mu.KLogger
 import mu.KotlinLogging
-import org.utbot.common.bracket
+import org.utbot.common.measureTime
 import org.utbot.common.runBlockingWithCancellationPredicate
 import org.utbot.common.runIgnoringCancellationException
 import org.utbot.common.trace
@@ -79,7 +79,7 @@ open class TestCaseGenerator(
                 System.setProperty(kotlinx.coroutines.DEBUG_PROPERTY_NAME, kotlinx.coroutines.DEBUG_PROPERTY_VALUE_OFF)
             }
 
-            timeoutLogger.trace().bracket("Soot initialization") {
+            timeoutLogger.trace().measureTime({ "Soot initialization"} ) {
                 SootUtils.runSoot(buildDirs, classpath, forceSootReload, jdkInfo)
             }
 
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt
index 616c8a871d..f870126799 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt
@@ -9,7 +9,7 @@ import org.utbot.framework.plugin.api.UtMethodTestSet
 import org.utbot.framework.plugin.api.util.id
 import org.utbot.sarif.SarifReport
 import org.utbot.sarif.SourceFindingStrategy
-import org.utbot.summary.summarize
+import org.utbot.summary.summarizeAll
 import java.io.File
 import java.nio.file.Path
 
@@ -59,9 +59,7 @@ class GenerateTestsAndSarifReportFacade(
                 sarifProperties.mockStrategy,
                 sarifProperties.classesToMockAlways,
                 sarifProperties.generationTimeout
-            ).map {
-                it.summarize(workingDirectory, targetClass.sourceCodeFile)
-            }
+            ).summarizeAll(workingDirectory, targetClass.sourceCodeFile)
 
     private fun generateTestCode(targetClass: TargetClassWrapper, testSets: List): String =
         initializeCodeGenerator(targetClass)
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt
index 01c880caea..49fc11bab7 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt
@@ -6,10 +6,7 @@ import com.jetbrains.rd.util.lifetime.Lifetime
 import kotlinx.coroutines.runBlocking
 import mu.KotlinLogging
 import org.utbot.analytics.AnalyticsConfigureUtil
-import org.utbot.common.AbstractSettings
-import org.utbot.common.allNestedClasses
-import org.utbot.common.appendHtmlLine
-import org.utbot.common.nameOfPackage
+import org.utbot.common.*
 import org.utbot.engine.util.mockListeners.ForceMockListener
 import org.utbot.engine.util.mockListeners.ForceStaticMockListener
 import org.utbot.framework.codegen.*
@@ -40,7 +37,7 @@ import org.utbot.rd.generated.settingsModel
 import org.utbot.rd.loggers.UtRdKLoggerFactory
 import org.utbot.sarif.RdSourceFindingStrategyFacade
 import org.utbot.sarif.SarifReport
-import org.utbot.summary.summarize
+import org.utbot.summary.summarizeAll
 import java.io.File
 import java.net.URLClassLoader
 import java.nio.file.Paths
@@ -50,21 +47,25 @@ import kotlin.time.Duration.Companion.seconds
 private val messageFromMainTimeoutMillis = 120.seconds
 private val logger = KotlinLogging.logger {}
 
+@Suppress("unused")
+object EngineProcessMain
+
 // use log4j2.configurationFile property to set log4j configuration
 suspend fun main(args: Array) = runBlocking {
+    Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger))
+
+    logger.info("-----------------------------------------------------------------------")
+    logger.info("-------------------NEW ENGINE PROCESS STARTED--------------------------")
+    logger.info("-----------------------------------------------------------------------")
     // 0 - auto port for server, should not be used here
     val port = findRdPort(args)
 
-    Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger))
 
     ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(port) {
         AbstractSettings.setupFactory(RdSettingsContainerFactory(protocol.settingsModel))
         val kryoHelper = KryoHelper(lifetime)
         engineProcessModel.setup(kryoHelper, it, protocol)
     }
-    logger.info { "runBlocking ending" }
-}.also {
-    logger.info { "runBlocking ended" }
 }
 
 private lateinit var testGenerator: TestCaseGenerator
@@ -74,12 +75,12 @@ private var idCounter: Long = 0
 
 private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatchdog, realProtocol: IProtocol) {
     val model = this
-    watchdog.wrapActiveCall(setupUtContext) { params ->
+    watchdog.measureTimeForActiveCall(setupUtContext, "UtContext setup") { params ->
         UtContext.setUtContext(UtContext(URLClassLoader(params.classpathForUrlsClassloader.map {
             File(it).toURI().toURL()
         }.toTypedArray())))
     }
-    watchdog.wrapActiveCall(createTestGenerator) { params ->
+    watchdog.measureTimeForActiveCall(createTestGenerator, "Creating Test Generator") { params ->
         AnalyticsConfigureUtil.configureML()
         Instrumenter.adapter = RdInstrumenter(realProtocol.rdInstrumenterAdapter)
         testGenerator = TestCaseGenerator(buildDirs = params.buildDir.map { Paths.get(it) },
@@ -92,37 +93,38 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
                 }
             })
     }
-    watchdog.wrapActiveCall(generate) { params ->
-        val mockFrameworkInstalled = params.mockInstalled
-        val conflictTriggers = ConflictTriggers(kryoHelper.readObject(params.conflictTriggers))
-        if (!mockFrameworkInstalled) {
-            ForceMockListener.create(testGenerator, conflictTriggers, cancelJob = true)
-        }
-        val staticsMockingConfigured = params.staticsMockingIsConfigureda
-        if (!staticsMockingConfigured) {
-            ForceStaticMockListener.create(testGenerator, conflictTriggers, cancelJob = true)
-        }
-        val result = testGenerator.generate(kryoHelper.readObject(params.methods),
-            MockStrategyApi.valueOf(params.mockStrategy),
-            kryoHelper.readObject(params.chosenClassesToMockAlways),
-            params.timeout,
-            generate = testFlow {
-                generationTimeout = params.generationTimeout
-                isSymbolicEngineEnabled = params.isSymbolicEngineEnabled
-                isFuzzingEnabled = params.isFuzzingEnabled
-                fuzzingValue = params.fuzzingValue
-            })
-            .apply { logger.info("generation ended, starting summarization, result size: ${this.size}") }
-            .map { it.summarize(Paths.get(params.searchDirectory), sourceFile = null) }
-            .apply { logger.info("summarization ended") }
-            .filterNot { it.executions.isEmpty() && it.errors.isEmpty() }
+    watchdog.measureTimeForActiveCall(generate, "Generating tests") { params ->
+        val methods: List = kryoHelper.readObject(params.methods)
+        logger.debug().measureTime({ "starting generation for ${methods.size} methods, starting with ${methods.first()}" }) {
+            val mockFrameworkInstalled = params.mockInstalled
+            val conflictTriggers = ConflictTriggers(kryoHelper.readObject(params.conflictTriggers))
+            if (!mockFrameworkInstalled) {
+                ForceMockListener.create(testGenerator, conflictTriggers, cancelJob = true)
+            }
+            val staticsMockingConfigured = params.staticsMockingIsConfigureda
+            if (!staticsMockingConfigured) {
+                ForceStaticMockListener.create(testGenerator, conflictTriggers, cancelJob = true)
+            }
+            val result = testGenerator.generate(methods,
+                MockStrategyApi.valueOf(params.mockStrategy),
+                kryoHelper.readObject(params.chosenClassesToMockAlways),
+                params.timeout,
+                generate = testFlow {
+                    generationTimeout = params.generationTimeout
+                    isSymbolicEngineEnabled = params.isSymbolicEngineEnabled
+                    isFuzzingEnabled = params.isFuzzingEnabled
+                    fuzzingValue = params.fuzzingValue
+                })
+                .summarizeAll(Paths.get(params.searchDirectory), null)
+                .filterNot { it.executions.isEmpty() && it.errors.isEmpty() }
 
-        val id = ++idCounter
+            val id = ++idCounter
 
-        testSets[id] = result
-        GenerateResult(result.size, id)
+            testSets[id] = result
+            GenerateResult(result.size, id)
+        }
     }
-    watchdog.wrapActiveCall(render) { params ->
+    watchdog.measureTimeForActiveCall(render, "Rendering tests") { params ->
         val testFramework = testFrameworkByName(params.testFramework)
         val staticMocking = if (params.staticsMocking.startsWith("No")) {
             NoStaticMocking
@@ -147,37 +149,32 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
             enableTestsTimeout = params.enableTestsTimeout,
             testClassPackageName = params.testClassPackageName
         )
-        codeGenerator.generateAsStringWithTestReport(testSets[testSetsId]!!)
-            .let {
-                testGenerationReports.add(it.testsGenerationReport)
-                RenderResult(it.generatedCode, it.utilClassKind?.javaClass?.simpleName)
-            }
+        codeGenerator.generateAsStringWithTestReport(testSets[testSetsId]!!).let {
+            testGenerationReports.add(it.testsGenerationReport)
+            RenderResult(it.generatedCode, it.utilClassKind?.javaClass?.simpleName)
+        }
     }
-    watchdog.wrapActiveCall(stopProcess) { watchdog.stopProtocol() }
-    watchdog.wrapActiveCall(obtainClassId) { canonicalName ->
+    watchdog.measureTimeForActiveCall(obtainClassId, "Obtain class id in UtContext") { canonicalName ->
         kryoHelper.writeObject(UtContext.currentContext()!!.classLoader.loadClass(canonicalName).id)
     }
-    watchdog.wrapActiveCall(findMethodsInClassMatchingSelected) { params ->
+    watchdog.measureTimeForActiveCall(findMethodsInClassMatchingSelected, "Find methods in Class") { params ->
         val classId = kryoHelper.readObject(params.classId)
         val selectedMethodDescriptions =
             params.methodDescriptions.map { MethodDescription(it.name, it.containingClass, it.parametersTypes) }
         FindMethodsInClassMatchingSelectedResult(kryoHelper.writeObject(classId.jClass.allNestedClasses.flatMap { clazz ->
             clazz.id.allMethods.mapNotNull { it.method.kotlinFunction }
                 .sortedWith(compareBy { selectedMethodDescriptions.indexOf(it.methodDescription()) })
-                .filter { it.methodDescription().normalized() in selectedMethodDescriptions }
-                .map { it.executableId }
+                .filter { it.methodDescription().normalized() in selectedMethodDescriptions }.map { it.executableId }
         }))
     }
-    watchdog.wrapActiveCall(findMethodParamNames) { params ->
+    watchdog.measureTimeForActiveCall(findMethodParamNames, "Find method parameters names") { params ->
         val classId = kryoHelper.readObject(params.classId)
         val byMethodDescription = kryoHelper.readObject>>(params.bySignature)
-        FindMethodParamNamesResult(kryoHelper.writeObject(
-            classId.jClass.allNestedClasses.flatMap { clazz -> clazz.id.allMethods.mapNotNull { it.method.kotlinFunction } }
-                .mapNotNull { method -> byMethodDescription[method.methodDescription()]?.let { params -> method.executableId to params } }
-                .toMap()
-        ))
+        FindMethodParamNamesResult(kryoHelper.writeObject(classId.jClass.allNestedClasses.flatMap { clazz -> clazz.id.allMethods.mapNotNull { it.method.kotlinFunction } }
+            .mapNotNull { method -> byMethodDescription[method.methodDescription()]?.let { params -> method.executableId to params } }
+            .toMap()))
     }
-    watchdog.wrapActiveCall(writeSarifReport) { params ->
+    watchdog.measureTimeForActiveCall(writeSarifReport, "Writing Sarif report") { params ->
         val reportFilePath = Paths.get(params.reportFilePath)
         reportFilePath.parent.toFile().mkdirs()
         val sarifReport = SarifReport(
@@ -188,12 +185,12 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
         reportFilePath.toFile().writeText(sarifReport)
         sarifReport
     }
-    watchdog.wrapActiveCall(generateTestReport) { params ->
+    watchdog.measureTimeForActiveCall(generateTestReport, "Generating test report") { params ->
         val eventLogMessage = params.eventLogMessage
         val testPackageName: String? = params.testPackageName
         var hasWarnings = false
         val reports = testGenerationReports
-        if (reports.isEmpty()) return@wrapActiveCall GenerateTestReportResult("No tests were generated", null, true)
+        if (reports.isEmpty()) return@measureTimeForActiveCall GenerateTestReportResult("No tests were generated", null, true)
         val isMultiPackage = params.isMultiPackage
         val (notifyMessage, statistics) = if (reports.size == 1) {
             val report = reports.first()
@@ -202,15 +199,13 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
             val message = buildString {
                 appendHtmlLine(report.toString(isShort = true))
 
-                val classUnderTestPackageName =
-                    report.classUnderTest.java.nameOfPackage
+                val classUnderTestPackageName = report.classUnderTest.java.nameOfPackage
 
-                destinationWarningMessage(testPackageName, classUnderTestPackageName)
-                    ?.let {
-                        hasWarnings = true
-                        appendHtmlLine(it)
-                        appendHtmlLine()
-                    }
+                destinationWarningMessage(testPackageName, classUnderTestPackageName)?.let {
+                    hasWarnings = true
+                    appendHtmlLine(it)
+                    appendHtmlLine()
+                }
                 eventLogMessage?.let {
                     appendHtmlLine(it)
                 }
@@ -232,13 +227,11 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
                 // TODO maybe add statistics info here
 
                 for (report in reports) {
-                    val classUnderTestPackageName =
-                        report.classUnderTest.java.nameOfPackage
+                    val classUnderTestPackageName = report.classUnderTest.java.nameOfPackage
 
                     hasWarnings = hasWarnings || report.hasWarnings
                     if (!isMultiPackage) {
-                        val destinationWarning =
-                            destinationWarningMessage(testPackageName, classUnderTestPackageName)
+                        val destinationWarning = destinationWarningMessage(testPackageName, classUnderTestPackageName)
                         if (destinationWarning != null) {
                             hasWarnings = true
                             appendHtmlLine(destinationWarning)
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
index dc49b4d12e..76ac332ac6 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
@@ -15,7 +15,7 @@ import kotlin.jvm.JvmStatic
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:30]
+ * #### Generated from [EngineProcessModel.kt:31]
  */
 class EngineProcessModel private constructor(
     private val _setupUtContext: RdCall,
@@ -23,7 +23,6 @@ class EngineProcessModel private constructor(
     private val _isCancelled: RdCall,
     private val _generate: RdCall,
     private val _render: RdCall,
-    private val _stopProcess: RdCall,
     private val _obtainClassId: RdCall,
     private val _findMethodsInClassMatchingSelected: RdCall,
     private val _findMethodParamNames: RdCall,
@@ -71,9 +70,9 @@ class EngineProcessModel private constructor(
                 bind(lifetime, protocol, "EngineProcessModel")
             }
         }
-
-
-        const val serializationHash = -6219345436129699239L
+        
+        
+        const val serializationHash = 5025678608993948804L
         
     }
     override val serializersOwner: ISerializersOwner get() = EngineProcessModel
@@ -85,7 +84,6 @@ class EngineProcessModel private constructor(
     val isCancelled: RdCall get() = _isCancelled
     val generate: RdCall get() = _generate
     val render: RdCall get() = _render
-    val stopProcess: RdCall get() = _stopProcess
     val obtainClassId: RdCall get() = _obtainClassId
     val findMethodsInClassMatchingSelected: RdCall get() = _findMethodsInClassMatchingSelected
     val findMethodParamNames: RdCall get() = _findMethodParamNames
@@ -99,7 +97,6 @@ class EngineProcessModel private constructor(
         _isCancelled.async = true
         _generate.async = true
         _render.async = true
-        _stopProcess.async = true
         _obtainClassId.async = true
         _findMethodsInClassMatchingSelected.async = true
         _findMethodParamNames.async = true
@@ -113,7 +110,6 @@ class EngineProcessModel private constructor(
         bindableChildren.add("isCancelled" to _isCancelled)
         bindableChildren.add("generate" to _generate)
         bindableChildren.add("render" to _render)
-        bindableChildren.add("stopProcess" to _stopProcess)
         bindableChildren.add("obtainClassId" to _obtainClassId)
         bindableChildren.add("findMethodsInClassMatchingSelected" to _findMethodsInClassMatchingSelected)
         bindableChildren.add("findMethodParamNames" to _findMethodParamNames)
@@ -129,7 +125,6 @@ class EngineProcessModel private constructor(
         RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Bool),
         RdCall(GenerateParams, GenerateResult),
         RdCall(RenderParams, RenderResult),
-        RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Void),
         RdCall(FrameworkMarshallers.String, FrameworkMarshallers.ByteArray),
         RdCall(FindMethodsInClassMatchingSelectedArguments, FindMethodsInClassMatchingSelectedResult),
         RdCall(FindMethodParamNamesArguments, FindMethodParamNamesResult),
@@ -148,7 +143,6 @@ class EngineProcessModel private constructor(
             print("isCancelled = "); _isCancelled.print(printer); println()
             print("generate = "); _generate.print(printer); println()
             print("render = "); _render.print(printer); println()
-            print("stopProcess = "); _stopProcess.print(printer); println()
             print("obtainClassId = "); _obtainClassId.print(printer); println()
             print("findMethodsInClassMatchingSelected = "); _findMethodsInClassMatchingSelected.print(printer); println()
             print("findMethodParamNames = "); _findMethodParamNames.print(printer); println()
@@ -165,7 +159,6 @@ class EngineProcessModel private constructor(
             _isCancelled.deepClonePolymorphic(),
             _generate.deepClonePolymorphic(),
             _render.deepClonePolymorphic(),
-            _stopProcess.deepClonePolymorphic(),
             _obtainClassId.deepClonePolymorphic(),
             _findMethodsInClassMatchingSelected.deepClonePolymorphic(),
             _findMethodParamNames.deepClonePolymorphic(),
@@ -180,7 +173,7 @@ val IProtocol.engineProcessModel get() = getOrCreateExtension(EngineProcessModel
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:100]
+ * #### Generated from [EngineProcessModel.kt:101]
  */
 data class FindMethodParamNamesArguments (
     val classId: ByteArray,
@@ -243,7 +236,7 @@ data class FindMethodParamNamesArguments (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:104]
+ * #### Generated from [EngineProcessModel.kt:105]
  */
 data class FindMethodParamNamesResult (
     val paramNames: ByteArray
@@ -300,7 +293,7 @@ data class FindMethodParamNamesResult (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:93]
+ * #### Generated from [EngineProcessModel.kt:94]
  */
 data class FindMethodsInClassMatchingSelectedArguments (
     val classId: ByteArray,
@@ -312,7 +305,7 @@ data class FindMethodsInClassMatchingSelectedArguments (
         override val _type: KClass = FindMethodsInClassMatchingSelectedArguments::class
         
         @Suppress("UNCHECKED_CAST")
-        override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): FindMethodsInClassMatchingSelectedArguments {
+        override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): FindMethodsInClassMatchingSelectedArguments  {
             val classId = buffer.readByteArray()
             val methodDescriptions = buffer.readList { MethodDescription.read(ctx, buffer) }
             return FindMethodsInClassMatchingSelectedArguments(classId, methodDescriptions)
@@ -335,7 +328,7 @@ data class FindMethodsInClassMatchingSelectedArguments (
         if (other == null || other::class != this::class) return false
         
         other as FindMethodsInClassMatchingSelectedArguments
-
+        
         if (!(classId contentEquals other.classId)) return false
         if (methodDescriptions != other.methodDescriptions) return false
         
@@ -344,8 +337,8 @@ data class FindMethodsInClassMatchingSelectedArguments (
     //hash code trait
     override fun hashCode(): Int  {
         var __r = 0
-        __r = __r * 31 + classId.contentHashCode()
-        __r = __r * 31 + methodDescriptions.hashCode()
+        __r = __r*31 + classId.contentHashCode()
+        __r = __r*31 + methodDescriptions.hashCode()
         return __r
     }
     //pretty print
@@ -363,7 +356,7 @@ data class FindMethodsInClassMatchingSelectedArguments (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:97]
+ * #### Generated from [EngineProcessModel.kt:98]
  */
 data class FindMethodsInClassMatchingSelectedResult (
     val executableIds: ByteArray
@@ -420,7 +413,7 @@ data class FindMethodsInClassMatchingSelectedResult (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:42]
+ * #### Generated from [EngineProcessModel.kt:43]
  */
 data class GenerateParams (
     val mockInstalled: Boolean,
@@ -543,7 +536,7 @@ data class GenerateParams (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:60]
+ * #### Generated from [EngineProcessModel.kt:61]
  */
 data class GenerateResult (
     val notEmptyCases: Int,
@@ -606,7 +599,7 @@ data class GenerateResult (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:112]
+ * #### Generated from [EngineProcessModel.kt:113]
  */
 data class GenerateTestReportArgs (
     val eventLogMessage: String?,
@@ -699,7 +692,7 @@ data class GenerateTestReportArgs (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:121]
+ * #### Generated from [EngineProcessModel.kt:122]
  */
 data class GenerateTestReportResult (
     val notifyMessage: String,
@@ -768,7 +761,7 @@ data class GenerateTestReportResult (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:31]
+ * #### Generated from [EngineProcessModel.kt:32]
  */
 data class JdkInfo (
     val path: String,
@@ -831,64 +824,61 @@ data class JdkInfo (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:88]
+ * #### Generated from [EngineProcessModel.kt:89]
  */
-data class MethodDescription(
+data class MethodDescription (
     val name: String,
     val containingClass: String?,
     val parametersTypes: List
 ) : IPrintable {
     //companion
-
+    
     companion object : IMarshaller {
         override val _type: KClass = MethodDescription::class
-
+        
         @Suppress("UNCHECKED_CAST")
-        override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): MethodDescription {
+        override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): MethodDescription  {
             val name = buffer.readString()
             val containingClass = buffer.readNullable { buffer.readString() }
             val parametersTypes = buffer.readList { buffer.readNullable { buffer.readString() } }
             return MethodDescription(name, containingClass, parametersTypes)
         }
-
-        override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: MethodDescription) {
+        
+        override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: MethodDescription)  {
             buffer.writeString(value.name)
             buffer.writeNullable(value.containingClass) { buffer.writeString(it) }
             buffer.writeList(value.parametersTypes) { v -> buffer.writeNullable(v) { buffer.writeString(it) } }
         }
-
-
+        
+        
     }
-
     //fields
     //methods
     //initializer
     //secondary constructor
     //equals trait
-    override fun equals(other: Any?): Boolean {
+    override fun equals(other: Any?): Boolean  {
         if (this === other) return true
         if (other == null || other::class != this::class) return false
-
+        
         other as MethodDescription
-
+        
         if (name != other.name) return false
         if (containingClass != other.containingClass) return false
         if (parametersTypes != other.parametersTypes) return false
-
+        
         return true
     }
-
     //hash code trait
-    override fun hashCode(): Int {
+    override fun hashCode(): Int  {
         var __r = 0
-        __r = __r * 31 + name.hashCode()
-        __r = __r * 31 + if (containingClass != null) containingClass.hashCode() else 0
-        __r = __r * 31 + parametersTypes.hashCode()
+        __r = __r*31 + name.hashCode()
+        __r = __r*31 + if (containingClass != null) containingClass.hashCode() else 0
+        __r = __r*31 + parametersTypes.hashCode()
         return __r
     }
-
     //pretty print
-    override fun print(printer: PrettyPrinter) {
+    override fun print(printer: PrettyPrinter)  {
         printer.println("MethodDescription (")
         printer.indent {
             print("name = "); name.print(printer); println()
@@ -903,9 +893,9 @@ data class MethodDescription(
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:64]
+ * #### Generated from [EngineProcessModel.kt:65]
  */
-data class RenderParams(
+data class RenderParams (
     val testSetsId: Long,
     val classUnderTest: ByteArray,
     val paramNames: ByteArray,
@@ -1044,7 +1034,7 @@ data class RenderParams(
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:81]
+ * #### Generated from [EngineProcessModel.kt:82]
  */
 data class RenderResult (
     val generatedCode: String,
@@ -1107,7 +1097,7 @@ data class RenderResult (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:85]
+ * #### Generated from [EngineProcessModel.kt:86]
  */
 data class SetupContextParams (
     val classpathForUrlsClassloader: List
@@ -1164,7 +1154,7 @@ data class SetupContextParams (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:36]
+ * #### Generated from [EngineProcessModel.kt:37]
  */
 data class TestGeneratorParams (
     val buildDir: Array,
@@ -1239,7 +1229,7 @@ data class TestGeneratorParams (
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:107]
+ * #### Generated from [EngineProcessModel.kt:108]
  */
 data class WriteSarifReportArguments (
     val testSetsId: Long,
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessRoot.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessRoot.Generated.kt
index 0b333c3a50..dd4a221774 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessRoot.Generated.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessRoot.Generated.kt
@@ -15,7 +15,7 @@ import kotlin.jvm.JvmStatic
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:5]
+ * #### Generated from [EngineProcessModel.kt:6]
  */
 class EngineProcessRoot private constructor(
 ) : RdExtBase() {
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt
index f4fbc6f17d..c9c56ee162 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt
@@ -15,7 +15,7 @@ import kotlin.jvm.JvmStatic
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:7]
+ * #### Generated from [EngineProcessModel.kt:8]
  */
 class RdInstrumenterAdapter private constructor(
     private val _computeSourceFileByClass: RdCall
@@ -97,7 +97,7 @@ val IProtocol.rdInstrumenterAdapter get() = getOrCreateExtension(RdInstrumenterA
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:8]
+ * #### Generated from [EngineProcessModel.kt:9]
  */
 data class ComputeSourceFileByClassArguments (
     val canonicalClassName: String
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt
index 598ae1e086..11324e77f2 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt
@@ -15,7 +15,7 @@ import kotlin.jvm.JvmStatic
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:16]
+ * #### Generated from [EngineProcessModel.kt:17]
  */
 class RdSourceFindingStrategy private constructor(
     private val _testsRelativePath: RdCall,
@@ -111,7 +111,7 @@ val IProtocol.rdSourceFindingStrategy get() = getOrCreateExtension(RdSourceFindi
 
 
 /**
- * #### Generated from [EngineProcessModel.kt:17]
+ * #### Generated from [EngineProcessModel.kt:18]
  */
 data class SourceStrategyMethodArgs (
     val testSetId: Long,
diff --git a/utbot-gradle/src/main/kotlin/org/utbot/gradle/plugin/GenerateTestsAndSarifReportTask.kt b/utbot-gradle/src/main/kotlin/org/utbot/gradle/plugin/GenerateTestsAndSarifReportTask.kt
index 2f7bac2262..1e5081a6ba 100644
--- a/utbot-gradle/src/main/kotlin/org/utbot/gradle/plugin/GenerateTestsAndSarifReportTask.kt
+++ b/utbot-gradle/src/main/kotlin/org/utbot/gradle/plugin/GenerateTestsAndSarifReportTask.kt
@@ -1,137 +1,137 @@
-package org.utbot.gradle.plugin
-
-import mu.KLogger
-import org.gradle.api.DefaultTask
-import org.gradle.api.tasks.TaskAction
-import org.utbot.common.bracket
-import org.utbot.common.debug
-import org.utbot.framework.plugin.api.TestCaseGenerator
-import org.utbot.framework.plugin.api.util.UtContext
-import org.utbot.framework.plugin.api.util.withUtContext
-import org.utbot.framework.plugin.sarif.GenerateTestsAndSarifReportFacade
-import org.utbot.gradle.plugin.extension.SarifGradleExtensionProvider
-import org.utbot.gradle.plugin.wrappers.GradleProjectWrapper
-import org.utbot.gradle.plugin.wrappers.SourceFindingStrategyGradle
-import org.utbot.gradle.plugin.wrappers.SourceSetWrapper
-import org.utbot.framework.plugin.sarif.TargetClassWrapper
-import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
-import java.io.File
-import java.net.URLClassLoader
-import javax.inject.Inject
-
-/**
- * The main class containing the entry point [generateTestsAndSarifReport].
- *
- * [Documentation](https://docs.gradle.org/current/userguide/custom_tasks.html)
- */
-open class GenerateTestsAndSarifReportTask @Inject constructor(
-    private val sarifProperties: SarifGradleExtensionProvider
-) : DefaultTask() {
-
-    init {
-        group = "utbot"
-        description = "Generate a SARIF report"
-    }
-
-    /**
-     * Entry point: called when the user starts this gradle task.
-     */
-    @TaskAction
-    fun generateTestsAndSarifReport() {
-        // the user specifies the parameters using "-Pname=value"
-        sarifProperties.taskParameters = project.gradle.startParameter.projectProperties
-        val rootGradleProject = try {
-            GradleProjectWrapper(project, sarifProperties)
-        } catch (t: Throwable) {
-            logger.error(t) { "Unexpected error while configuring the gradle task" }
-            return
-        }
-        try {
-
-            generateForProjectRecursively(rootGradleProject)
-            GenerateTestsAndSarifReportFacade.mergeReports(
-                sarifReports = rootGradleProject.collectReportsRecursively(),
-                mergedSarifReportFile = rootGradleProject.sarifReportFile
-            )
-        } catch (t: Throwable) {
-            logger.error(t) { "Unexpected error while generating SARIF report" }
-            return
-        }
-    }
-
-    // internal
-
-    // overwriting the getLogger() function from the DefaultTask
-    private val logger: KLogger = org.utbot.gradle.plugin.logger
-
-    private val dependencyPaths by lazy {
-        val thisClassLoader = this::class.java.classLoader as? URLClassLoader
-            ?: return@lazy System.getProperty("java.class.path")
-        thisClassLoader.urLs.joinToString(File.pathSeparator) { it.path }
-    }
-
-    /**
-     * Generates tests and a SARIF report for classes in the [gradleProject] and in all its child projects.
-     */
-    private fun generateForProjectRecursively(gradleProject: GradleProjectWrapper) {
-        gradleProject.sourceSets.forEach { sourceSet ->
-            generateForSourceSet(sourceSet)
-        }
-        gradleProject.childProjects.forEach { childProject ->
-            generateForProjectRecursively(childProject)
-        }
-    }
-
-    /**
-     * Generates tests and a SARIF report for classes in the [sourceSet].
-     */
-    private fun generateForSourceSet(sourceSet: SourceSetWrapper) {
-        logger.debug().bracket("Generating tests for the '${sourceSet.sourceSet.name}' source set") {
-            withUtContext(UtContext(sourceSet.classLoader)) {
-                val testCaseGenerator =
-                    TestCaseGenerator(
-                        listOf(sourceSet.workingDirectory),
-                        sourceSet.runtimeClasspath,
-                        dependencyPaths,
-                        JdkInfoDefaultProvider().info
-                    )
-                sourceSet.targetClasses.forEach { targetClass ->
-                    generateForClass(sourceSet, targetClass, testCaseGenerator)
-                }
-            }
-        }
-    }
-
-    /**
-     * Generates tests and a SARIF report for the class [targetClass].
-     */
-    private fun generateForClass(
-        sourceSet: SourceSetWrapper,
-        targetClass: TargetClassWrapper,
-        testCaseGenerator: TestCaseGenerator,
-    ) {
-        logger.debug().bracket("Generating tests for the $targetClass") {
-            val sourceFindingStrategy = SourceFindingStrategyGradle(sourceSet, targetClass.testsCodeFile.path)
-            GenerateTestsAndSarifReportFacade(sarifProperties, sourceFindingStrategy, testCaseGenerator)
-                .generateForClass(targetClass, sourceSet.workingDirectory)
-        }
-    }
-
-    /**
-     * Returns SARIF reports created for this [GradleProjectWrapper] and for all its child projects.
-     */
-    private fun GradleProjectWrapper.collectReportsRecursively(): List =
-        this.sourceSets.flatMap { sourceSetWrapper ->
-            sourceSetWrapper.collectReports()
-        } + this.childProjects.flatMap { childProject ->
-            childProject.collectReportsRecursively()
-        }
-
-    /**
-     * Returns SARIF reports created for this [SourceSetWrapper].
-     */
-    private fun SourceSetWrapper.collectReports(): List =
-        this.targetClasses.map { targetClass ->
-            targetClass.sarifReportFile.readText()
-        }
-}
+package org.utbot.gradle.plugin
+
+import mu.KLogger
+import org.gradle.api.DefaultTask
+import org.gradle.api.tasks.TaskAction
+import org.utbot.common.measureTime
+import org.utbot.common.debug
+import org.utbot.framework.plugin.api.TestCaseGenerator
+import org.utbot.framework.plugin.api.util.UtContext
+import org.utbot.framework.plugin.api.util.withUtContext
+import org.utbot.framework.plugin.sarif.GenerateTestsAndSarifReportFacade
+import org.utbot.gradle.plugin.extension.SarifGradleExtensionProvider
+import org.utbot.gradle.plugin.wrappers.GradleProjectWrapper
+import org.utbot.gradle.plugin.wrappers.SourceFindingStrategyGradle
+import org.utbot.gradle.plugin.wrappers.SourceSetWrapper
+import org.utbot.framework.plugin.sarif.TargetClassWrapper
+import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
+import java.io.File
+import java.net.URLClassLoader
+import javax.inject.Inject
+
+/**
+ * The main class containing the entry point [generateTestsAndSarifReport].
+ *
+ * [Documentation](https://docs.gradle.org/current/userguide/custom_tasks.html)
+ */
+open class GenerateTestsAndSarifReportTask @Inject constructor(
+    private val sarifProperties: SarifGradleExtensionProvider
+) : DefaultTask() {
+
+    init {
+        group = "utbot"
+        description = "Generate a SARIF report"
+    }
+
+    /**
+     * Entry point: called when the user starts this gradle task.
+     */
+    @TaskAction
+    fun generateTestsAndSarifReport() {
+        // the user specifies the parameters using "-Pname=value"
+        sarifProperties.taskParameters = project.gradle.startParameter.projectProperties
+        val rootGradleProject = try {
+            GradleProjectWrapper(project, sarifProperties)
+        } catch (t: Throwable) {
+            logger.error(t) { "Unexpected error while configuring the gradle task" }
+            return
+        }
+        try {
+
+            generateForProjectRecursively(rootGradleProject)
+            GenerateTestsAndSarifReportFacade.mergeReports(
+                sarifReports = rootGradleProject.collectReportsRecursively(),
+                mergedSarifReportFile = rootGradleProject.sarifReportFile
+            )
+        } catch (t: Throwable) {
+            logger.error(t) { "Unexpected error while generating SARIF report" }
+            return
+        }
+    }
+
+    // internal
+
+    // overwriting the getLogger() function from the DefaultTask
+    private val logger: KLogger = org.utbot.gradle.plugin.logger
+
+    private val dependencyPaths by lazy {
+        val thisClassLoader = this::class.java.classLoader as? URLClassLoader
+            ?: return@lazy System.getProperty("java.class.path")
+        thisClassLoader.urLs.joinToString(File.pathSeparator) { it.path }
+    }
+
+    /**
+     * Generates tests and a SARIF report for classes in the [gradleProject] and in all its child projects.
+     */
+    private fun generateForProjectRecursively(gradleProject: GradleProjectWrapper) {
+        gradleProject.sourceSets.forEach { sourceSet ->
+            generateForSourceSet(sourceSet)
+        }
+        gradleProject.childProjects.forEach { childProject ->
+            generateForProjectRecursively(childProject)
+        }
+    }
+
+    /**
+     * Generates tests and a SARIF report for classes in the [sourceSet].
+     */
+    private fun generateForSourceSet(sourceSet: SourceSetWrapper) {
+        logger.debug().measureTime({ "Generating tests for the '${sourceSet.sourceSet.name}' source set" }) {
+            withUtContext(UtContext(sourceSet.classLoader)) {
+                val testCaseGenerator =
+                    TestCaseGenerator(
+                        listOf(sourceSet.workingDirectory),
+                        sourceSet.runtimeClasspath,
+                        dependencyPaths,
+                        JdkInfoDefaultProvider().info
+                    )
+                sourceSet.targetClasses.forEach { targetClass ->
+                    generateForClass(sourceSet, targetClass, testCaseGenerator)
+                }
+            }
+        }
+    }
+
+    /**
+     * Generates tests and a SARIF report for the class [targetClass].
+     */
+    private fun generateForClass(
+        sourceSet: SourceSetWrapper,
+        targetClass: TargetClassWrapper,
+        testCaseGenerator: TestCaseGenerator,
+    ) {
+        logger.debug().measureTime({ "Generating tests for the $targetClass" }) {
+            val sourceFindingStrategy = SourceFindingStrategyGradle(sourceSet, targetClass.testsCodeFile.path)
+            GenerateTestsAndSarifReportFacade(sarifProperties, sourceFindingStrategy, testCaseGenerator)
+                .generateForClass(targetClass, sourceSet.workingDirectory)
+        }
+    }
+
+    /**
+     * Returns SARIF reports created for this [GradleProjectWrapper] and for all its child projects.
+     */
+    private fun GradleProjectWrapper.collectReportsRecursively(): List =
+        this.sourceSets.flatMap { sourceSetWrapper ->
+            sourceSetWrapper.collectReports()
+        } + this.childProjects.flatMap { childProject ->
+            childProject.collectReportsRecursively()
+        }
+
+    /**
+     * Returns SARIF reports created for this [SourceSetWrapper].
+     */
+    private fun SourceSetWrapper.collectReports(): List =
+        this.targetClasses.map { targetClass ->
+            targetClass.sarifReportFile.readText()
+        }
+}
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt
index 422a0bd069..07abbdb850 100644
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt
+++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt
@@ -1,6 +1,8 @@
 package org.utbot.instrumentation
 
+import com.jetbrains.rd.util.ILoggerFactory
 import com.jetbrains.rd.util.Logger
+import com.jetbrains.rd.util.Statics
 import com.jetbrains.rd.util.lifetime.Lifetime
 import com.jetbrains.rd.util.lifetime.LifetimeDefinition
 import com.jetbrains.rd.util.lifetime.isAlive
@@ -20,6 +22,7 @@ import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import mu.KotlinLogging
 import org.utbot.framework.plugin.api.InstrumentedProcessDeathException
+import org.utbot.common.logException
 import org.utbot.framework.plugin.api.FieldId
 import org.utbot.framework.plugin.api.util.UtContext
 import org.utbot.framework.plugin.api.util.signature
@@ -29,7 +32,9 @@ import org.utbot.instrumentation.process.generated.ComputeStaticFieldParams
 import org.utbot.instrumentation.process.generated.InvokeMethodCommandParams
 import org.utbot.instrumentation.rd.InstrumentedProcess
 import org.utbot.instrumentation.util.InstrumentedProcessError
+import org.utbot.rd.generated.synchronizationModel
 import org.utbot.rd.loggers.UtRdKLoggerFactory
+import org.utbot.rd.loggers.overrideDefaultRdLoggerFactoryWithKLogger
 
 private val logger = KotlinLogging.logger {}
 
@@ -120,7 +125,7 @@ class ConcreteExecutor> p
         val defaultPool = ConcreteExecutorPool()
 
         init {
-            Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger))
+            overrideDefaultRdLoggerFactoryWithKLogger(logger)
         }
 
         /**
@@ -178,14 +183,7 @@ class ConcreteExecutor> p
     suspend fun  withProcess(exclusively: Boolean = false, block: suspend InstrumentedProcess.() -> T): T {
         fun throwConcreteIfDead(e: Throwable, proc: InstrumentedProcess?) {
             if (proc?.lifetime?.isAlive != true) {
-                throw InstrumentedProcessDeathException(e,
-                    instrumentedProcessRunner.errorLogFile,
-                    try {
-                        proc?.run { process.inputStream.bufferedReader().lines().toList() } ?: emptyList()
-                    } catch (e: Exception) {
-                        emptyList()
-                    }
-                )
+                throw InstrumentedProcessDeathException(e)
             }
         }
 
@@ -229,18 +227,15 @@ class ConcreteExecutor> p
         signature: String,
         arguments: Array,
         parameters: Any?
-    ): TIResult = try {
+    ): TIResult = logger.logException("executeAsync, response(ERROR)") {
         withProcess {
             val argumentsByteArray = kryoHelper.writeObject(arguments.asList())
             val parametersByteArray = kryoHelper.writeObject(parameters)
             val params = InvokeMethodCommandParams(className, signature, argumentsByteArray, parametersByteArray)
 
-            val ba = instrumentedProcessModel.invokeMethodCommand.startSuspending(lifetime, params).result
-            kryoHelper.readObject(ba)
+            val result = instrumentedProcessModel.invokeMethodCommand.startSuspending(lifetime, params).result
+            kryoHelper.readObject(result)
         }
-    } catch (e: Throwable) {
-        logger.trace { "executeAsync, response(ERROR): $e" }
-        throw e
     }
 
     /**
@@ -275,7 +270,7 @@ class ConcreteExecutor> p
                 if (alive) {
                     try {
                         processInstance?.run {
-                            instrumentedProcessModel.stopProcess.start(lifetime, Unit)
+                            protocol.synchronizationModel.stopProcess.fire(Unit)
                         }
                     } catch (_: Exception) {}
                     processInstance = null
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/agent/DynamicClassTransformer.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/agent/DynamicClassTransformer.kt
index 5ab16b1db9..28607220e3 100644
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/agent/DynamicClassTransformer.kt
+++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/agent/DynamicClassTransformer.kt
@@ -11,7 +11,7 @@ import java.security.ProtectionDomain
 import kotlin.io.path.absolutePathString
 
 
-private val logger = getLogger("DynamicClassTransformer")
+private val logger = getLogger()
 
 /**
  * Transformer, which will transform only classes with certain names.
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ExecutionPhase.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ExecutionPhase.kt
index bcff67b0f8..7c64275b07 100644
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ExecutionPhase.kt
+++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ExecutionPhase.kt
@@ -1,10 +1,11 @@
 package org.utbot.instrumentation.instrumentation.execution.phases
 
 import com.jetbrains.rd.util.getLogger
+import org.utbot.common.measureTime
 import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult
-import org.utbot.rd.logMeasure
+import org.utbot.rd.loggers.debug
 
-private val logger = getLogger("ExecutionPhase")
+private val logger = getLogger()
 
 abstract class ExecutionPhaseException(override val message: String) : Exception()
 
@@ -20,7 +21,7 @@ interface ExecutionPhase {
 
 fun  T.start(block: T.() -> R): R =
     try {
-        logger.logMeasure(this.javaClass.simpleName) {
+        logger.debug().measureTime({ this.javaClass.simpleName } ) {
             this.block()
         }
     } catch (e: ExecutionPhaseStop) {
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt
index 96db99322a..0e1af9c98c 100644
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt
+++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt
@@ -2,6 +2,7 @@ package org.utbot.instrumentation.process
 
 import com.jetbrains.rd.util.*
 import com.jetbrains.rd.util.lifetime.Lifetime
+import com.jetbrains.rd.util.reactive.adviseOnce
 import kotlinx.coroutines.*
 import org.mockito.Mockito
 import org.utbot.common.*
@@ -18,14 +19,16 @@ import org.utbot.rd.IdleWatchdog
 import org.utbot.rd.ClientProtocolBuilder
 import org.utbot.rd.RdSettingsContainerFactory
 import org.utbot.rd.findRdPort
+import org.utbot.rd.generated.loggerModel
 import org.utbot.rd.generated.settingsModel
-import org.utbot.rd.loggers.UtRdConsoleLoggerFactory
+import org.utbot.rd.generated.synchronizationModel
+import org.utbot.rd.loggers.UtRdRemoteLoggerFactory
 import java.io.File
+import java.io.IOException
 import java.io.OutputStream
 import java.io.PrintStream
 import java.net.URLClassLoader
 import java.security.AllPermission
-import kotlin.system.measureTimeMillis
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.seconds
 
@@ -56,12 +59,24 @@ internal object HandlerClassesLoader : URLClassLoader(emptyArray()) {
  * Command-line option to disable the sandbox
  */
 const val DISABLE_SANDBOX_OPTION = "--disable-sandbox"
-const val ENABLE_LOGS_OPTION = "--enable-logs"
-private val logger = getLogger("InstrumentedProcess")
+private val logger = getLogger()
 private val messageFromMainTimeout: Duration = 120.seconds
 
-fun logLevelArgument(level: LogLevel): String {
-    return "$ENABLE_LOGS_OPTION=$level"
+private fun closeStandardStreams() {
+    // we should change out/err streams as not to spend time on user output
+    // and also because rd default logging system writes some initial values to stdout, polluting it as well
+    val tmpStream = PrintStream(object : OutputStream() {
+        override fun write(b: Int) {}
+    })
+    val prevOut = System.out
+    val prevError = System.err
+    System.setOut(tmpStream)
+    System.setErr(tmpStream)
+    // stdin/stderr should be closed as not to leave hanging descriptors
+    // and we cannot log any exceptions here as rd remote logging is still not configured
+    // so we pass any exceptions
+    silent { prevOut.close() }
+    silent { prevError.close() }
 }
 
 interface DummyForMockitoWarmup {
@@ -74,27 +89,21 @@ interface DummyForMockitoWarmup {
  */
 fun warmupMockito() {
     try {
-        val unused = Mockito.mock(DummyForMockitoWarmup::class.java)
-    } catch (ignored: Throwable) {
+        Mockito.mock(DummyForMockitoWarmup::class.java)
+    } catch (e: Throwable) {
+        logger.warn { "Exception during mockito warmup: ${e.stackTraceToString()}" }
     }
 }
 
-private fun findLogLevel(args: Array): LogLevel {
-    val logArgument = args.find{ it.contains(ENABLE_LOGS_OPTION) } ?: return LogLevel.Fatal
-
-    return enumValueOf(logArgument.split("=").last())
-}
+@Suppress("unused")
+object InstrumentedProcessMain
 
 /**
  * It should be compiled into separate jar file (instrumented_process.jar) and be run with an agent (agent.jar) option.
  */
 fun main(args: Array) = runBlocking {
     // We don't want user code to litter the standard output, so we redirect it.
-    val tmpStream = PrintStream(object : OutputStream() {
-        override fun write(b: Int) {}
-    })
-
-    System.setOut(tmpStream)
+    closeStandardStreams()
 
     if (!args.contains(DISABLE_SANDBOX_OPTION)) {
         permissions {
@@ -104,14 +113,14 @@ fun main(args: Array) = runBlocking {
         }
     }
 
-    val logLevel: LogLevel = findLogLevel(args)
-    Logger.set(Lifetime.Eternal, UtRdConsoleLoggerFactory(logLevel, System.err))
-
     val port = findRdPort(args)
 
     try {
         ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeout).start(port) {
-            this.protocol.scheduler.queue { warmupMockito() }
+            synchronizationModel.initRemoteLogging.adviseOnce(lifetime) {
+                Logger.set(Lifetime.Eternal, UtRdRemoteLoggerFactory(loggerModel))
+                this.protocol.scheduler.queue { warmupMockito() }
+            }
             val kryoHelper = KryoHelper(lifetime)
             logger.info { "setup started" }
             AbstractSettings.setupFactory(RdSettingsContainerFactory(protocol.settingsModel))
@@ -121,9 +130,6 @@ fun main(args: Array) = runBlocking {
     } catch (e: Throwable) {
         logger.error { "Terminating process because exception occurred: ${e.stackTraceToString()}" }
     }
-    logger.info { "runBlocking ending" }
-}.also {
-    logger.info { "runBlocking ended" }
 }
 
 private lateinit var pathsToUserClasses: Set
@@ -132,20 +138,15 @@ private lateinit var instrumentation: Instrumentation<*>
 private var warmupDone = false
 
 private fun InstrumentedProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatchdog) {
-    watchdog.wrapActiveCall(warmup) {
-        logger.info { "received warmup request" }
+    watchdog.measureTimeForActiveCall(warmup, "Classloader warmup request") {
         if (!warmupDone) {
-            val time = measureTimeMillis {
-                HandlerClassesLoader.scanForClasses("").toList() // here we transform classes
-            }
-            logger.info { "warmup finished in $time ms" }
+            HandlerClassesLoader.scanForClasses("").toList() // here we transform classes
             warmupDone = true
         } else {
             logger.info { "warmup already happened" }
         }
     }
-    watchdog.wrapActiveCall(invokeMethodCommand) { params ->
-        logger.debug { "received invokeMethod request: ${params.classname}, ${params.signature}" }
+    watchdog.measureTimeForActiveCall(invokeMethodCommand, "Invoke method request") { params ->
         val clazz = HandlerClassesLoader.loadClass(params.classname)
         val res = kotlin.runCatching {
             instrumentation.invoke(
@@ -156,35 +157,26 @@ private fun InstrumentedProcessModel.setup(kryoHelper: KryoHelper, watchdog: Idl
             )
         }
         res.fold({
-            logger.debug { "invokeMethod success" }
             InvokeMethodCommandResult(kryoHelper.writeObject(it))
         }) {
-            logger.debug { "invokeMethod failure" }
-            logger.error(it)
             throw it
         }
     }
-    watchdog.wrapActiveCall(setInstrumentation) { params ->
+    watchdog.measureTimeForActiveCall(setInstrumentation, "Instrumentation setup") { params ->
         logger.debug { "setInstrumentation request" }
         instrumentation = kryoHelper.readObject(params.instrumentation)
-        logger.trace { "instrumentation - ${instrumentation.javaClass.name} " }
+        logger.debug { "instrumentation - ${instrumentation.javaClass.name} " }
         Agent.dynamicClassTransformer.transformer = instrumentation // classTransformer is set
         Agent.dynamicClassTransformer.addUserPaths(pathsToUserClasses)
         instrumentation.init(pathsToUserClasses)
     }
-    watchdog.wrapActiveCall(addPaths) { params ->
-        logger.debug { "addPaths request" }
+    watchdog.measureTimeForActiveCall(addPaths, "User and dependency classpath setup") { params ->
         pathsToUserClasses = params.pathsToUserClasses.split(File.pathSeparatorChar).toSet()
         HandlerClassesLoader.addUrls(pathsToUserClasses)
         kryoHelper.setKryoClassLoader(HandlerClassesLoader) // Now kryo will use our classloader when it encounters unregistered class.
         UtContext.setUtContext(UtContext(HandlerClassesLoader))
     }
-    watchdog.wrapActiveCall(stopProcess) {
-        logger.debug { "stop request" }
-        watchdog.stopProtocol()
-    }
-    watchdog.wrapActiveCall(collectCoverage) { params ->
-        logger.debug { "collect coverage request" }
+    watchdog.measureTimeForActiveCall(collectCoverage, "Coverage") { params ->
         val anyClass: Class<*> = kryoHelper.readObject(params.clazz)
         logger.debug { "class - ${anyClass.name}" }
         val result = (instrumentation as CoverageInstrumentation).collectCoverageInfo(anyClass)
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt
index 327ef53437..8d06d98128 100644
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt
+++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt
@@ -11,7 +11,6 @@ import org.utbot.framework.process.OpenModulesContainer
 import org.utbot.instrumentation.agent.DynamicClassTransformer
 import org.utbot.rd.rdPortArgument
 import java.io.File
-import java.time.LocalDateTime
 
 private val logger = KotlinLogging.logger {}
 
@@ -27,34 +26,29 @@ class InstrumentedProcessRunner {
             listOf("-javaagent:$jarFile", "-ea", "-jar", "$jarFile")
     }
 
-    var errorLogFile: File = NULL_FILE
-
     fun start(rdPort: Int): Process {
         val portArgument = rdPortArgument(rdPort)
 
         logger.debug { "Starting instrumented process: ${cmds.joinToString(" ")} $portArgument" }
 
-        UT_BOT_TEMP_DIR.mkdirs()
-        val formatted = dateTimeFormatter.format(LocalDateTime.now())
-        errorLogFile = File(UT_BOT_TEMP_DIR, "$formatted.log")
-
         val directory = WorkingDirService.provide().toFile()
         val commandsWithOptions = buildList {
             addAll(cmds)
             if (!UtSettings.useSandbox) {
                 add(DISABLE_SANDBOX_OPTION)
             }
-            add(logLevelArgument(UtSettings.instrumentedProcessLogLevel))
             add(portArgument)
         }
 
         val processBuilder = ProcessBuilder(commandsWithOptions)
-            .redirectError(errorLogFile)
             .directory(directory)
 
         return processBuilder.start().also {
-            logger.info { "Instrumented process started with PID=${it.getPid}" }
-            logger.info { "Instrumented process log file: ${errorLogFile.absolutePath}" }
+            logger.info {
+                "------------------------------------------------------------------\n" +
+                "--------Instrumented process started with PID=${it.getPid}--------\n" +
+                "------------------------------------------------------------------"
+            }
         }
     }
 
@@ -62,13 +56,10 @@ class InstrumentedProcessRunner {
         private fun suspendValue(): String = if (UtSettings.suspendInstrumentedProcessExecutionInDebugMode) "y" else "n"
 
         private const val UTBOT_INSTRUMENTATION = "utbot-instrumentation"
-        private const val ERRORS_FILE_PREFIX = "utbot-instrumentedprocess-errors"
         private const val INSTRUMENTATION_LIB = "lib"
 
         private val DEBUG_RUN_CMD = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue()},quiet=y,address=${UtSettings.instrumentedProcessDebugPort}"
 
-        private val UT_BOT_TEMP_DIR: File = File(utBotTempDirectory.toFile(), ERRORS_FILE_PREFIX)
-
         private val NULL_FILE_PATH: String = if (System.getProperty("os.name").startsWith("Windows")) {
             "NUL"
         } else {
@@ -85,7 +76,7 @@ class InstrumentedProcessRunner {
          * in the gradle configuration of the project which depends on utbot-instrumentation module.
          */
         private val jarFile: File by lazy {
-            logger.debug().bracket("Finding $UTBOT_INSTRUMENTATION jar") {
+            logger.debug().measureTime({ "Finding $UTBOT_INSTRUMENTATION jar" } ) {
                 run {
                     logger.debug("Trying to find jar in the resources.")
                     val tempDir = utBotTempDirectory.toFile()
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt
index 8008030f82..973c79a597 100644
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt
+++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt
@@ -12,13 +12,20 @@ import org.utbot.instrumentation.process.generated.instrumentedProcessModel
 import org.utbot.instrumentation.util.KryoHelper
 import org.utbot.rd.ProcessWithRdServer
 import org.utbot.rd.exceptions.InstantProcessDeathException
+import org.utbot.rd.generated.LoggerModel
+import org.utbot.rd.generated.loggerModel
+import org.utbot.rd.generated.synchronizationModel
+import org.utbot.rd.loggers.UtRdKLogger
+import org.utbot.rd.loggers.UtRdRemoteLogger
 import org.utbot.rd.onSchedulerBlocking
 import org.utbot.rd.startUtProcessWithRdServer
 import org.utbot.rd.terminateOnException
 
-private val logger = KotlinLogging.logger {}
+private val logger = KotlinLogging.logger { }
+private val rdLogger = UtRdKLogger(logger, "")
 
-class InstrumentedProcessInstantDeathException: InstantProcessDeathException(UtSettings.instrumentedProcessDebugPort, UtSettings.runInstrumentedProcessWithDebug)
+class InstrumentedProcessInstantDeathException :
+    InstantProcessDeathException(UtSettings.instrumentedProcessDebugPort, UtSettings.runInstrumentedProcessWithDebug)
 
 /**
  * Main goals of this class:
@@ -33,6 +40,7 @@ class InstrumentedProcess private constructor(
         classLoader?.let { setKryoClassLoader(it) }
     }
     val instrumentedProcessModel: InstrumentedProcessModel = onSchedulerBlocking { protocol.instrumentedProcessModel }
+    val loggerModel: LoggerModel = onSchedulerBlocking { protocol.loggerModel }
 
     companion object {
         suspend operator fun > invoke(
@@ -54,10 +62,22 @@ class InstrumentedProcess private constructor(
 
             logger.trace("rd process started")
 
-            val proc = InstrumentedProcess(
-                classLoader,
-                rdProcess
-            )
+            val proc = InstrumentedProcess(classLoader, rdProcess)
+
+            // currently we do not specify log level for different categories in instrumented process
+            // though it is possible with some additional map on categories -> consider performance
+            proc.loggerModel.getCategoryMinimalLogLevel.set { _ ->
+                // this logLevel is obtained from KotlinLogger
+                rdLogger.logLevel.ordinal
+            }
+
+            proc.loggerModel.log.advise(proc.lifetime) {
+                val logLevel = UtRdRemoteLogger.logLevelValues[it.logLevelOrdinal]
+                // assume throwable already in message
+                rdLogger.log(logLevel, it.message, null)
+            }
+
+            rdProcess.protocol.synchronizationModel.initRemoteLogging.fire(Unit)
 
             proc.lifetime.onTermination {
                 logger.trace { "process is terminating" }
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt
index 812ad77951..6718640e3e 100644
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt
+++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt
@@ -22,7 +22,6 @@ class InstrumentedProcessModel private constructor(
     private val _warmup: RdCall,
     private val _setInstrumentation: RdCall,
     private val _invokeMethodCommand: RdCall,
-    private val _stopProcess: RdCall,
     private val _collectCoverage: RdCall,
     private val _computeStaticField: RdCall
 ) : RdExtBase() {
@@ -62,7 +61,7 @@ class InstrumentedProcessModel private constructor(
         }
         
         
-        const val serializationHash = 7371522075001495459L
+        const val serializationHash = 2443784041000581664L
         
     }
     override val serializersOwner: ISerializersOwner get() = InstrumentedProcessModel
@@ -92,11 +91,6 @@ class InstrumentedProcessModel private constructor(
      */
     val invokeMethodCommand: RdCall get() = _invokeMethodCommand
     
-    /**
-     * This command tells the instrumented process to stop
-     */
-    val stopProcess: RdCall get() = _stopProcess
-    
     /**
      * This command is sent to the instrumented process from the [ConcreteExecutor] if user wants to collect coverage for the
     [clazz]
@@ -115,7 +109,6 @@ class InstrumentedProcessModel private constructor(
         _warmup.async = true
         _setInstrumentation.async = true
         _invokeMethodCommand.async = true
-        _stopProcess.async = true
         _collectCoverage.async = true
         _computeStaticField.async = true
     }
@@ -125,7 +118,6 @@ class InstrumentedProcessModel private constructor(
         bindableChildren.add("warmup" to _warmup)
         bindableChildren.add("setInstrumentation" to _setInstrumentation)
         bindableChildren.add("invokeMethodCommand" to _invokeMethodCommand)
-        bindableChildren.add("stopProcess" to _stopProcess)
         bindableChildren.add("collectCoverage" to _collectCoverage)
         bindableChildren.add("computeStaticField" to _computeStaticField)
     }
@@ -137,7 +129,6 @@ class InstrumentedProcessModel private constructor(
         RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Void),
         RdCall(SetInstrumentationParams, FrameworkMarshallers.Void),
         RdCall(InvokeMethodCommandParams, InvokeMethodCommandResult),
-        RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Void),
         RdCall(CollectCoverageParams, CollectCoverageResult),
         RdCall(ComputeStaticFieldParams, ComputeStaticFieldResult)
     )
@@ -152,7 +143,6 @@ class InstrumentedProcessModel private constructor(
             print("warmup = "); _warmup.print(printer); println()
             print("setInstrumentation = "); _setInstrumentation.print(printer); println()
             print("invokeMethodCommand = "); _invokeMethodCommand.print(printer); println()
-            print("stopProcess = "); _stopProcess.print(printer); println()
             print("collectCoverage = "); _collectCoverage.print(printer); println()
             print("computeStaticField = "); _computeStaticField.print(printer); println()
         }
@@ -165,7 +155,6 @@ class InstrumentedProcessModel private constructor(
             _warmup.deepClonePolymorphic(),
             _setInstrumentation.deepClonePolymorphic(),
             _invokeMethodCommand.deepClonePolymorphic(),
-            _stopProcess.deepClonePolymorphic(),
             _collectCoverage.deepClonePolymorphic(),
             _computeStaticField.deepClonePolymorphic()
         )
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt
index eef1a9608b..c63682b2b5 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt
@@ -10,7 +10,9 @@ import com.intellij.psi.impl.file.impl.JavaFileManager
 import com.intellij.psi.search.GlobalSearchScope
 import com.intellij.refactoring.util.classMembers.MemberInfo
 import com.jetbrains.rd.util.ConcurrentHashMap
+import com.jetbrains.rd.util.ILoggerFactory
 import com.jetbrains.rd.util.Logger
+import com.jetbrains.rd.util.Statics
 import com.jetbrains.rd.util.lifetime.Lifetime
 import com.jetbrains.rd.util.lifetime.LifetimeDefinition
 import kotlinx.coroutines.runBlocking
@@ -41,21 +43,25 @@ import org.utbot.rd.generated.SettingsModel
 import org.utbot.rd.generated.settingsModel
 import org.utbot.rd.generated.synchronizationModel
 import org.utbot.rd.loggers.UtRdKLoggerFactory
+import org.utbot.rd.loggers.overrideDefaultRdLoggerFactoryWithKLogger
 import org.utbot.sarif.SourceFindingStrategy
 import java.io.File
 import java.nio.charset.Charset
 import java.nio.file.Files
 import java.nio.file.Path
 import java.nio.file.StandardCopyOption
-import java.time.LocalDateTime
 import kotlin.io.path.pathString
 import kotlin.reflect.KProperty1
 import kotlin.reflect.full.memberProperties
 
-private val engineProcessLogConfigurations = utBotTempDirectory.toFile().resolve("rdEngineProcessLogConfigurations")
+private val engineProcessLogConfigurationsDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogConfigurations")
 private val logger = KotlinLogging.logger {}
 private val engineProcessLogDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogs")
 
+private const val configurationFileDeleteKey = "delete_this_comment_key"
+private const val deleteOpenComment = ""
+
 data class RdTestGenerationResult(val notEmptyCases: Int, val testSetsId: Long)
 
 class EngineProcessInstantDeathException :
@@ -68,19 +74,20 @@ class EngineProcess private constructor(val project: Project, private val classN
 
         init {
             engineProcessLogDirectory.mkdirs()
-            engineProcessLogConfigurations.mkdirs()
-            Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger))
+            engineProcessLogConfigurationsDirectory.mkdirs()
+            overrideDefaultRdLoggerFactoryWithKLogger(logger)
 
             val customFile = File(UtSettings.engineProcessLogConfigFile)
 
             if (customFile.exists()) {
                 log4j2ConfigFile = customFile
             } else {
-                log4j2ConfigFile = Files.createTempFile(engineProcessLogConfigurations.toPath(), null, ".xml").toFile()
-                log4j2ConfigFile.deleteOnExit()
+                log4j2ConfigFile = Files.createTempFile(engineProcessLogConfigurationsDirectory.toPath(), null, ".xml").toFile()
                 this.javaClass.classLoader.getResourceAsStream("log4j2.xml")?.use { logConfig ->
                     val resultConfig = logConfig.readBytes().toString(Charset.defaultCharset())
+                        .replace(Regex("$deleteOpenComment|$deleteCloseComment"), "")
                         .replace("ref=\"IdeaAppender\"", "ref=\"EngineProcessAppender\"")
+                        .replace("\${env:UTBOT_LOG_DIR}", engineProcessLogDirectory.canonicalPath.trimEnd(File.separatorChar) + File.separatorChar)
                     Files.copy(
                         resultConfig.byteInputStream(),
                         log4j2ConfigFile.toPath(),
@@ -133,16 +140,12 @@ class EngineProcess private constructor(val project: Project, private val classN
                     val cmd = obtainEngineProcessCommandLine(port)
                     val directory = WorkingDirService.provide().toFile()
                     val builder = ProcessBuilder(cmd).directory(directory)
-                    val formatted = dateTimeFormatter.format(LocalDateTime.now())
-                    val logFile = File(engineProcessLogDirectory, "$formatted.log")
-
-                    builder.redirectOutput(logFile)
-                    builder.redirectError(logFile)
-
                     val process = builder.start()
 
                     logger.info { "Engine process started with PID = ${process.getPid}" }
-                    logger.info { "Engine process log file - ${logFile.canonicalPath}" }
+                    logger.info { "Engine process log directory - ${engineProcessLogDirectory.canonicalPath}" }
+                    logger.info { "Engine process log file - ${engineProcessLogDirectory.resolve("utbot-engine-current.log")}" }
+                    logger.info { "Log4j2 configuration file path - ${log4j2ConfigFile.canonicalPath}" }
 
                     if (!process.isAlive) {
                         throw EngineProcessInstantDeathException()
@@ -398,7 +401,7 @@ class EngineProcess private constructor(val project: Project, private val classN
 
     init {
         lifetime.onTermination {
-            engineModel.stopProcess.start(Unit)
+            protocol.synchronizationModel.stopProcess.fire(Unit)
         }
         settingsModel.settingFor.set { params ->
             SettingForResult(AbstractSettings.allSettings[params.key]?.let { settings: AbstractSettings ->
diff --git a/utbot-intellij/src/main/resources/log4j2.xml b/utbot-intellij/src/main/resources/log4j2.xml
index 58caefe205..6a9ae540c8 100644
--- a/utbot-intellij/src/main/resources/log4j2.xml
+++ b/utbot-intellij/src/main/resources/log4j2.xml
@@ -1 +1,34 @@
-

    

        
            
        


        
            
        
    
    
        
            
        
        
            
        
        
            
        
    

\ No newline at end of file
+
+
+    
+        
+        
+            
+        
+        
+        
+        
+        
+        
+        
+    
+    
+        
+            
+        
+        
+            
+        
+        
+            
+        
+    
+
\ No newline at end of file
diff --git a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt
index cba7acaef4..d3f7b6a8cd 100644
--- a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt
+++ b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt
@@ -3,7 +3,7 @@ package org.utbot.contest
 import mu.KotlinLogging
 import org.objectweb.asm.Type
 import org.utbot.common.FileUtil
-import org.utbot.common.bracket
+import org.utbot.common.measureTime
 import org.utbot.common.filterWhen
 import org.utbot.common.info
 import org.utbot.common.isAbstract
@@ -57,7 +57,7 @@ import org.utbot.framework.minimization.minimizeExecutions
 import org.utbot.framework.plugin.api.*
 import org.utbot.framework.plugin.api.util.isSynthetic
 import org.utbot.framework.util.jimpleBody
-import org.utbot.summary.summarize
+import org.utbot.summary.summarizeAll
 
 internal const val junitVersion = 4
 private val logger = KotlinLogging.logger {}
@@ -118,7 +118,7 @@ fun main(args: Array) {
         // This saves the time budget for real work instead of soot initialization.
         TestCaseGenerator(listOf(classfileDir), classpathString, dependencyPath, JdkInfoService.provide())
 
-        logger.info().bracket("warmup: kotlin reflection :: init") {
+        logger.info().measureTime({ "warmup: kotlin reflection :: init" }) {
             prepareClass(ConcreteExecutorPool::class.java, "")
             prepareClass(Warmup::class.java, "")
         }
@@ -198,10 +198,10 @@ fun runGeneration(
     if (runFromEstimator) {
         setOptions()
         //will not be executed in real contest
-        logger.info().bracket("warmup: 1st optional soot initialization and executor warmup (not to be counted in time budget)") {
+        logger.info().measureTime({ "warmup: 1st optional soot initialization and executor warmup (not to be counted in time budget)" }) {
             TestCaseGenerator(listOf(cut.classfileDir.toPath()), classpathString, dependencyPath, JdkInfoService.provide(), forceSootReload = false)
         }
-        logger.info().bracket("warmup (first): kotlin reflection :: init") {
+        logger.info().measureTime({ "warmup (first): kotlin reflection :: init" }) {
             prepareClass(ConcreteExecutorPool::class.java, "")
             prepareClass(Warmup::class.java, "")
         }
@@ -228,9 +228,9 @@ fun runGeneration(
             cgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(CodegenLanguage.defaultItem),
         )
 
-    logger.info().bracket("class ${cut.fqn}", { statsForClass }) {
+    logger.info().measureTime({ "class ${cut.fqn}" }, { statsForClass }) {
 
-        val filteredMethods = logger.info().bracket("preparation class ${cut.clazz}: kotlin reflection :: run") {
+        val filteredMethods = logger.info().measureTime({ "preparation class ${cut.clazz}: kotlin reflection :: run" }) {
             prepareClass(cut.clazz, methodNameFilter)
         }
 
@@ -240,7 +240,7 @@ fun runGeneration(
         if (filteredMethods.isEmpty()) return@runBlocking statsForClass
 
         val testCaseGenerator =
-            logger.info().bracket("2nd optional soot initialization") {
+            logger.info().measureTime({ "2nd optional soot initialization" }) {
                 TestCaseGenerator(listOf(cut.classfileDir.toPath()), classpathString, dependencyPath, JdkInfoService.provide(), forceSootReload = false)
             }
 
@@ -313,7 +313,7 @@ fun runGeneration(
 
 
                     var testsCounter = 0
-                    logger.info().bracket("method $method", { statsForMethod }) {
+                    logger.info().measureTime({ "method $method" }, { statsForMethod }) {
                         logger.info {
                             " -- Remaining time budget: $remainingBudget ms, " +
                                     "#remaining_methods: $remainingMethodsCount, " +
@@ -403,10 +403,9 @@ fun runGeneration(
 
         val testSets = testsByMethod.map { (method, executions) ->
             UtMethodTestSet(method, minimizeExecutions(executions), jimpleBody(method))
-                .summarize(cut.classfileDir.toPath(), sourceFile = null)
-        }
+        }.summarizeAll(cut.classfileDir.toPath(), sourceFile = null)
 
-        logger.info().bracket("Flushing tests for [${cut.simpleName}] on disk") {
+        logger.info().measureTime({ "Flushing tests for [${cut.simpleName}] on disk" }) {
             writeTestClass(cut, codeGenerator.generateAsString(testSets))
         }
         //write classes
diff --git a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimator.kt b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimator.kt
index 199bae753b..d1271f70c2 100644
--- a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimator.kt
+++ b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimator.kt
@@ -14,7 +14,7 @@ import mu.KotlinLogging
 import org.utbot.analytics.EngineAnalyticsContext
 import org.utbot.analytics.Predictors
 import org.utbot.common.FileUtil
-import org.utbot.common.bracket
+import org.utbot.common.measureTime
 import org.utbot.common.getPid
 import org.utbot.common.info
 import org.utbot.contest.Paths.classesLists
@@ -166,7 +166,7 @@ enum class Tool {
                 val testClass = cut.generatedTestFile
                 classStats.testClassFile = testClass
 
-                logger.info().bracket("Compiling class ${testClass.absolutePath}") {
+                logger.info().measureTime({ "Compiling class ${testClass.absolutePath}" }) {
                     val exitCode = compileClass(
                         compiledTestDir.absolutePath,
                         project.compileClasspathString,
diff --git a/utbot-junit-contest/src/main/kotlin/org/utbot/monitoring/StatisticsMonitoring.kt b/utbot-junit-contest/src/main/kotlin/org/utbot/monitoring/StatisticsMonitoring.kt
index ab4c1ff718..5af270f045 100644
--- a/utbot-junit-contest/src/main/kotlin/org/utbot/monitoring/StatisticsMonitoring.kt
+++ b/utbot-junit-contest/src/main/kotlin/org/utbot/monitoring/StatisticsMonitoring.kt
@@ -7,7 +7,7 @@ import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
 import mu.KotlinLogging
 import org.utbot.common.ThreadBasedExecutor
-import org.utbot.common.bracket
+import org.utbot.common.measureTime
 import org.utbot.common.info
 import org.utbot.contest.ContestEstimatorJdkInfoProvider
 import org.utbot.contest.ContextManager
@@ -50,7 +50,7 @@ fun main(args: Array) {
             Paths.moduleTestDir
         )
 
-        logger.info().bracket("Run UTBot generation [fuzzing ratio = $fuzzingRatio]") {
+        logger.info().measureTime({ "Run UTBot generation [fuzzing ratio = $fuzzingRatio]" }) {
             val start = System.nanoTime()
 
             executor.invokeWithTimeout(runTimeout) {
diff --git a/utbot-maven/src/main/kotlin/org/utbot/maven/plugin/GenerateTestsAndSarifReportMojo.kt b/utbot-maven/src/main/kotlin/org/utbot/maven/plugin/GenerateTestsAndSarifReportMojo.kt
index 94cb050950..1a016c98cb 100644
--- a/utbot-maven/src/main/kotlin/org/utbot/maven/plugin/GenerateTestsAndSarifReportMojo.kt
+++ b/utbot-maven/src/main/kotlin/org/utbot/maven/plugin/GenerateTestsAndSarifReportMojo.kt
@@ -1,228 +1,228 @@
-package org.utbot.maven.plugin
-
-import mu.KotlinLogging
-import org.apache.maven.plugin.AbstractMojo
-import org.apache.maven.plugins.annotations.Execute
-import org.apache.maven.plugins.annotations.LifecyclePhase
-import org.apache.maven.plugins.annotations.Mojo
-import org.apache.maven.plugins.annotations.Parameter
-import org.apache.maven.project.MavenProject
-import org.utbot.common.bracket
-import org.utbot.common.debug
-import org.utbot.framework.plugin.api.TestCaseGenerator
-import org.utbot.framework.plugin.api.util.UtContext
-import org.utbot.framework.plugin.api.util.withUtContext
-import org.utbot.framework.plugin.sarif.GenerateTestsAndSarifReportFacade
-import org.utbot.framework.plugin.sarif.TargetClassWrapper
-import org.utbot.framework.plugin.services.JdkInfoService
-import org.utbot.maven.plugin.extension.SarifMavenConfigurationProvider
-import org.utbot.maven.plugin.wrappers.MavenProjectWrapper
-import org.utbot.maven.plugin.wrappers.SourceFindingStrategyMaven
-import java.io.File
-import java.net.URLClassLoader
-
-internal val logger = KotlinLogging.logger {}
-
-/**
- * The main class containing the entry point [execute].
- *
- * [Documentation](https://maven.apache.org/guides/plugin/guide-java-plugin-development.html)
- */
-@Mojo(
-    name = "generateTestsAndSarifReport",
-    defaultPhase = LifecyclePhase.GENERATE_TEST_SOURCES
-)
-@Execute(
-    phase = LifecyclePhase.GENERATE_TEST_SOURCES
-)
-class GenerateTestsAndSarifReportMojo : AbstractMojo() {
-
-    /**
-     * The maven project for which we are creating a SARIF report.
-     */
-    @Parameter(defaultValue = "\${project}", readonly = true)
-    lateinit var mavenProject: MavenProject
-
-    /**
-     * Classes for which the SARIF report will be created.
-     * Uses all classes from the user project if this list is empty.
-     */
-    @Parameter(defaultValue = "")
-    internal var targetClasses: List = listOf()
-
-    /**
-     * Absolute path to the root of the relative paths in the SARIF report.
-     */
-    @Parameter(defaultValue = "\${project.basedir}")
-    internal lateinit var projectRoot: File
-
-    /**
-     * Relative path (against module root) to the root of the generated tests.
-     */
-    @Parameter(defaultValue = "target/generated/test")
-    internal lateinit var generatedTestsRelativeRoot: String
-
-    /**
-     * Relative path (against module root) to the root of the SARIF reports.
-     */
-    @Parameter(defaultValue = "target/generated/sarif")
-    internal lateinit var sarifReportsRelativeRoot: String
-
-    /**
-     * Mark the directory with generated tests as `test sources root` or not.
-     */
-    @Parameter(defaultValue = "true")
-    internal var markGeneratedTestsDirectoryAsTestSourcesRoot: Boolean = true
-
-    /**
-     * Generate tests for private methods or not.
-     */
-    @Parameter(defaultValue = "false")
-    internal var testPrivateMethods: Boolean = false
-
-    /**
-     * Can be one of: 'junit4', 'junit5', 'testng'.
-     */
-    @Parameter(defaultValue = "junit5")
-    internal lateinit var testFramework: String
-
-    /**
-     * Can be one of: 'mockito'.
-     */
-    @Parameter(defaultValue = "mockito")
-    internal lateinit var mockFramework: String
-
-    /**
-     * Maximum tests generation time for one class (in milliseconds).
-     */
-    @Parameter
-    internal var generationTimeout: Long = 60 * 1000L
-
-    /**
-     * Can be one of: 'java', 'kotlin'.
-     */
-    @Parameter(defaultValue = "java")
-    internal lateinit var codegenLanguage: String
-
-    /**
-     * Can be one of: 'no-mocks', 'other-packages', 'other-classes'.
-     */
-    @Parameter(defaultValue = "no-mocks")
-    internal lateinit var mockStrategy: String
-
-    /**
-     * Can be one of: 'do-not-mock-statics', 'mock-statics'.
-     */
-    @Parameter(defaultValue = "do-not-mock-statics")
-    internal lateinit var staticsMocking: String
-
-    /**
-     * Can be one of: 'force', 'do-not-force'.
-     */
-    @Parameter(defaultValue = "force")
-    internal lateinit var forceStaticMocking: String
-
-    /**
-     * Classes to force mocking theirs static methods and constructors.
-     */
-    @Parameter(defaultValue = "")
-    internal var classesToMockAlways: List = listOf()
-
-    /**
-     * Provides configuration needed to create a SARIF report.
-     */
-    val sarifProperties: SarifMavenConfigurationProvider
-        get() = SarifMavenConfigurationProvider(this)
-
-    /**
-     * Contains information about the maven project for which we are creating a SARIF report.
-     */
-    lateinit var rootMavenProjectWrapper: MavenProjectWrapper
-
-    private val dependencyPaths by lazy {
-        val thisClassLoader = this::class.java.classLoader as? URLClassLoader
-            ?: return@lazy System.getProperty("java.class.path")
-        thisClassLoader.urLs.joinToString(File.pathSeparator) { it.path }
-    }
-
-    /**
-     * Entry point: called when the user starts this maven task.
-     */
-    override fun execute() {
-        try {
-            rootMavenProjectWrapper = MavenProjectWrapper(mavenProject, sarifProperties)
-        } catch (t: Throwable) {
-            logger.error(t) { "Unexpected error while configuring the maven task" }
-            return
-        }
-        try {
-            generateForProjectRecursively(rootMavenProjectWrapper)
-            GenerateTestsAndSarifReportFacade.mergeReports(
-                sarifReports = rootMavenProjectWrapper.collectReportsRecursively(),
-                mergedSarifReportFile = rootMavenProjectWrapper.sarifReportFile
-            )
-        } catch (t: Throwable) {
-            logger.error(t) { "Unexpected error while generating SARIF report" }
-            return
-        }
-    }
-
-    // internal
-
-    /**
-     * Generates tests and a SARIF report for classes in the [mavenProjectWrapper] and in all its child projects.
-     */
-    private fun generateForProjectRecursively(mavenProjectWrapper: MavenProjectWrapper) {
-        logger.debug().bracket("Generating tests for the '${mavenProjectWrapper.mavenProject.name}' source set") {
-            withUtContext(UtContext(mavenProjectWrapper.classLoader)) {
-                val testCaseGenerator =
-                    TestCaseGenerator(
-                        listOf(mavenProjectWrapper.workingDirectory),
-                        mavenProjectWrapper.runtimeClasspath,
-                        dependencyPaths,
-                        JdkInfoService.provide()
-                    )
-                mavenProjectWrapper.targetClasses.forEach { targetClass ->
-                    generateForClass(mavenProjectWrapper, targetClass, testCaseGenerator)
-                }
-            }
-        }
-        mavenProjectWrapper.childProjects.forEach { childProject ->
-            generateForProjectRecursively(childProject)
-        }
-    }
-
-    /**
-     * Generates tests and a SARIF report for the class [targetClass].
-     */
-    private fun generateForClass(
-        mavenProjectWrapper: MavenProjectWrapper,
-        targetClass: TargetClassWrapper,
-        testCaseGenerator: TestCaseGenerator,
-    ) {
-        logger.debug().bracket("Generating tests for the $targetClass") {
-            val sourceFindingStrategy =
-                SourceFindingStrategyMaven(mavenProjectWrapper, targetClass.testsCodeFile.path)
-            val generateTestsAndSarifReportFacade =
-                GenerateTestsAndSarifReportFacade(sarifProperties, sourceFindingStrategy, testCaseGenerator)
-            generateTestsAndSarifReportFacade
-                .generateForClass(targetClass, mavenProjectWrapper.workingDirectory)
-        }
-    }
-
-    /**
-     * Returns SARIF reports created for this [MavenProjectWrapper] and for all its child projects.
-     */
-    private fun MavenProjectWrapper.collectReportsRecursively(): List =
-        this.childProjects.flatMap { childProject ->
-            childProject.collectReportsRecursively()
-        } + this.collectReports()
-
-    /**
-     * Returns SARIF reports created for this [MavenProjectWrapper].
-     */
-    private fun MavenProjectWrapper.collectReports(): List =
-        this.targetClasses.map { targetClass ->
-            targetClass.sarifReportFile.readText()
-        }
-}
+package org.utbot.maven.plugin
+
+import mu.KotlinLogging
+import org.apache.maven.plugin.AbstractMojo
+import org.apache.maven.plugins.annotations.Execute
+import org.apache.maven.plugins.annotations.LifecyclePhase
+import org.apache.maven.plugins.annotations.Mojo
+import org.apache.maven.plugins.annotations.Parameter
+import org.apache.maven.project.MavenProject
+import org.utbot.common.measureTime
+import org.utbot.common.debug
+import org.utbot.framework.plugin.api.TestCaseGenerator
+import org.utbot.framework.plugin.api.util.UtContext
+import org.utbot.framework.plugin.api.util.withUtContext
+import org.utbot.framework.plugin.sarif.GenerateTestsAndSarifReportFacade
+import org.utbot.framework.plugin.sarif.TargetClassWrapper
+import org.utbot.framework.plugin.services.JdkInfoService
+import org.utbot.maven.plugin.extension.SarifMavenConfigurationProvider
+import org.utbot.maven.plugin.wrappers.MavenProjectWrapper
+import org.utbot.maven.plugin.wrappers.SourceFindingStrategyMaven
+import java.io.File
+import java.net.URLClassLoader
+
+internal val logger = KotlinLogging.logger {}
+
+/**
+ * The main class containing the entry point [execute].
+ *
+ * [Documentation](https://maven.apache.org/guides/plugin/guide-java-plugin-development.html)
+ */
+@Mojo(
+    name = "generateTestsAndSarifReport",
+    defaultPhase = LifecyclePhase.GENERATE_TEST_SOURCES
+)
+@Execute(
+    phase = LifecyclePhase.GENERATE_TEST_SOURCES
+)
+class GenerateTestsAndSarifReportMojo : AbstractMojo() {
+
+    /**
+     * The maven project for which we are creating a SARIF report.
+     */
+    @Parameter(defaultValue = "\${project}", readonly = true)
+    lateinit var mavenProject: MavenProject
+
+    /**
+     * Classes for which the SARIF report will be created.
+     * Uses all classes from the user project if this list is empty.
+     */
+    @Parameter(defaultValue = "")
+    internal var targetClasses: List = listOf()
+
+    /**
+     * Absolute path to the root of the relative paths in the SARIF report.
+     */
+    @Parameter(defaultValue = "\${project.basedir}")
+    internal lateinit var projectRoot: File
+
+    /**
+     * Relative path (against module root) to the root of the generated tests.
+     */
+    @Parameter(defaultValue = "target/generated/test")
+    internal lateinit var generatedTestsRelativeRoot: String
+
+    /**
+     * Relative path (against module root) to the root of the SARIF reports.
+     */
+    @Parameter(defaultValue = "target/generated/sarif")
+    internal lateinit var sarifReportsRelativeRoot: String
+
+    /**
+     * Mark the directory with generated tests as `test sources root` or not.
+     */
+    @Parameter(defaultValue = "true")
+    internal var markGeneratedTestsDirectoryAsTestSourcesRoot: Boolean = true
+
+    /**
+     * Generate tests for private methods or not.
+     */
+    @Parameter(defaultValue = "false")
+    internal var testPrivateMethods: Boolean = false
+
+    /**
+     * Can be one of: 'junit4', 'junit5', 'testng'.
+     */
+    @Parameter(defaultValue = "junit5")
+    internal lateinit var testFramework: String
+
+    /**
+     * Can be one of: 'mockito'.
+     */
+    @Parameter(defaultValue = "mockito")
+    internal lateinit var mockFramework: String
+
+    /**
+     * Maximum tests generation time for one class (in milliseconds).
+     */
+    @Parameter
+    internal var generationTimeout: Long = 60 * 1000L
+
+    /**
+     * Can be one of: 'java', 'kotlin'.
+     */
+    @Parameter(defaultValue = "java")
+    internal lateinit var codegenLanguage: String
+
+    /**
+     * Can be one of: 'no-mocks', 'other-packages', 'other-classes'.
+     */
+    @Parameter(defaultValue = "no-mocks")
+    internal lateinit var mockStrategy: String
+
+    /**
+     * Can be one of: 'do-not-mock-statics', 'mock-statics'.
+     */
+    @Parameter(defaultValue = "do-not-mock-statics")
+    internal lateinit var staticsMocking: String
+
+    /**
+     * Can be one of: 'force', 'do-not-force'.
+     */
+    @Parameter(defaultValue = "force")
+    internal lateinit var forceStaticMocking: String
+
+    /**
+     * Classes to force mocking theirs static methods and constructors.
+     */
+    @Parameter(defaultValue = "")
+    internal var classesToMockAlways: List = listOf()
+
+    /**
+     * Provides configuration needed to create a SARIF report.
+     */
+    val sarifProperties: SarifMavenConfigurationProvider
+        get() = SarifMavenConfigurationProvider(this)
+
+    /**
+     * Contains information about the maven project for which we are creating a SARIF report.
+     */
+    lateinit var rootMavenProjectWrapper: MavenProjectWrapper
+
+    private val dependencyPaths by lazy {
+        val thisClassLoader = this::class.java.classLoader as? URLClassLoader
+            ?: return@lazy System.getProperty("java.class.path")
+        thisClassLoader.urLs.joinToString(File.pathSeparator) { it.path }
+    }
+
+    /**
+     * Entry point: called when the user starts this maven task.
+     */
+    override fun execute() {
+        try {
+            rootMavenProjectWrapper = MavenProjectWrapper(mavenProject, sarifProperties)
+        } catch (t: Throwable) {
+            logger.error(t) { "Unexpected error while configuring the maven task" }
+            return
+        }
+        try {
+            generateForProjectRecursively(rootMavenProjectWrapper)
+            GenerateTestsAndSarifReportFacade.mergeReports(
+                sarifReports = rootMavenProjectWrapper.collectReportsRecursively(),
+                mergedSarifReportFile = rootMavenProjectWrapper.sarifReportFile
+            )
+        } catch (t: Throwable) {
+            logger.error(t) { "Unexpected error while generating SARIF report" }
+            return
+        }
+    }
+
+    // internal
+
+    /**
+     * Generates tests and a SARIF report for classes in the [mavenProjectWrapper] and in all its child projects.
+     */
+    private fun generateForProjectRecursively(mavenProjectWrapper: MavenProjectWrapper) {
+        logger.debug().measureTime({ "Generating tests for the '${mavenProjectWrapper.mavenProject.name}' source set" }) {
+            withUtContext(UtContext(mavenProjectWrapper.classLoader)) {
+                val testCaseGenerator =
+                    TestCaseGenerator(
+                        listOf(mavenProjectWrapper.workingDirectory),
+                        mavenProjectWrapper.runtimeClasspath,
+                        dependencyPaths,
+                        JdkInfoService.provide()
+                    )
+                mavenProjectWrapper.targetClasses.forEach { targetClass ->
+                    generateForClass(mavenProjectWrapper, targetClass, testCaseGenerator)
+                }
+            }
+        }
+        mavenProjectWrapper.childProjects.forEach { childProject ->
+            generateForProjectRecursively(childProject)
+        }
+    }
+
+    /**
+     * Generates tests and a SARIF report for the class [targetClass].
+     */
+    private fun generateForClass(
+        mavenProjectWrapper: MavenProjectWrapper,
+        targetClass: TargetClassWrapper,
+        testCaseGenerator: TestCaseGenerator,
+    ) {
+        logger.debug().measureTime({ "Generating tests for the $targetClass" }) {
+            val sourceFindingStrategy =
+                SourceFindingStrategyMaven(mavenProjectWrapper, targetClass.testsCodeFile.path)
+            val generateTestsAndSarifReportFacade =
+                GenerateTestsAndSarifReportFacade(sarifProperties, sourceFindingStrategy, testCaseGenerator)
+            generateTestsAndSarifReportFacade
+                .generateForClass(targetClass, mavenProjectWrapper.workingDirectory)
+        }
+    }
+
+    /**
+     * Returns SARIF reports created for this [MavenProjectWrapper] and for all its child projects.
+     */
+    private fun MavenProjectWrapper.collectReportsRecursively(): List =
+        this.childProjects.flatMap { childProject ->
+            childProject.collectReportsRecursively()
+        } + this.collectReports()
+
+    /**
+     * Returns SARIF reports created for this [MavenProjectWrapper].
+     */
+    private fun MavenProjectWrapper.collectReports(): List =
+        this.targetClasses.map { targetClass ->
+            targetClass.sarifReportFile.readText()
+        }
+}
diff --git a/utbot-rd/build.gradle b/utbot-rd/build.gradle
index 6097d21dcd..01ac9574df 100644
--- a/utbot-rd/build.gradle
+++ b/utbot-rd/build.gradle
@@ -181,6 +181,15 @@ task generateCommonModels(type: RdGenTask) {
         directory = generatedOutputDir.canonicalPath
         namespace = "org.utbot.rd.generated"
     }
+
+    rdParams.generator {
+        language = "kotlin"
+        transform = "symmetric"
+        root = "org.utbot.rd.models.LoggerRoot"
+
+        directory = generatedOutputDir.canonicalPath
+        namespace = "org.utbot.rd.generated"
+    }
 }
 
 task generateCSharpModels(type: RdGenTask) {
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt
index fb35913d02..a9b3d23bc0 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt
@@ -3,12 +3,14 @@ package org.utbot.rd
 import com.jetbrains.rd.framework.*
 import com.jetbrains.rd.framework.impl.RdCall
 import com.jetbrains.rd.framework.util.launch
+import com.jetbrains.rd.util.LogLevel
 import com.jetbrains.rd.util.getLogger
 import com.jetbrains.rd.util.info
 import com.jetbrains.rd.util.lifetime.Lifetime
 import com.jetbrains.rd.util.lifetime.LifetimeDefinition
 import com.jetbrains.rd.util.lifetime.isAlive
 import com.jetbrains.rd.util.lifetime.plusAssign
+import com.jetbrains.rd.util.reactive.adviseEternal
 import com.jetbrains.rd.util.threading.SingleThreadScheduler
 import com.jetbrains.rd.util.trace
 import kotlinx.coroutines.CancellationException
@@ -17,6 +19,7 @@ import kotlinx.coroutines.channels.trySendBlocking
 import kotlinx.coroutines.withTimeoutOrNull
 import org.utbot.common.*
 import org.utbot.rd.generated.synchronizationModel
+import org.utbot.rd.loggers.withLevel
 import java.io.File
 import kotlin.time.Duration
 
@@ -98,6 +101,21 @@ class IdleWatchdog(private val ldef: LifetimeDefinition, val timeout: Duration)
         }
     }
 
+    /**
+     * Adds callback to RdCall with indicating that during this activity process should not die.
+     * After block ended - idle timer restarts.
+     * Also additonally logs
+     */
+    fun  measureTimeForActiveCall(call: RdCall, requestName: String, level: LogLevel = LogLevel.Debug, block: (T) -> R) {
+        call.set { it ->
+            logger.withLevel(level).measureTime({ requestName }) {
+                wrapActive {
+                    block(it)
+                }
+            }
+        }
+    }
+
     suspend fun setupTimeout() {
         ldef.launch {
             var lastState = State.ENDED
@@ -162,6 +180,7 @@ class ClientProtocolBuilder {
                 clientProtocol.synchronizationModel.suspendTimeoutTimer.set { param ->
                     watchdog.suspendTimeout = param
                 }
+                clientProtocol.synchronizationModel.stopProcess.adviseEternal { _ -> watchdog.stopProtocol() }
                 clientProtocol.block(watchdog)
             }
 
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt
index e2f28119b3..a6321f1e18 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt
@@ -11,6 +11,7 @@ import com.jetbrains.rd.util.lifetime.throwIfNotAlive
 import com.jetbrains.rd.util.trace
 import kotlinx.coroutines.delay
 import org.utbot.common.getPid
+import org.utbot.common.silent
 import org.utbot.rd.generated.synchronizationModel
 import java.io.File
 import java.nio.file.Files
@@ -90,6 +91,13 @@ class ProcessWithRdServerImpl private constructor(
 ) : ProcessWithRdServer, LifetimedProcess by child {
     override val protocol = serverFactory(lifetime)
 
+    override fun terminate() {
+        silent {
+            protocol.synchronizationModel.stopProcess.fire(Unit)
+        }
+        child.terminate()
+    }
+
     companion object {
         suspend operator fun invoke(
             child: LifetimedProcess, serverFactory: (Lifetime) -> Protocol
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt
index 535c294032..4dabe27c7b 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt
@@ -3,8 +3,7 @@ package org.utbot.rd
 import com.jetbrains.rd.framework.*
 import com.jetbrains.rd.framework.util.NetUtils
 import com.jetbrains.rd.framework.util.synchronizeWith
-import com.jetbrains.rd.util.Logger
-import com.jetbrains.rd.util.debug
+import com.jetbrains.rd.util.*
 import com.jetbrains.rd.util.lifetime.Lifetime
 import com.jetbrains.rd.util.lifetime.LifetimeDefinition
 import com.jetbrains.rd.util.lifetime.throwIfNotAlive
@@ -14,6 +13,7 @@ import com.jetbrains.rd.util.threading.SingleThreadScheduler
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.runBlocking
+import org.utbot.common.LoggerWithLogMethod
 
 suspend fun  ProcessWithRdServer.onScheduler(block: () -> T): T {
     val deffered = CompletableDeferred()
@@ -124,19 +124,4 @@ suspend fun startUtProcessWithRdServer(
             it
         )
     }
-}
-
-inline fun  Logger.logMeasure(tag: String = "", block: () -> T): T {
-    val start = System.currentTimeMillis()
-    this.debug { "started evaluating $tag" }
-    try {
-        return block()
-    }
-    catch (e: Throwable) {
-        this.debug { "exception during evaluating $tag: ${e.stackTraceToString()}" }
-        throw e
-    }
-    finally {
-        this.debug { "evaluating $tag took ${System.currentTimeMillis() - start} ms" }
-    }
 }
\ No newline at end of file
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerModel.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerModel.Generated.kt
new file mode 100644
index 0000000000..d8f0f59f87
--- /dev/null
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerModel.Generated.kt
@@ -0,0 +1,177 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection")
+package org.utbot.rd.generated
+
+import com.jetbrains.rd.framework.*
+import com.jetbrains.rd.framework.base.*
+import com.jetbrains.rd.framework.impl.*
+
+import com.jetbrains.rd.util.lifetime.*
+import com.jetbrains.rd.util.reactive.*
+import com.jetbrains.rd.util.string.*
+import com.jetbrains.rd.util.*
+import kotlin.reflect.KClass
+import kotlin.jvm.JvmStatic
+
+
+
+/**
+ * #### Generated from [LoggerModel.kt:8]
+ */
+class LoggerModel private constructor(
+    private val _log: RdSignal,
+    private val _getCategoryMinimalLogLevel: RdCall
+) : RdExtBase() {
+    //companion
+    
+    companion object : ISerializersOwner {
+        
+        override fun registerSerializersCore(serializers: ISerializers)  {
+            serializers.register(LogArguments)
+        }
+        
+        
+        @JvmStatic
+        @JvmName("internalCreateModel")
+        @Deprecated("Use create instead", ReplaceWith("create(lifetime, protocol)"))
+        internal fun createModel(lifetime: Lifetime, protocol: IProtocol): LoggerModel  {
+            @Suppress("DEPRECATION")
+            return create(lifetime, protocol)
+        }
+        
+        @JvmStatic
+        @Deprecated("Use protocol.loggerModel or revise the extension scope instead", ReplaceWith("protocol.loggerModel"))
+        fun create(lifetime: Lifetime, protocol: IProtocol): LoggerModel  {
+            LoggerRoot.register(protocol.serializers)
+            
+            return LoggerModel().apply {
+                identify(protocol.identity, RdId.Null.mix("LoggerModel"))
+                bind(lifetime, protocol, "LoggerModel")
+            }
+        }
+        
+        
+        const val serializationHash = -6259198217478203203L
+        
+    }
+    override val serializersOwner: ISerializersOwner get() = LoggerModel
+    override val serializationHash: Long get() = LoggerModel.serializationHash
+    
+    //fields
+    val log: IAsyncSignal get() = _log
+    
+    /**
+     * Parameter - log category.
+    Result - integer value for com.jetbrains.rd.util.LogLevel.
+     */
+    val getCategoryMinimalLogLevel: RdCall get() = _getCategoryMinimalLogLevel
+    //methods
+    //initializer
+    init {
+        _log.async = true
+        _getCategoryMinimalLogLevel.async = true
+    }
+    
+    init {
+        bindableChildren.add("log" to _log)
+        bindableChildren.add("getCategoryMinimalLogLevel" to _getCategoryMinimalLogLevel)
+    }
+    
+    //secondary constructor
+    private constructor(
+    ) : this(
+        RdSignal(LogArguments),
+        RdCall(FrameworkMarshallers.String, FrameworkMarshallers.Int)
+    )
+    
+    //equals trait
+    //hash code trait
+    //pretty print
+    override fun print(printer: PrettyPrinter)  {
+        printer.println("LoggerModel (")
+        printer.indent {
+            print("log = "); _log.print(printer); println()
+            print("getCategoryMinimalLogLevel = "); _getCategoryMinimalLogLevel.print(printer); println()
+        }
+        printer.print(")")
+    }
+    //deepClone
+    override fun deepClone(): LoggerModel   {
+        return LoggerModel(
+            _log.deepClonePolymorphic(),
+            _getCategoryMinimalLogLevel.deepClonePolymorphic()
+        )
+    }
+    //contexts
+}
+val IProtocol.loggerModel get() = getOrCreateExtension(LoggerModel::class) { @Suppress("DEPRECATION") LoggerModel.create(lifetime, this) }
+
+
+
+/**
+ * @property logLevelOrdinal Integer value for com.jetbrains.rd.util.LogLevel
+ * #### Generated from [LoggerModel.kt:9]
+ */
+data class LogArguments (
+    val category: String,
+    val logLevelOrdinal: Int,
+    val message: String
+) : IPrintable {
+    //companion
+    
+    companion object : IMarshaller {
+        override val _type: KClass = LogArguments::class
+        
+        @Suppress("UNCHECKED_CAST")
+        override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): LogArguments  {
+            val category = buffer.readString()
+            val logLevelOrdinal = buffer.readInt()
+            val message = buffer.readString()
+            return LogArguments(category, logLevelOrdinal, message)
+        }
+        
+        override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: LogArguments)  {
+            buffer.writeString(value.category)
+            buffer.writeInt(value.logLevelOrdinal)
+            buffer.writeString(value.message)
+        }
+        
+        
+    }
+    //fields
+    //methods
+    //initializer
+    //secondary constructor
+    //equals trait
+    override fun equals(other: Any?): Boolean  {
+        if (this === other) return true
+        if (other == null || other::class != this::class) return false
+        
+        other as LogArguments
+        
+        if (category != other.category) return false
+        if (logLevelOrdinal != other.logLevelOrdinal) return false
+        if (message != other.message) return false
+        
+        return true
+    }
+    //hash code trait
+    override fun hashCode(): Int  {
+        var __r = 0
+        __r = __r*31 + category.hashCode()
+        __r = __r*31 + logLevelOrdinal.hashCode()
+        __r = __r*31 + message.hashCode()
+        return __r
+    }
+    //pretty print
+    override fun print(printer: PrettyPrinter)  {
+        printer.println("LogArguments (")
+        printer.indent {
+            print("category = "); category.print(printer); println()
+            print("logLevelOrdinal = "); logLevelOrdinal.print(printer); println()
+            print("message = "); message.print(printer); println()
+        }
+        printer.print(")")
+    }
+    //deepClone
+    //contexts
+}
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerRoot.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerRoot.Generated.kt
new file mode 100644
index 0000000000..64503c5452
--- /dev/null
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerRoot.Generated.kt
@@ -0,0 +1,58 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection")
+package org.utbot.rd.generated
+
+import com.jetbrains.rd.framework.*
+import com.jetbrains.rd.framework.base.*
+import com.jetbrains.rd.framework.impl.*
+
+import com.jetbrains.rd.util.lifetime.*
+import com.jetbrains.rd.util.reactive.*
+import com.jetbrains.rd.util.string.*
+import com.jetbrains.rd.util.*
+import kotlin.reflect.KClass
+import kotlin.jvm.JvmStatic
+
+
+
+/**
+ * #### Generated from [LoggerModel.kt:7]
+ */
+class LoggerRoot private constructor(
+) : RdExtBase() {
+    //companion
+    
+    companion object : ISerializersOwner {
+        
+        override fun registerSerializersCore(serializers: ISerializers)  {
+            LoggerRoot.register(serializers)
+            LoggerModel.register(serializers)
+        }
+        
+        
+        
+        
+        
+        const val serializationHash = -3743703762234585836L
+        
+    }
+    override val serializersOwner: ISerializersOwner get() = LoggerRoot
+    override val serializationHash: Long get() = LoggerRoot.serializationHash
+    
+    //fields
+    //methods
+    //initializer
+    //secondary constructor
+    //equals trait
+    //hash code trait
+    //pretty print
+    override fun print(printer: PrettyPrinter)  {
+        printer.println("LoggerRoot (")
+        printer.print(")")
+    }
+    //deepClone
+    override fun deepClone(): LoggerRoot   {
+        return LoggerRoot(
+        )
+    }
+    //contexts
+}
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt
index 066be03fb1..e520a29383 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt
@@ -19,7 +19,9 @@ import kotlin.jvm.JvmStatic
  */
 class SynchronizationModel private constructor(
     private val _suspendTimeoutTimer: RdCall,
-    private val _synchronizationSignal: RdSignal
+    private val _initRemoteLogging: RdSignal,
+    private val _synchronizationSignal: RdSignal,
+    private val _stopProcess: RdSignal
 ) : RdExtBase() {
     //companion
     
@@ -49,7 +51,7 @@ class SynchronizationModel private constructor(
         }
         
         
-        const val serializationHash = 3813608056984691311L
+        const val serializationHash = 5881306106692642003L
         
     }
     override val serializersOwner: ISerializersOwner get() = SynchronizationModel
@@ -57,24 +59,36 @@ class SynchronizationModel private constructor(
     
     //fields
     val suspendTimeoutTimer: RdCall get() = _suspendTimeoutTimer
+    val initRemoteLogging: IAsyncSignal get() = _initRemoteLogging
     val synchronizationSignal: IAsyncSignal get() = _synchronizationSignal
+    
+    /**
+     * This command tells the instrumented process to stop
+     */
+    val stopProcess: IAsyncSignal get() = _stopProcess
     //methods
     //initializer
     init {
         _suspendTimeoutTimer.async = true
+        _initRemoteLogging.async = true
         _synchronizationSignal.async = true
+        _stopProcess.async = true
     }
     
     init {
         bindableChildren.add("suspendTimeoutTimer" to _suspendTimeoutTimer)
+        bindableChildren.add("initRemoteLogging" to _initRemoteLogging)
         bindableChildren.add("synchronizationSignal" to _synchronizationSignal)
+        bindableChildren.add("stopProcess" to _stopProcess)
     }
     
     //secondary constructor
     private constructor(
     ) : this(
         RdCall(FrameworkMarshallers.Bool, FrameworkMarshallers.Void),
-        RdSignal(FrameworkMarshallers.String)
+        RdSignal(FrameworkMarshallers.Void),
+        RdSignal(FrameworkMarshallers.String),
+        RdSignal(FrameworkMarshallers.Void)
     )
     
     //equals trait
@@ -84,7 +98,9 @@ class SynchronizationModel private constructor(
         printer.println("SynchronizationModel (")
         printer.indent {
             print("suspendTimeoutTimer = "); _suspendTimeoutTimer.print(printer); println()
+            print("initRemoteLogging = "); _initRemoteLogging.print(printer); println()
             print("synchronizationSignal = "); _synchronizationSignal.print(printer); println()
+            print("stopProcess = "); _stopProcess.print(printer); println()
         }
         printer.print(")")
     }
@@ -92,7 +108,9 @@ class SynchronizationModel private constructor(
     override fun deepClone(): SynchronizationModel   {
         return SynchronizationModel(
             _suspendTimeoutTimer.deepClonePolymorphic(),
-            _synchronizationSignal.deepClonePolymorphic()
+            _initRemoteLogging.deepClonePolymorphic(),
+            _synchronizationSignal.deepClonePolymorphic(),
+            _stopProcess.deepClonePolymorphic()
         )
     }
     //contexts
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt
index 648f17bf41..b696b5f55c 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt
@@ -8,7 +8,7 @@ import java.time.LocalDateTime
 class UtRdConsoleLogger(
     private val loggersLevel: LogLevel,
     private val streamToWrite: PrintStream,
-    private val category: String = ""
+    private val category: String
 ) : Logger {
     override fun isEnabled(level: LogLevel): Boolean {
         return level >= loggersLevel
@@ -16,7 +16,8 @@ class UtRdConsoleLogger(
 
     private fun format(category: String, level: LogLevel, message: Any?, throwable: Throwable?) : String {
         val throwableToPrint = if (level < LogLevel.Error) throwable  else throwable ?: Exception() //to print stacktrace
-        return "${LocalDateTime.now().format(timeFormatter)} | ${level.toString().uppercase().padEnd(5)} | ${category.substringAfterLast('.').padEnd(25)} | ${message?.toString()?:""} ${throwableToPrint?.getThrowableText()?.let { "| $it" }?:""}"
+        val rdCategory = if (category.isNotEmpty()) "RdCategory: ${category.substringAfterLast('.').padEnd(25)} | " else ""
+        return "${LocalDateTime.now().format(timeFormatter)} | ${level.toString().uppercase().padEnd(5)} | $rdCategory${message?.toString()?:""} ${throwableToPrint?.getThrowableText()?.let { "| $it" }?:""}"
     }
 
     override fun log(level: LogLevel, message: Any?, throwable: Throwable?) {
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLoggerFactory.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLoggerFactory.kt
index c34e6a3788..ad2d02a8a1 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLoggerFactory.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLoggerFactory.kt
@@ -5,6 +5,10 @@ import com.jetbrains.rd.util.LogLevel
 import com.jetbrains.rd.util.Logger
 import java.io.PrintStream
 
+/**
+ * Creates loggers with predefined log level, that writes to provided stream.
+ * Create logger category is added to message.
+ */
 class UtRdConsoleLoggerFactory(
     private val loggersLevel: LogLevel,
     private val streamToWrite: PrintStream
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt
index 64c2ded59c..b706757690 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt
@@ -3,25 +3,34 @@ package org.utbot.rd.loggers
 import com.jetbrains.rd.util.*
 import mu.KLogger
 
-class UtRdKLogger(private val realLogger: KLogger): Logger {
-    override fun isEnabled(level: LogLevel): Boolean {
-        return when (level) {
-            LogLevel.Trace -> realLogger.isTraceEnabled
-            LogLevel.Debug -> realLogger.isDebugEnabled
-            LogLevel.Info -> realLogger.isInfoEnabled
-            LogLevel.Warn -> realLogger.isWarnEnabled
-            LogLevel.Error -> realLogger.isErrorEnabled
-            LogLevel.Fatal -> realLogger.isErrorEnabled
+/**
+ * Adapter from RD Logger to KLogger
+ */
+class UtRdKLogger(private val realLogger: KLogger, val category: String) : Logger {
+    val logLevel: LogLevel
+        get() {
+            return when {
+                realLogger.isTraceEnabled -> LogLevel.Trace
+                realLogger.isDebugEnabled -> LogLevel.Debug
+                realLogger.isInfoEnabled -> LogLevel.Info
+                realLogger.isWarnEnabled -> LogLevel.Warn
+                realLogger.isErrorEnabled -> LogLevel.Error
+                else -> LogLevel.Fatal
+            }
         }
+
+    override fun isEnabled(level: LogLevel): Boolean {
+        return level >= logLevel
     }
 
     private fun format(level: LogLevel, message: Any?, throwable: Throwable?): String {
         val throwableToPrint = if (level < LogLevel.Error) throwable else throwable ?: Exception() //to print stacktrace
-        return "${message?.toString() ?: ""} ${throwableToPrint?.getThrowableText()?.let { "| $it" } ?: ""}"
+        val rdCategory = if (category.isNotEmpty()) "RdCategory: ${category.substringAfterLast('.').padEnd(25)} | " else ""
+        return "$rdCategory${message?.toString() ?: ""} ${throwableToPrint?.getThrowableText()?.let { "| $it" } ?: ""}"
     }
 
     override fun log(level: LogLevel, message: Any?, throwable: Throwable?) {
-        if (!isEnabled(level))
+        if (!isEnabled(level) || (message == null && throwable == null))
             return
 
         val msg = format(level, message, throwable)
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLoggerFactory.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLoggerFactory.kt
index 2d0c4ee194..7982c963f3 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLoggerFactory.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLoggerFactory.kt
@@ -4,8 +4,13 @@ import com.jetbrains.rd.util.ILoggerFactory
 import com.jetbrains.rd.util.Logger
 import mu.KLogger
 
+/**
+ * Creates RD loggers that writes to provided KLogger.
+ *
+ * Created logger category is added to message.
+ */
 class UtRdKLoggerFactory(private val realLogger: KLogger) : ILoggerFactory {
     override fun getLogger(category: String): Logger {
-        return UtRdKLogger(realLogger)
+        return UtRdKLogger(realLogger, category)
     }
 }
\ No newline at end of file
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdLogUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdLogUtil.kt
new file mode 100644
index 0000000000..9aae485486
--- /dev/null
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdLogUtil.kt
@@ -0,0 +1,27 @@
+package org.utbot.rd.loggers
+
+import com.jetbrains.rd.util.*
+import com.jetbrains.rd.util.lifetime.Lifetime
+import mu.KLogger
+import org.utbot.common.LoggerWithLogMethod
+
+
+fun Logger.withLevel(logLevel: LogLevel): LoggerWithLogMethod = LoggerWithLogMethod {
+    this.log(logLevel, it)
+}
+
+fun Logger.info(): LoggerWithLogMethod = LoggerWithLogMethod {
+    this.info(it)
+}
+fun Logger.debug(): LoggerWithLogMethod = LoggerWithLogMethod {
+    this.debug(it)
+}
+fun Logger.trace(): LoggerWithLogMethod = LoggerWithLogMethod {
+    this.trace(it)
+}
+
+fun overrideDefaultRdLoggerFactoryWithKLogger(logger: KLogger) {
+    if (Statics().get() !is UtRdKLoggerFactory) {
+        Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger))
+    }
+}
\ No newline at end of file
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdRemoteLogger.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdRemoteLogger.kt
new file mode 100644
index 0000000000..08b397c350
--- /dev/null
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdRemoteLogger.kt
@@ -0,0 +1,62 @@
+package org.utbot.rd.loggers
+
+import com.jetbrains.rd.util.LogLevel
+import com.jetbrains.rd.util.Logger
+import com.jetbrains.rd.util.collections.CountingSet
+import com.jetbrains.rd.util.getThrowableText
+import com.jetbrains.rd.util.threadLocalWithInitial
+import org.utbot.rd.generated.LogArguments
+import org.utbot.rd.generated.LoggerModel
+import org.utbot.rd.startBlocking
+
+class UtRdRemoteLogger(
+    private val loggerModel: LoggerModel,
+    private val category: String
+) : Logger {
+    private val logLevel: LogLevel = logLevelValues[loggerModel.getCategoryMinimalLogLevel.startBlocking(category)]
+
+    companion object {
+        val logLevelValues = LogLevel.values()
+        private val threadLocalExecutingBackingFiled: ThreadLocal> =
+            threadLocalWithInitial { CountingSet() }
+
+        internal val threadLocalExecuting get() = threadLocalExecutingBackingFiled.get()
+    }
+
+    override fun isEnabled(level: LogLevel): Boolean {
+        // On every protocol sends/receives event RD to its own loggers.
+        // They will be redirected here, and then sent via RD to another process,
+        // producing new log event again thus causing infinite recursion.
+        // The solution is to prohibit writing any logs from inside logger.
+        // This is implemented via thread local counter per logger,
+        // which will be incremented when this logger fires event to another process,
+        // and deny all following log events until previous log event is delivered.
+        if (threadLocalExecuting[this] > 0)
+            return false
+
+        return level >= logLevel
+    }
+
+    private fun format(message: Any?, throwable: Throwable?): String {
+        val rdCategory = if (category.isNotEmpty()) "RdCategory: ${category.substringAfterLast('.').padEnd(25)} | " else ""
+        return "$rdCategory${message?.toString() ?: ""}${
+            throwable?.getThrowableText()?.let { "${message?.let { " | " } ?: ""}$it" } ?: ""
+        }"
+    }
+
+    override fun log(level: LogLevel, message: Any?, throwable: Throwable?) {
+        if (!isEnabled(level) || message == null && throwable == null)
+            return
+
+        threadLocalExecuting.add(this, +1)
+        try {
+            val renderedMsg = format(message, throwable)
+            val args = LogArguments(category, level.ordinal, renderedMsg)
+
+            loggerModel.log.fire(args)
+        } finally {
+            threadLocalExecuting.add(this, -1)
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdRemoteLoggerFactory.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdRemoteLoggerFactory.kt
new file mode 100644
index 0000000000..e2024376a3
--- /dev/null
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdRemoteLoggerFactory.kt
@@ -0,0 +1,17 @@
+package org.utbot.rd.loggers
+
+import com.jetbrains.rd.util.ILoggerFactory
+import com.jetbrains.rd.util.Logger
+import org.utbot.rd.generated.LoggerModel
+
+/**
+ * Creates loggers that are mapped to the remote counter-part.
+ * Category is added to message
+*/
+class UtRdRemoteLoggerFactory(
+    private val loggerModel: LoggerModel
+) : ILoggerFactory {
+    override fun getLogger(category: String): Logger {
+        return UtRdRemoteLogger(loggerModel, category)
+    }
+}
\ No newline at end of file
diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt
index 1c89ef0312..532962d55b 100644
--- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt
+++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt
@@ -130,7 +130,6 @@ object EngineProcessModel : Ext(EngineProcessRoot) {
         call("isCancelled", PredefinedType.void, PredefinedType.bool).async
         call("generate", generateParams, generateResult).async
         call("render", renderParams, renderResult).async
-        call("stopProcess", PredefinedType.void, PredefinedType.void).async
         call("obtainClassId", PredefinedType.string, array(PredefinedType.byte)).async
         call("findMethodsInClassMatchingSelected", findMethodsInClassMatchingSelectedArguments, findMethodsInClassMatchingSelectedResult).async
         call("findMethodParamNames", findMethodParamNamesArguments, findMethodParamNamesResult).async
diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt
index 205b95d75f..f66a2a990b 100644
--- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt
+++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt
@@ -64,11 +64,6 @@ object InstrumentedProcessModel : Ext(InstrumentedProcessRoot) {
                     "which declaring class's name is [className].\n" +
                     "@property parameters are the parameters needed for an execution, e.g. static environment"
         }
-        call("StopProcess", PredefinedType.void, PredefinedType.void).apply {
-            async
-            documentation =
-                "This command tells the instrumented process to stop"
-        }
         call("CollectCoverage", CollectCoverageParams, CollectCoverageResult).apply {
             async
             documentation =
diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/LoggerModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/LoggerModel.kt
new file mode 100644
index 0000000000..7ca4d8e6b7
--- /dev/null
+++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/LoggerModel.kt
@@ -0,0 +1,23 @@
+@file:Suppress("unused")
+
+package org.utbot.rd.models
+
+import com.jetbrains.rd.generator.nova.*
+
+object LoggerRoot : Root()
+object LoggerModel : Ext(LoggerRoot) {
+    val logArguments = structdef {
+        field("category", PredefinedType.string)
+        field("logLevelOrdinal", PredefinedType.int).doc("Integer value for com.jetbrains.rd.util.LogLevel")
+        field("message", PredefinedType.string)
+    }
+
+    init {
+        signal("log", logArguments).async
+        call(
+            "getCategoryMinimalLogLevel",
+            PredefinedType.string,
+            PredefinedType.int
+        ).async.doc("Parameter - log category.\nResult - integer value for com.jetbrains.rd.util.LogLevel.")
+    }
+}
\ No newline at end of file
diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt
index 15fa71a260..077173ba23 100644
--- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt
+++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt
@@ -8,6 +8,12 @@ object SynchronizationRoot: Root()
 object SynchronizationModel: Ext(SynchronizationRoot) {
     init {
         call("suspendTimeoutTimer", PredefinedType.bool, PredefinedType.void).async
+        signal("initRemoteLogging", PredefinedType.void).async
         signal("synchronizationSignal", PredefinedType.string).async
+        signal("StopProcess", PredefinedType.void).apply {
+            async
+            documentation =
+                "This command tells the instrumented process to stop"
+        }
     }
 }
\ No newline at end of file
diff --git a/utbot-summary-tests/src/test/kotlin/examples/SummaryTestCaseGeneratorTest.kt b/utbot-summary-tests/src/test/kotlin/examples/SummaryTestCaseGeneratorTest.kt
index 5a7ab647e8..88edc7308c 100644
--- a/utbot-summary-tests/src/test/kotlin/examples/SummaryTestCaseGeneratorTest.kt
+++ b/utbot-summary-tests/src/test/kotlin/examples/SummaryTestCaseGeneratorTest.kt
@@ -3,16 +3,14 @@ package examples
 import org.junit.jupiter.api.*
 import org.utbot.common.WorkaroundReason
 import org.utbot.common.workaround
-import org.utbot.framework.SummariesGenerationType
 import org.utbot.framework.UtSettings.checkNpeInNestedMethods
 import org.utbot.framework.UtSettings.checkNpeInNestedNotPrivateMethods
 import org.utbot.framework.UtSettings.checkSolverTimeoutMillis
-import org.utbot.framework.UtSettings.summaryGenerationType
 import org.utbot.framework.plugin.api.*
 import org.utbot.framework.plugin.api.util.UtContext
 import org.utbot.framework.plugin.api.util.executableId
 import org.utbot.summary.comment.nextSynonyms
-import org.utbot.summary.summarize
+import org.utbot.summary.summarizeAll
 import org.utbot.testing.CoverageMatcher
 import org.utbot.testing.TestExecution
 import org.utbot.testing.UtValueTestCaseChecker
@@ -60,7 +58,7 @@ open class SummaryTestCaseGeneratorTest(
             checkNpeInNestedNotPrivateMethods = true
         }
         val testSet = executionsModel(method.executableId, mockStrategy)
-        val testSetWithSummarization = testSet.summarize(searchDirectory, sourceFile = null)
+        val testSetWithSummarization = listOf(testSet).summarizeAll(searchDirectory, sourceFile = null).single()
 
         testSetWithSummarization.executions.checkMatchersWithTextSummary(summaryKeys)
         testSetWithSummarization.executions.checkMatchersWithMethodNames(methodNames)
diff --git a/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt b/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt
index a2b0cc6111..d53f59b766 100644
--- a/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt
+++ b/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt
@@ -16,6 +16,8 @@ import org.utbot.summary.name.SimpleNameBuilder
 import java.io.File
 import java.nio.file.Path
 import mu.KotlinLogging
+import org.utbot.common.measureTime
+import org.utbot.common.info
 import org.utbot.framework.SummariesGenerationType.*
 import org.utbot.framework.UtSettings.enableClusterCommentsGeneration
 import org.utbot.framework.UtSettings.enableJavaDocGeneration
@@ -36,7 +38,17 @@ import soot.SootMethod
 
 private val logger = KotlinLogging.logger {}
 
-fun UtMethodTestSet.summarize(searchDirectory: Path, sourceFile: File?): UtMethodTestSet {
+fun Collection.summarizeAll(searchDirectory: Path, sourceFile: File?): List = logger.info().measureTime({
+    "----------------------------------------------------------------------------------------\n" +
+    "-------------------Summarization started for ${this.size} test cases--------------------\n" +
+    "----------------------------------------------------------------------------------------"
+    }) {
+    this.map {
+        it.summarizeOne(searchDirectory, sourceFile)
+    }
+}
+
+private fun UtMethodTestSet.summarizeOne(searchDirectory: Path, sourceFile: File?): UtMethodTestSet = logger.info().measureTime({ "Summarization for ${this.method}"} ){
     if (summaryGenerationType == NONE) return this
 
     val sourceFileToAnalyze = sourceFile
@@ -46,34 +58,29 @@ fun UtMethodTestSet.summarize(searchDirectory: Path, sourceFile: File?): UtMetho
             NONE -> null
         }
 
-    return try {
-        makeDiverseExecutions(this)
+    makeDiverseExecutions(this)
 
-        // HACK: we avoid calling [invokeDescriptions] method to save time, it is useless in Contest
-        val invokeDescriptions = when (summaryGenerationType) {
-            FULL -> invokeDescriptions(this, searchDirectory)
-            LIGHT,
-            NONE -> emptyList()
-        }
+    // HACK: we avoid calling [invokeDescriptions] method to save time, it is useless in Contest
+    val invokeDescriptions = when (summaryGenerationType) {
+        FULL -> invokeDescriptions(this, searchDirectory)
+        LIGHT,
+        NONE -> emptyList()
+    }
 
-        // every cluster has summary and list of executions
-        val executionClusters = Summarization(sourceFileToAnalyze, invokeDescriptions).fillSummaries(this)
-        val updatedExecutions = executionClusters.flatMap { it.executions }
-        var pos = 0
-        val clustersInfo = executionClusters.map {
-            val clusterSize = it.executions.size
-            val indices = pos until (pos + clusterSize)
-            pos += clusterSize
-            it.clusterInfo to indices
-        }
-        this.copy(
+    // every cluster has summary and list of executions
+    val executionClusters = Summarization(sourceFileToAnalyze, invokeDescriptions).fillSummaries(this)
+    val updatedExecutions = executionClusters.flatMap { it.executions }
+    var pos = 0
+    val clustersInfo = executionClusters.map {
+        val clusterSize = it.executions.size
+        val indices = pos until (pos + clusterSize)
+        pos += clusterSize
+        it.clusterInfo to indices
+    }
+    return this.copy(
             executions = updatedExecutions,
             clustersInfo = clustersInfo
         ) // TODO: looks weird and don't create the real copy
-    } catch (e: Throwable) {
-        logger.info(e) { "Summary generation error: ${e.message}" }
-        this
-    }
 }
 
 class Summarization(val sourceFile: File?, val invokeDescriptions: List) {
diff --git a/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt b/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt
index 82194912f0..571dce5112 100644
--- a/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt
+++ b/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt
@@ -3,7 +3,7 @@ package org.utbot.testing
 import mu.KotlinLogging
 import org.junit.jupiter.api.Assertions.assertTrue
 import org.utbot.common.FileUtil
-import org.utbot.common.bracket
+import org.utbot.common.measureTime
 import org.utbot.common.info
 import org.utbot.framework.codegen.CodeGenerator
 import org.utbot.framework.codegen.CodeGeneratorResult
@@ -49,7 +49,7 @@ class TestCodeGeneratorPipeline(private val testFrameworkConfiguration: TestFram
 
         logger
             .info()
-            .bracket("Executing code generation tests for [$classesPipelinesNames]") {
+            .measureTime({ "Executing code generation tests for [$classesPipelinesNames]" }) {
                 CodeGeneration.filterPipelines(classesPipelines).forEach {
                     withUtContext(UtContext(it.stageContext.classUnderTest.java.classLoader)) {
                         processCodeGenerationStage(it)
diff --git a/utbot-testing/src/main/kotlin/org/utbot/testing/UtValueTestCaseChecker.kt b/utbot-testing/src/main/kotlin/org/utbot/testing/UtValueTestCaseChecker.kt
index 9b64a53c2f..d78e29ee7d 100644
--- a/utbot-testing/src/main/kotlin/org/utbot/testing/UtValueTestCaseChecker.kt
+++ b/utbot-testing/src/main/kotlin/org/utbot/testing/UtValueTestCaseChecker.kt
@@ -42,7 +42,7 @@ import org.utbot.framework.plugin.api.util.kClass
 import org.utbot.framework.plugin.api.util.withUtContext
 import org.utbot.framework.util.Conflict
 import org.utbot.framework.util.toValueTestCase
-import org.utbot.summary.summarize
+import org.utbot.summary.summarizeAll
 import org.utbot.testcheckers.ExecutionsNumberMatcher
 import java.io.File
 import java.nio.file.Path
@@ -1730,7 +1730,7 @@ abstract class UtValueTestCaseChecker(
             } else {
                 walk(executableId, mockStrategy, additionalDependenciesClassPath)
             }
-            testSet.summarize(searchDirectory, sourceFile = null)
+            listOf(testSet).summarizeAll(searchDirectory, sourceFile = null)
             val valueTestCase = testSet.toValueTestCase()
 
             assertTrue(testSet.errors.isEmpty()) {