diff --git a/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt new file mode 100644 index 0000000000..50ad3db83d --- /dev/null +++ b/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt @@ -0,0 +1,5 @@ +package org.utbot.common + +private val javaSpecificationVersion = System.getProperty("java.specification.version") +val isJvm8 = javaSpecificationVersion.equals("1.8") +val isJvm9Plus = !javaSpecificationVersion.contains(".") && javaSpecificationVersion.toInt() >= 9 diff --git a/utbot-core/src/main/kotlin/org/utbot/common/OsUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/OsUtil.kt new file mode 100644 index 0000000000..3c8a1b53d6 --- /dev/null +++ b/utbot-core/src/main/kotlin/org/utbot/common/OsUtil.kt @@ -0,0 +1,8 @@ +package org.utbot.common + +import java.util.* + +private val os = System.getProperty("os.name").lowercase(Locale.getDefault()) +val isWindows = os.startsWith("windows") +val isUnix = !isWindows +val isMac = os.startsWith("mac") \ No newline at end of file diff --git a/utbot-core/src/main/kotlin/org/utbot/common/ProcessUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/ProcessUtil.kt new file mode 100644 index 0000000000..a0a4b136c3 --- /dev/null +++ b/utbot-core/src/main/kotlin/org/utbot/common/ProcessUtil.kt @@ -0,0 +1,70 @@ +package org.utbot.common + +import com.sun.jna.Library +import com.sun.jna.Native +import com.sun.jna.Pointer +import com.sun.jna.platform.win32.Kernel32 +import com.sun.jna.platform.win32.WinNT + +/** + * working pid for jvm 8 and 9+ + */ +val Process.getPid: Long + get() = try { + if (isJvm9Plus) { + // because we cannot reference Java9+ API here + ClassLoader.getSystemClassLoader().loadClass("java.lang.Process").getDeclaredMethod("pid").invoke(this) as Long + } else { + when (javaClass.name) { + "java.lang.UNIXProcess" -> { + val fPid = javaClass.getDeclaredField("pid") + fPid.withAccessibility { fPid.getLong(this) } + + } + + "java.lang.Win32Process", "java.lang.ProcessImpl" -> { + val fHandle = javaClass.getDeclaredField("handle") + fHandle.withAccessibility { + val handle = fHandle.getLong(this) + val winntHandle = WinNT.HANDLE() + winntHandle.pointer = Pointer.createConstant(handle) + Kernel32.INSTANCE.GetProcessId(winntHandle).toLong() + } + } + + else -> -2 + } + } + } catch (e: Exception) { + -1 + } + +private interface CLibrary : Library { + fun getpid(): Int + + companion object { + val INSTANCE = Native.load("c", CLibrary::class.java) as CLibrary + } +} + +/** + * working for jvm 8 and 9+ + */ +val currentProcessPid: Long + get() = + try { + if (isJvm9Plus) { + ClassLoader.getSystemClassLoader().loadClass("java.lang.ProcessHandle").let { + val handle = it.getDeclaredMethod("current").invoke(it) + it.getDeclaredMethod("pid").invoke(handle) as Long + } + } else { + if (isWindows) { + Kernel32.INSTANCE.GetCurrentProcessId() + } else { + CLibrary.INSTANCE.getpid() + }.toLong() + } + } catch (e: Throwable) { + -1 + } \ No newline at end of file diff --git a/utbot-instrumentation/build.gradle b/utbot-instrumentation/build.gradle index cccb18fe61..c1671d2a49 100644 --- a/utbot-instrumentation/build.gradle +++ b/utbot-instrumentation/build.gradle @@ -13,6 +13,7 @@ dependencies { implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: '2022.3.1' implementation group: 'com.jetbrains.rd', name: 'rd-core', version: '2022.3.1' + implementation group: 'net.java.dev.jna', name: 'jna-platform', version: '5.5.0' // TODO: this is necessary for inline classes mocking in UtExecutionInstrumentation diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt index 918b533d6a..679609d8cd 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt @@ -13,7 +13,7 @@ import com.jetbrains.rd.util.lifetime.plusAssign import com.jetbrains.rd.util.threading.SingleThreadScheduler import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel -import org.utbot.common.scanForClasses +import org.utbot.common.* import org.utbot.framework.plugin.api.util.UtContext import org.utbot.instrumentation.agent.Agent import org.utbot.instrumentation.instrumentation.Instrumentation @@ -64,7 +64,7 @@ private object HandlerClassesLoader : URLClassLoader(emptyArray()) { private typealias ChildProcessLogLevel = LogLevel -private val logLevel = ChildProcessLogLevel.Trace +private val logLevel = ChildProcessLogLevel.Info // Logging private val dateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS") @@ -116,7 +116,7 @@ suspend fun main(args: Array) = runBlocking { ?.run { split("=").last().toInt().coerceIn(1..65535) } ?: throw IllegalArgumentException("No port provided") - val pid = ProcessHandle.current().pid().toInt() + val pid = currentProcessPid.toInt() val def = LifetimeDefinition() SingleThreadScheduler(Lifetime.Eternal, "") @@ -146,6 +146,7 @@ suspend fun main(args: Array) = runBlocking { lifetime += { logInfo { "lifetime terminated" } } try { logInfo {"pid - $pid"} + logInfo {"isJvm8 - $isJvm8, isJvm9Plus - $isJvm9Plus, isWindows - $isWindows"} initiate(lifetime, port, pid) } finally { val syncFile = File(processSyncDirectory, childCreatedFileName(pid)) @@ -264,11 +265,6 @@ private suspend fun initiate(lifetime: Lifetime, port: Int, pid: Int) { SocketWire.Client(lifetime, scheduler, port), lifetime ) - logInfo { - "heartbeatAlive - ${clientProtocol.wire.heartbeatAlive.value}, connected - ${ - clientProtocol.wire.connected.value - }" - } val (sync, protocolModel) = obtainClientIO(lifetime, clientProtocol) protocolModel.setup(kryoHelper) { @@ -279,6 +275,7 @@ private suspend fun initiate(lifetime: Lifetime, port: Int, pid: Int) { val answerFromMainProcess = sync.adviseForConditionAsync(lifetime) { if (it == "main") { + logTrace { "received from main" } measureExecutionForTermination { sync.fire("child") } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt index 82ba3ad898..10dc7b5cc3 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt @@ -62,7 +62,7 @@ class ChildProcessRunner { .directory(directory) return processBuilder.start().also { - logger.debug { "Process started with PID=${it.pid()}" } + logger.debug { "Process started with PID=${it.getPid}" } if (UtSettings.logConcreteExecutionErrors) { logger.debug { "Child process error log: ${errorLogFile.absolutePath}" } @@ -99,7 +99,7 @@ class ChildProcessRunner { run { logger.debug("Trying to find jar in the resources.") val tempDir = utBotTempDirectory.toFile() - val unzippedJarName = "$UTBOT_INSTRUMENTATION-${ProcessHandle.current().pid()}.jar" + val unzippedJarName = "$UTBOT_INSTRUMENTATION-${currentProcessPid}.jar" val instrumentationJarFile = File(tempDir, unzippedJarName) ChildProcessRunner::class.java.classLoader diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt index cb608bfe13..47ca22e050 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt @@ -6,6 +6,7 @@ import com.jetbrains.rd.util.lifetime.Lifetime import com.jetbrains.rd.util.lifetime.isAlive import kotlinx.coroutines.delay import mu.KotlinLogging +import org.utbot.common.getPid import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.process.ChildProcessRunner import org.utbot.instrumentation.rd.generated.AddPathsParams @@ -53,7 +54,7 @@ class UtInstrumentationProcess private constructor( // 1. child process will create file "${processId}.created" - this indicates that child process is ready to receive messages // 2. we will test the connection via sync RdSignal // only then we can successfully start operating - val pid = process.toHandle().pid().toInt() + val pid = process.getPid.toInt() val syncFile = File(processSyncDirectory, childCreatedFileName(pid)) while (lifetime.isAlive) {