Commit 9c395884 authored by Kunshan Wang's avatar Kunshan Wang

Allow logback XML configuration

This will give the user more control over logging in Holstein.
Specifically, the XML configuration can configure appenders so that logs
can be written into stderr instead of stdout.
parent 9162db02
...@@ -241,7 +241,20 @@ globalSize must be a multiple of 32768 bytes (32K).* ...@@ -241,7 +241,20 @@ globalSize must be a multiple of 32768 bytes (32K).*
[mu-client-pypy](https://gitlab.anu.edu.au/mu/mu-client-pypy/) project to [mu-client-pypy](https://gitlab.anu.edu.au/mu/mu-client-pypy/) project to
work. default: false work. default: false
*Log levels can be: ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF. Case-insensitive. Holstein uses the [logback](https://logback.qos.ch/) framework as its logging
backend. Logs can be configured using XML configuration by specifying the
following option:
- **logbackConfig**: The path to the logback XML configuration file.
Example:
```
./tools/runmu.sh --logbackConfig=./logging/logging.xml -- ./target/boot-image-echo.muref Hello world!
```
Alternatively, you can set the log level using the following options. *Log
levels can be: ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF. Case-insensitive.
Setting to WARN should get rid of most logging information, except the serious Setting to WARN should get rid of most logging information, except the serious
ones.* The default log level is DEBUG. ones.* The default log level is DEBUG.
......
<!-- This is an example. Make a copy of this file, and modify it for your need. -->
<configuration>
<!-- Set debug="true" to enable debugging for the logback logging framework itself -->
<!-- configuration debug="true" -->
<!-- This appender writes into stdout -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="uvm.refimpl.itpr" level="DEBUG" />
<!-- This appender writes into stderr -->
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
<target>System.err</target>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Loggers are organised by packages. uvm includes everything in Holstein. -->
<logger name="uvm" level="INFO" />
<logger name="uvm.refimpl.itpr" level="INFO" /> <!-- Set this to DEBUG to print execution traces. -->
<logger name="uvm.refimpl.mem" level="INFO" /> <!-- Set this to DEBUG to print GC logs. -->
<root level="INFO">
<appender-ref ref="STDERR" />
</root>
</configuration>
<!--
vim: ts=2 sw=2 sts=2 et
-->
...@@ -8,13 +8,15 @@ package uvm.refimpl ...@@ -8,13 +8,15 @@ package uvm.refimpl
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import uvm.refimpl.mem.TypeSizes._ import uvm.refimpl.mem.TypeSizes._
import org.slf4j.LoggerFactory
import ch.qos.logback.classic.joran.JoranConfigurator
import ch.qos.logback.classic.LoggerContext
class VMOption[+T]( class VMOption[+T](
val name: String, val name: String,
val desc: String, val desc: String,
val parser: String => T, val parser: String => T,
val default: T val default: T) {
) {
def maybeParse(maybeStr: Option[String]): T = { def maybeParse(maybeStr: Option[String]): T = {
maybeStr.map(parser).getOrElse(default) maybeStr.map(parser).getOrElse(default)
} }
...@@ -30,19 +32,19 @@ object VMConfParser { ...@@ -30,19 +32,19 @@ object VMConfParser {
case 'M' => Some(1024L * 1024L) case 'M' => Some(1024L * 1024L)
case 'G' => Some(1024L * 1024L * 1024L) case 'G' => Some(1024L * 1024L * 1024L)
case 'T' => Some(1024L * 1024L * 1024L * 1024L) case 'T' => Some(1024L * 1024L * 1024L * 1024L)
case _ => None case _ => None
} }
multiplier match { multiplier match {
case Some(m) => str.init.toLong * m case Some(m) => str.init.toLong * m
case None => str.toLong case None => str.toLong
} }
} }
def parseBoolean(str: String): Boolean = { def parseBoolean(str: String): Boolean = {
str.toLowerCase().toBoolean str.toLowerCase().toBoolean
} }
def parseColonSeparatedList(str: String): Seq[String] = { def parseColonSeparatedList(str: String): Seq[String] = {
str.split(":") str.split(":")
} }
...@@ -50,138 +52,155 @@ object VMConfParser { ...@@ -50,138 +52,155 @@ object VMConfParser {
def parseMaybeString(str: String): Option[String] = { def parseMaybeString(str: String): Option[String] = {
Some(str) Some(str)
} }
val options = new ArrayBuffer[VMOption[Any]] val options = new ArrayBuffer[VMOption[Any]]
def opt[T]( def opt[T](
name: String, name: String,
desc: String, desc: String,
parser: String => T, parser: String => T,
default: T default: T): VMOption[T] = {
): VMOption[T] = {
val option = new VMOption[T](name, desc, parser, default) val option = new VMOption[T](name, desc, parser, default)
options += option options += option
option option
} }
val sosSize = opt[Long]( val sosSize = opt[Long](
name = "sosSize", name = "sosSize",
desc = """The size of the small object space in bytes. May have suffix K, M, G or T. 1K = 1024B""", desc = """The size of the small object space in bytes. May have suffix K, M, G or T. 1K = 1024B""",
parser = sizeWithSuffix, parser = sizeWithSuffix,
default = VMConf.DEFAULT_CONF.sosSize) default = VMConf.DEFAULT_CONF.sosSize)
val losSize = opt[Long]( val losSize = opt[Long](
name = "losSize", name = "losSize",
desc = """The size of the large object space in bytes. May have suffix K, M, G or T. 1K = 1024B""", desc = """The size of the large object space in bytes. May have suffix K, M, G or T. 1K = 1024B""",
parser = sizeWithSuffix, parser = sizeWithSuffix,
default = VMConf.DEFAULT_CONF.losSize) default = VMConf.DEFAULT_CONF.losSize)
val globalSize = opt[Long]( val globalSize = opt[Long](
name = "globalSize", name = "globalSize",
desc = """The size of the large object space in bytes. May have suffix K, M, G or T. 1K = 1024B""", desc = """The size of the large object space in bytes. May have suffix K, M, G or T. 1K = 1024B""",
parser = sizeWithSuffix, parser = sizeWithSuffix,
default = VMConf.DEFAULT_CONF.globalSize) default = VMConf.DEFAULT_CONF.globalSize)
val stackSize = opt[Long]( val stackSize = opt[Long](
name = "stackSize", name = "stackSize",
desc = """The size of each stack in bytes. May have suffix K, M, G or T. 1K = 1024B""", desc = """The size of each stack in bytes. May have suffix K, M, G or T. 1K = 1024B""",
parser = sizeWithSuffix, parser = sizeWithSuffix,
default = VMConf.DEFAULT_CONF.stackSize) default = VMConf.DEFAULT_CONF.stackSize)
val dumpBundle = opt[Boolean]( val dumpBundle = opt[Boolean](
name = "dumpBundle", name = "dumpBundle",
desc = """Dump the bundle when a bundle is loaded.""", desc = """Dump the bundle when a bundle is loaded.""",
parser = parseBoolean, parser = parseBoolean,
default = VMConf.DEFAULT_CONF.dumpBundle) default = VMConf.DEFAULT_CONF.dumpBundle)
val staticCheck = opt[Boolean]( val staticCheck = opt[Boolean](
name = "staticCheck", name = "staticCheck",
desc = """Run static checker after each bundle is loaded.""", desc = """Run static checker after each bundle is loaded.""",
parser = parseBoolean, parser = parseBoolean,
default = VMConf.DEFAULT_CONF.staticCheck) default = VMConf.DEFAULT_CONF.staticCheck)
val sourceInfo = opt[Boolean]( val sourceInfo = opt[Boolean](
name = "sourceInfo", name = "sourceInfo",
desc = """Provide line/column info in Mu IR when errors occur. May significantly slow down parsing!!!""", desc = """Provide line/column info in Mu IR when errors occur. May significantly slow down parsing!!!""",
parser = parseBoolean, parser = parseBoolean,
default = VMConf.DEFAULT_CONF.sourceInfo) default = VMConf.DEFAULT_CONF.sourceInfo)
val automagicReloc = opt[Boolean]( val automagicReloc = opt[Boolean](
name = "automagicReloc", name = "automagicReloc",
desc = """'Automagic' relocation. Affects boot image building, but not loading.""", desc = """'Automagic' relocation. Affects boot image building, but not loading.""",
parser = parseBoolean, parser = parseBoolean,
default = VMConf.DEFAULT_CONF.automagicReloc) default = VMConf.DEFAULT_CONF.automagicReloc)
val extraLibs = opt[Seq[String]]( val extraLibs = opt[Seq[String]](
name = "extraLibs", name = "extraLibs",
desc = """Extra libraries to load when starting the micro VM. This is a colon-separated list of libraries. desc = """Extra libraries to load when starting the micro VM. This is a colon-separated list of libraries.
Each is passed to the dlsym function.""", Each is passed to the dlsym function.""",
parser = parseColonSeparatedList, parser = parseColonSeparatedList,
default = VMConf.DEFAULT_CONF.extraLibs) default = VMConf.DEFAULT_CONF.extraLibs)
val bootImg = opt[Option[String]]( val bootImg = opt[Option[String]](
name = "bootImg", name = "bootImg",
desc = """The path to the boot image.""", desc = """The path to the boot image.""",
parser = parseMaybeString, parser = parseMaybeString,
default = VMConf.DEFAULT_CONF.bootImg) default = VMConf.DEFAULT_CONF.bootImg)
val logbackConfig = opt[String](
name = "logbackConfig",
desc = """The logback XML configuration file""",
parser = identity,
default = "")
val vmLog = opt[String]( val vmLog = opt[String](
name = "vmLog", name = "vmLog",
desc = """The log level of the micro VM. Can be ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF""", desc = """The log level of the micro VM. Can be ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF""",
parser = identity, parser = identity,
default = "WARN") default = "WARN")
val gcLog = opt[String]( val gcLog = opt[String](
name = "gcLog", name = "gcLog",
desc = """The log level of the garbage collector. Can be ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF""", desc = """The log level of the garbage collector. Can be ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF""",
parser = identity, parser = identity,
default = "ERROR") default = "ERROR")
val uPtrHack = opt[Boolean]( val uPtrHack = opt[Boolean](
name = "uPtrHack", name = "uPtrHack",
desc = """Allow memory locations of general reference types to be accessed by uptr""", desc = """Allow memory locations of general reference types to be accessed by uptr""",
parser = parseBoolean, parser = parseBoolean,
default = false) default = false)
} }
object VMConf { object VMConf {
val DEFAULT_CONF = new VMConf() val DEFAULT_CONF = new VMConf()
val ReComment = """^\s*#.*$""".r val ReComment = """^\s*#.*$""".r
val ReBlank = """^\s*$""".r val ReBlank = """^\s*$""".r
val ReConf = """^\s*(\w+)=(\S+)\s*$""".r val ReConf = """^\s*(\w+)=(\S+)\s*$""".r
def apply(confStr: String): VMConf = { def apply(confStr: String): VMConf = {
val kvs = confStr.lines.map { val kvs = confStr.lines.map {
case ReComment() => None case ReComment() => None
case ReBlank() => None case ReBlank() => None
case ReConf(key, value) => Some((key, value)) case ReConf(key, value) => Some((key, value))
}.flatten }.flatten
val map = kvs.toMap val map = kvs.toMap
apply(map) apply(map)
} }
def apply(kvMap: Map[String, String]): VMConf = { def apply(kvMap: Map[String, String]): VMConf = {
kvMap.get("vmLog") foreach { value => setLog("uvm", value) } configureLog(kvMap)
kvMap.get("gcLog") foreach { value => setLog("uvm.refimpl.mem", value) }
new VMConf( new VMConf(
sosSize = VMConfParser.sosSize(kvMap), sosSize = VMConfParser.sosSize(kvMap),
losSize = VMConfParser.losSize(kvMap), losSize = VMConfParser.losSize(kvMap),
globalSize = VMConfParser.globalSize(kvMap), globalSize = VMConfParser.globalSize(kvMap),
stackSize = VMConfParser.stackSize(kvMap), stackSize = VMConfParser.stackSize(kvMap),
dumpBundle = VMConfParser.dumpBundle(kvMap), dumpBundle = VMConfParser.dumpBundle(kvMap),
staticCheck = VMConfParser.staticCheck(kvMap), staticCheck = VMConfParser.staticCheck(kvMap),
sourceInfo = VMConfParser.sourceInfo(kvMap), sourceInfo = VMConfParser.sourceInfo(kvMap),
automagicReloc = VMConfParser.automagicReloc(kvMap), automagicReloc = VMConfParser.automagicReloc(kvMap),
extraLibs = VMConfParser.extraLibs(kvMap), extraLibs = VMConfParser.extraLibs(kvMap),
bootImg = VMConfParser.bootImg(kvMap), bootImg = VMConfParser.bootImg(kvMap),
uPtrHack = VMConfParser.uPtrHack(kvMap) uPtrHack = VMConfParser.uPtrHack(kvMap))
) }
def configureLog(kvMap: Map[String, String]): Unit = {
kvMap.get("logbackConfig") foreach { filename =>
val context = LoggerFactory.getILoggerFactory().asInstanceOf[LoggerContext]
context.reset()
val configurator = new JoranConfigurator();
configurator.setContext(context);
configurator.doConfigure(filename);
}
kvMap.get("vmLog") foreach { value => setLog("uvm", value) }
kvMap.get("gcLog") foreach { value => setLog("uvm.refimpl.mem", value) }
} }
def setLog(name: String, levelStr: String): Unit = { def setLog(name: String, levelStr: String): Unit = {
import ch.qos.logback.classic.{ Level, Logger => LLogger } import ch.qos.logback.classic.{ Level, Logger => LLogger }
import ch.qos.logback.classic.Level._ import ch.qos.logback.classic.Level._
...@@ -204,6 +223,5 @@ class VMConf( ...@@ -204,6 +223,5 @@ class VMConf(
val automagicReloc: Boolean = false, val automagicReloc: Boolean = false,
val extraLibs: Seq[String] = Seq(), val extraLibs: Seq[String] = Seq(),
val bootImg: Option[String] = None, val bootImg: Option[String] = None,
val uPtrHack: Boolean = false val uPtrHack: Boolean = false)
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment