Skip to content

Commit

Permalink
Start Spring analyzer process from engine process to use RD logger
Browse files Browse the repository at this point in the history
  • Loading branch information
IlyaMuravjov committed Apr 3, 2023
1 parent 52efe90 commit 102c652
Show file tree
Hide file tree
Showing 25 changed files with 472 additions and 345 deletions.
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pytorchNativeVersion=1.9.1
shadowJarVersion=7.1.2
openblasVersion=0.3.10-1.5.4
arpackNgVersion=3.7.0-1.5.4
commonsLoggingVersion=1.2

# configuration for build server
#
Expand Down
21 changes: 21 additions & 0 deletions utbot-core/src/main/kotlin/org/utbot/common/StandardStreamUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.utbot.common

import java.io.OutputStream
import java.io.PrintStream

fun silentlyCloseStandardStreams() {
// 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() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -297,13 +297,6 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS
// endregion

// region spring analyzer process debug
/**
* Path to custom log4j2 configuration file for SpringAnalyzerProcess.
* By default utbot-intellij/src/main/resources/log4j2.xml is used.
* Also default value is used if provided value is not a file.
*/
var springAnalyzerProcessLogConfigFile by getStringProperty("")

/**
* The property is useful only for the IntelliJ IDEs.
* If the property is set in true the spring analyzer process opens a debug port.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.utbot.framework.process

import org.utbot.common.osSpecificJavaExecutable
import org.utbot.framework.plugin.services.JdkInfoService
import org.utbot.rd.rdPortArgument
import java.io.File
import java.nio.file.Path
import kotlin.io.path.pathString

private val javaExecutablePathString: Path
get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}")

// TODO use it in EngineProcess and InstrumentedProcess
fun withCommonProcessCommandLineArgs(
processSpecificArgs: List<String>,
debugPort: Int,
runWithDebug: Boolean,
suspendExecutionInDebugMode: Boolean,
rdPort: Int
): List<String> = buildList {
val suspendValue = if (suspendExecutionInDebugMode) "y" else "n"
val debugArgument = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue},quiet=y,address=$debugPort"
.takeIf { runWithDebug }

add(javaExecutablePathString.pathString)
val javaVersionSpecificArgs = OpenModulesContainer.javaVersionSpecificArguments
if (javaVersionSpecificArgs.isNotEmpty()) {
addAll(javaVersionSpecificArgs)
}
debugArgument?.let { add(it) }
addAll(processSpecificArgs)
add(rdPortArgument(rdPort))
}
2 changes: 2 additions & 0 deletions utbot-framework/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ dependencies {
api project(':utbot-framework-api')
api project(':utbot-rd')

implementation project(':utbot-spring-analyzer-model')

implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: rdVersion
implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import org.utbot.rd.RdSettingsContainerFactory
import org.utbot.rd.findRdPort
import org.utbot.rd.generated.settingsModel
import org.utbot.rd.loggers.UtRdKLoggerFactory
import org.utbot.rd.terminateOnException
import org.utbot.sarif.RdSourceFindingStrategyFacade
import org.utbot.sarif.SarifReport
import org.utbot.summary.summarizeAll
Expand Down Expand Up @@ -79,6 +80,20 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
File(it).toURI().toURL()
}.toTypedArray())))
}
watchdog.measureTimeForActiveCall(getSpringBeanQualifiedNames, "Getting Spring bean definitions") { params ->
val springAnalyzerProcess = SpringAnalyzerProcess.createBlocking()
val beans = springAnalyzerProcess.terminateOnException { _ ->
springAnalyzerProcess.getBeanQualifiedNames(
params.classpath.toList(),
params.config,
// TODO remove once spring-analyzer learns to find resources on its own, temporarily leaving it here for testing with hardcoded absolute paths
propertyFilesPaths = emptyList(),
xmlConfigurationPaths = emptyList()
).toTypedArray()
}
springAnalyzerProcess.terminate()
beans
}
watchdog.measureTimeForActiveCall(createTestGenerator, "Creating Test Generator") { params ->
AnalyticsConfigureUtil.configureML()
Instrumenter.adapter = RdInstrumenter(realProtocol.rdInstrumenterAdapter)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.utbot.framework.process

import com.jetbrains.rd.util.lifetime.LifetimeDefinition
import kotlinx.coroutines.runBlocking
import mu.KotlinLogging
import org.apache.commons.io.FileUtils
import org.utbot.common.getPid
import org.utbot.common.utBotTempDirectory
import org.utbot.framework.UtSettings
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.setupRdLogger
import org.utbot.rd.onSchedulerBlocking
import org.utbot.rd.startBlocking
import org.utbot.rd.startUtProcessWithRdServer
import org.utbot.rd.terminateOnException
import org.utbot.spring.process.generated.SpringAnalyzerParams
import org.utbot.spring.process.generated.SpringAnalyzerProcessModel
import org.utbot.spring.process.generated.springAnalyzerProcessModel
import java.nio.file.Files

class SpringAnalyzerProcessInstantDeathException :
InstantProcessDeathException(UtSettings.springAnalyzerProcessDebugPort, UtSettings.runSpringAnalyzerProcessWithDebug)

private const val SPRING_ANALYZER_JAR_FILENAME = "utbot-spring-analyzer-shadow.jar"
private val logger = KotlinLogging.logger {}
private val rdLogger = UtRdKLogger(logger, "")

class SpringAnalyzerProcess private constructor(
rdProcess: ProcessWithRdServer
) : ProcessWithRdServer by rdProcess {

companion object {
private fun obtainProcessSpecificCommandLineArgs(): List<String> {
val jarFile =
Files.createDirectories(utBotTempDirectory.toFile().resolve("spring-analyzer").toPath())
.toFile().resolve(SPRING_ANALYZER_JAR_FILENAME)
FileUtils.copyURLToFile(
this::class.java.classLoader.getResource("lib/$SPRING_ANALYZER_JAR_FILENAME"),
jarFile
)
return listOf(
"-Dorg.apache.commons.logging.LogFactory=org.utbot.rd.loggers.RDApacheCommonsLogFactory",
"-jar",
jarFile.path
)
}

fun createBlocking() = runBlocking { SpringAnalyzerProcess() }

suspend operator fun invoke(): SpringAnalyzerProcess = LifetimeDefinition().terminateOnException { lifetime ->
val rdProcess = startUtProcessWithRdServer(lifetime) { port ->
val cmd = withCommonProcessCommandLineArgs(
obtainProcessSpecificCommandLineArgs(),
debugPort = UtSettings.springAnalyzerProcessDebugPort,
runWithDebug = UtSettings.runSpringAnalyzerProcessWithDebug,
suspendExecutionInDebugMode = UtSettings.suspendSpringAnalyzerProcessExecutionInDebugMode,
rdPort = port
)
val process = ProcessBuilder(cmd)
.directory(Files.createTempDirectory(utBotTempDirectory, "spring-analyzer").toFile())
.start()

logger.info { "Spring Analyzer process started with PID = ${process.getPid}" }

if (!process.isAlive) throw SpringAnalyzerProcessInstantDeathException()

process
}
rdProcess.awaitProcessReady()
val proc = SpringAnalyzerProcess(rdProcess)
setupRdLogger(rdProcess, proc.loggerModel, rdLogger)
return proc
}
}

private val springAnalyzerModel: SpringAnalyzerProcessModel = onSchedulerBlocking { protocol.springAnalyzerProcessModel }
private val loggerModel: LoggerModel = onSchedulerBlocking { protocol.loggerModel }

init {
lifetime.onTermination {
protocol.synchronizationModel.stopProcess.fire(Unit)
}
}

fun getBeanQualifiedNames(
classpath: List<String>,
configuration: String,
propertyFilesPaths: List<String>,
xmlConfigurationPaths: List<String>
): List<String> {
val params = SpringAnalyzerParams(
classpath.toTypedArray(),
configuration,
propertyFilesPaths.toTypedArray(),
xmlConfigurationPaths.toTypedArray()
)
val result = springAnalyzerModel.analyze.startBlocking(params)
return result.beanTypes.toList()
}
}
Loading

0 comments on commit 102c652

Please sign in to comment.