Daemon log, controlling it from launching, proper stdout/err redirection technique, params to control jvm options of the daemon

This commit is contained in:
ligee
2015-08-18 17:44:20 +02:00
committed by Ilya Chernikov
parent 39d6592e1f
commit 5fee180d09
4 changed files with 108 additions and 17 deletions
@@ -70,12 +70,13 @@ public class KotlinCompilerClient {
}
private fun startDaemon(compilerId: CompilerId, daemonOptions: DaemonOptions, errStream: PrintStream) {
private fun startDaemon(compilerId: CompilerId, daemonLaunchingOptions: DaemonLaunchingOptions, daemonOptions: DaemonOptions, errStream: PrintStream) {
val javaExecutable = listOf(System.getProperty("java.home"), "bin", "java").joinToString(File.separator)
// TODO: add some specific environment variables to the cp and may be command line, to allow some specific startup configs
val args = listOf(javaExecutable,
"-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator),
COMPILER_DAEMON_CLASS_FQN) +
"-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) +
daemonLaunchingOptions.jvmParams +
COMPILER_DAEMON_CLASS_FQN +
daemonOptions.asParams +
compilerId.asParams
errStream.println("[daemon client] starting the daemon as: " + args.joinToString(" "))
@@ -131,7 +132,7 @@ public class KotlinCompilerClient {
(localId.compilerDigest.isEmpty() || remoteId.compilerDigest.isEmpty() || localId.compilerDigest == remoteId.compilerDigest)
}
public fun connectToCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions, errStream: PrintStream, autostart: Boolean = true, checkId: Boolean = true): CompileService? {
public fun connectToCompileService(compilerId: CompilerId, daemonLaunchingOptions: DaemonLaunchingOptions, daemonOptions: DaemonOptions, errStream: PrintStream, autostart: Boolean = true, checkId: Boolean = true): CompileService? {
val service = connectToService(compilerId, daemonOptions, errStream)
if (service != null) {
if (!checkId || checkCompilerId(service, compilerId, errStream)) {
@@ -151,7 +152,7 @@ public class KotlinCompilerClient {
else errStream.println("[daemon client] cannot connect to Compile Daemon, trying to start")
}
startDaemon(compilerId, daemonOptions, errStream)
startDaemon(compilerId, daemonLaunchingOptions, daemonOptions, errStream)
errStream.println("[daemon client] daemon started, trying to connect")
return connectToService(compilerId, daemonOptions, errStream)
}
@@ -187,8 +188,9 @@ public class KotlinCompilerClient {
platformStatic public fun main(vararg args: String) {
val compilerId = CompilerId()
val daemonOptions = DaemonOptions()
val daemonLaunchingOptions = DaemonLaunchingOptions()
val clientOptions = ClientOptions()
val filteredArgs = args.asIterable().propParseFilter(compilerId, daemonOptions, clientOptions)
val filteredArgs = args.asIterable().propParseFilter(compilerId, daemonOptions, daemonLaunchingOptions, clientOptions)
if (!clientOptions.stop) {
if (compilerId.compilerClasspath.none()) {
@@ -214,7 +216,7 @@ public class KotlinCompilerClient {
compilerId.updateDigest()
}
connectToCompileService(compilerId, daemonOptions, System.out, autostart = !clientOptions.stop, checkId = !clientOptions.stop)?.let {
connectToCompileService(compilerId, daemonLaunchingOptions, daemonOptions, System.out, autostart = !clientOptions.stop, checkId = !clientOptions.stop)?.let {
when {
clientOptions.stop -> {
println("Shutdown the daemon")
@@ -78,6 +78,18 @@ public fun Iterable<String>.propParseFilter(vararg cs: CmdlineParams) : Iterable
propParseFilter(cs.flatMap { it.parsers })
public data class DaemonLaunchingOptions(
public var jvmParams: List<String> = listOf()
) : CmdlineParams {
override val asParams: Iterable<String>
get() =
propToParams(::jvmParams, { it.joinToString("##") }) // TODO: consider some other options rather than using potentially dangerous delimiter
override val parsers: List<PropParser<*,*,*>>
get() = listOf( PropParser(this, ::jvmParams, { it.split("##")})) // TODO: see appropriate comment in asParams
}
public data class DaemonOptions(
public var port: Int = COMPILE_DAEMON_DEFAULT_PORT,
public var autoshutdownMemoryThreshold: Long = 0 /* 0 means unchecked */,
@@ -17,19 +17,83 @@
package org.jetbrains.kotlin.rmi.service
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.rmi.COMPILE_DAEMON_DEFAULT_PORT
import org.jetbrains.kotlin.rmi.CompilerId
import org.jetbrains.kotlin.rmi.DaemonOptions
import org.jetbrains.kotlin.rmi.propParseFilter
import org.jetbrains.kotlin.service.CompileServiceImpl
import java.io.OutputStream
import java.io.PrintStream
import java.rmi.RMISecurityManager
import java.rmi.registry.LocateRegistry
import java.text.SimpleDateFormat
import java.util.*
import java.util.logging.LogManager
import java.util.logging.Logger
import kotlin.platform.platformStatic
class LogStream(name: String) : OutputStream() {
val log by lazy { Logger.getLogger(name) }
val lineBuf = StringBuilder()
override fun write(byte: Int) {
if (byte.toChar() == '\n') flush()
else lineBuf.append(byte.toChar())
}
override fun write(data: ByteArray, offset: Int, length: Int) {
var ofs = offset
var lineStart = ofs
while (ofs < length) {
if (data[ofs].toChar() == '\n') {
flush(data, lineStart, ofs - lineStart)
lineStart = ofs + 1
}
ofs++
}
if (lineStart < length)
lineBuf.append(data, lineStart, length - lineStart)
}
fun flush(data: ByteArray, offset: Int, length: Int) {
if (offset == 0 && length == data.size())
log.info(lineBuf.toString() + data.toString())
else
log.info(lineBuf.toString() + data.toString().substring(offset, length))
lineBuf.setLength(0)
}
override fun flush() {
log.info(lineBuf.toString())
lineBuf.setLength(0)
}
}
public class CompileDaemon {
companion object {
init {
val logPath: String = System.getProperty("kotlin.daemon.log.path")?.trimEnd('/','\\') ?: "%t"
val logTime: String = SimpleDateFormat("yyyy-MM-dd.HH-mm-ss-SSS").format(Date())
val cfg: String =
"handlers = java.util.logging.FileHandler\n" +
"java.util.logging.FileHandler.level = ALL\n" +
"java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter\n" +
"java.util.logging.FileHandler.encoding = UTF-8\n" +
"java.util.logging.FileHandler.limit = 1073741824\n" + // 1Mb
"java.util.logging.FileHandler.count = 3\n" +
"java.util.logging.FileHandler.append = false\n" +
"java.util.logging.FileHandler.pattern = $logPath/kotlin-daemon.$logTime.%g.log\n" +
"java.util.logging.SimpleFormatter.format = %1\$tF %1\$tT.%1\$tL [%3\$s] %4\$s: %5\$s\\n\n"
LogManager.getLogManager().readConfiguration(cfg.byteInputStream())
}
val log by lazy { Logger.getLogger("daemon") }
platformStatic public fun main(args: Array<String>) {
val compilerId = CompilerId()
@@ -37,10 +101,14 @@ public class CompileDaemon {
val filteredArgs = args.asIterable().propParseFilter(compilerId, daemonOptions)
if (filteredArgs.any()) {
println("usage: <daemon> <compilerId options> <daemon options>")
val helpLine = "usage: <daemon> <compilerId options> <daemon options>"
log.info(helpLine)
println(helpLine)
throw IllegalArgumentException("Unknown arguments")
}
log.info("starting daemon")
// TODO: find minimal set of permissions and restore security management
// if (System.getSecurityManager() == null)
// System.setSecurityManager (RMISecurityManager())
@@ -54,6 +122,13 @@ public class CompileDaemon {
if (daemonOptions.startEcho.isNotEmpty())
println(daemonOptions.startEcho)
// this stops redirected streams reader(s) on the client side and prevent some situations with hanging threads
System.out.close()
System.err.close()
System.setErr(PrintStream(LogStream("stderr")))
System.setOut(PrintStream(LogStream("stdout")))
}
}
}
@@ -48,6 +48,8 @@ class CompileServiceImpl<Compiler: CLICompiler<*>>(
val daemonOptions: DaemonOptions
) : CompileService, UnicastRemoteObject() {
val log by lazy { Logger.getLogger("compiler") }
private val rwlock = ReentrantReadWriteLock()
private var alive = false
@@ -118,19 +120,19 @@ class CompileServiceImpl<Compiler: CLICompiler<*>>(
try {
if (args.none())
throw IllegalArgumentException("Error: empty arguments list.")
println("Starting compilation with args: " + args.joinToString(" "))
log.info("Starting compilation with args: " + args.joinToString(" "))
val startMem = usedMemory() / 1024
val startTime = System.nanoTime()
val res = body()
val endTime = System.nanoTime()
val endMem = usedMemory() / 1024
println("Done")
println("Elapsed time: " + TimeUnit.NANOSECONDS.toMillis(endTime - startTime) + " ms")
println("Used memory: $endMem kb (${"%+d".format(endMem - startMem)} kb)")
log.info("Done")
log.info("Elapsed time: " + TimeUnit.NANOSECONDS.toMillis(endTime - startTime) + " ms")
log.info("Used memory: $endMem kb (${"%+d".format(endMem - startMem)} kb)")
return res
}
catch (e: Exception) {
println("Error: $e")
log.info("Error: $e")
throw e
}
}
@@ -147,7 +149,7 @@ class CompileServiceImpl<Compiler: CLICompiler<*>>(
fun<R> spy(msg: String, body: () -> R): R {
val res = body()
println(msg + " = " + res.toString())
log.info(msg + " = " + res.toString())
return res
}
@@ -157,10 +159,10 @@ class CompileServiceImpl<Compiler: CLICompiler<*>>(
override fun shutdown() {
ifAliveExclusive {
println("Shutdown started")
log.info("Shutdown started")
alive = false
UnicastRemoteObject.unexportObject(this, true)
println("Shutdown complete")
log.info("Shutdown complete")
}
}