object Prop { val build_tags = SQL.Column.string("build_tags") // lines val build_args = SQL.Column.string("build_args") // lines val build_group_id = SQL.Column.string("build_group_id") val build_id = SQL.Column.string("build_id") val build_engine = SQL.Column.string("build_engine") val build_host = SQL.Column.string("build_host") val build_start = SQL.Column.date("build_start") val build_end = SQL.Column.date("build_end") val isabelle_version = SQL.Column.string("isabelle_version") val afp_version = SQL.Column.string("afp_version")
object Settings { val ISABELLE_BUILD_OPTIONS = SQL.Column.string("ISABELLE_BUILD_OPTIONS") val ML_PLATFORM = SQL.Column.string("ML_PLATFORM") val ML_HOME = SQL.Column.string("ML_HOME") val ML_SYSTEM = SQL.Column.string("ML_SYSTEM") val ML_OPTIONS = SQL.Column.string("ML_OPTIONS")
val ml_settings = List(ML_PLATFORM, ML_HOME, ML_SYSTEM, ML_OPTIONS) val all_settings = ISABELLE_BUILD_OPTIONS :: ml_settings
type Entry = (String, String) type T = List[Entry]
val Date_Format = { val fmts =
Date.Formatter.variants(
List("EEE MMM d HH:mm:ss O yyyy", "EEE MMM d HH:mm:ss VV yyyy"),
List(Locale.ENGLISH, Locale.GERMAN)) :::
List(
DateTimeFormatter.RFC_1123_DATE_TIME,
Date.Formatter.pattern("EEE MMM d HH:mm:ss yyyy").withZone(Date.timezone_berlin))
def tune_timezone(s: String): String =
s match { case"CET" | "MET" => "GMT+1" case"CEST" | "MEST" => "GMT+2" case"EST" => "Europe/Berlin" case _ => s
} def tune_weekday(s: String): String =
s match { case"Die" => "Di" case"Mit" => "Mi" case"Don" => "Do" case"Fre" => "Fr" case"Sam" => "Sa" case"Son" => "So" case _ => s
}
def tune(s: String): String =
Word.implode(
Word.explode(s) match { case a :: "M\uFFFDr" :: bs => tune_weekday(a) :: "Mär" :: bs.map(tune_timezone) case a :: bs => tune_weekday(a) :: bs.map(tune_timezone) case Nil => Nil
}
)
Date.Format.make(fmts, tune)
}
}
class Log_File private( val name: String, val lines: List[String], val cache: XML.Cache
) {
log_file =>
val Start = new Regex("""^isabelle_identify: (.+)$""") val No_End = new Regex("""$.""") val Isabelle_Version = List(new Regex("""^Isabelle version: (\S+)$""")) val AFP_Version = List(new Regex("""^AFP version: (\S+)$"""))
}
object Isatest { val log_prefix = "isatest-makeall-" val engine = "isatest" val Start = new Regex("""^------------------- starting test --- (.+) --- (.+)$""") val End = new Regex("""^------------------- test (?:successful|FAILED) --- (.+) --- .*$""") val Isabelle_Version = List(new Regex("""^Isabelle version: (\S+)$"""))
}
object AFP_Test { val log_prefix = "afp-test-devel-" val engine = "afp-test" val Start = new Regex("""^Start test(?: for .+)? at ([^,]+), (.*)$""") val Start_Old = new Regex("""^Start test(?: for .+)? at ([^,]+)$""") val End = new Regex("""^End test on (.+), .+, elapsed time:.*$""") val Isabelle_Version = List(new Regex("""^Isabelle version: .* -- hg id (\S+)$""")) val AFP_Version = List(new Regex("""^AFP version: .* -- hg id (\S+)$""")) val Bad_Init = new Regex("""^cp:.*: Disc quota exceeded$""")
}
object Jenkins { val log_prefix = "jenkins_" val engine = "jenkins" val Host = new Regex("""^Building remotely on (\S+) \((\S+)\).*$""") val Start = new Regex("""^(?:Started by an SCM change|Started from command line by admin|).*$""") val Start_Date = new Regex("""^Build started at (.+)$""") val No_End = new Regex("""$.""") val Isabelle_Version =
List(new Regex("""^(?:Build for Isabelle id|Isabelle id) (\w+).*$"""), new Regex("""^ISABELLE_CI_REPO_ID="(\w+)".*$"""), new Regex("""^(\w{12}) tip.*$""")) val AFP_Version =
List(new Regex("""^(?:Build for AFP id|AFP id) (\w+).*$"""), new Regex("""^ISABELLE_CI_AFP_ID="(\w+)".*$""")) val CONFIGURATION = "=== CONFIGURATION ===" val BUILD = "=== BUILD ==="
}
object Build_Manager { val log_prefix = "build_manager" val engine = "build_manager" val Start = new Regex("""^Starting job \S+ at ([^,]+), on (\S+)$""") val End = new Regex("""^Job ended at ([^,]+), with status \w+$""") val Isabelle_Version = List(new Regex("""^Using Isabelle/?(\w*)$""")) val AFP_Version = List(new Regex("""^Using AFP/?(\w*)$"""))
}
val start_date = List(Prop.build_start.name -> print_date(start)) val end_date =
log_file.lines.last match { case End(log_file.Strict_Date(end_date)) =>
List(Prop.build_end.name -> print_date(end_date)) case _ => Nil
}
val isabelle_version =
log_file.find_match(Isabelle_Version).map(Prop.isabelle_version.name -> _) val afp_version =
log_file.find_match(AFP_Version).map(Prop.afp_version.name -> _)
log_file.lines match { case line :: _ if Protocol.Meta_Info_Marker.test_yxml(line) =>
Meta_Info(log_file.find_props(Protocol.Meta_Info_Marker).get, log_file.get_all_settings)
case line :: _ if line.startsWith("\u0000") => Meta_Info.empty case List(Isatest.End(_)) => Meta_Info.empty case _ :: AFP_Test.Bad_Init() :: _ => Meta_Info.empty case Nil => Meta_Info.empty
case _ => log_file.err("cannot detect log file format")
}
}
/** build info: toplevel output of isabelle build or Admin/build_other **/
val SESSION_NAME = "session_name"
enum Session_Status { case existing, finished, failed, cancelled }
sealedcaseclass Build_Info(sessions: Map[String, Session_Entry]) { def finished_sessions: List[String] = for ((a, b) <- sessions.toList if b.finished) yield a def failed_sessions: List[String] = for ((a, b) <- sessions.toList if b.failed) yield a
}
val Session_No_Groups = new Regex("""^Session (\S+)$""") val Session_Groups = new Regex("""^Session (\S+) \((.*)\)$""") val Session_Finished1 = new Regex("""^Finished (\S+) \((\d+):(\d+):(\d+) elapsed time, (\d+):(\d+):(\d+) cpu time.*$""") val Session_Finished2 = new Regex("""^Finished ([^\s/]+) \((\d+):(\d+):(\d+) elapsed time.*$""") val Session_Timing = new Regex("""^Timing (\S+) \((\d+) threads, (\d+\.\d+)s elapsed time, (\d+\.\d+)s cpu time, (\d+\.\d+)s GC time.*$""") val Session_Started1 = new Regex("""^(?:Running|Building) (\S+) \.\.\.$""") val Session_Started2 = new Regex("""^(?:Running|Building) (\S+) \(?on ([^\s/]+)[^)]*\)? \.\.\.$""") val Session_Started3 = new Regex("""^(?:Running|Building) (\S+) \(started (\d+):(\d+):(\d+)\) \.\.\.$""") val Session_Started4 = new Regex("""^(?:Running|Building) (\S+) \(started (\d+):(\d+):(\d+) on ([^\s/]+)[^)]*\) \.\.\.$""") val Sources = new Regex("""^Sources (\S+) (\S{""" + SHA1.digest_length + """})$""") val Heap = new Regex("""^Heap (\S+) \((\d+) bytes\)$""")
object Theory_Timing { def unapply(line: String): Option[(String, (String, Timing))] =
Protocol.Theory_Timing_Marker.unapply(line.replace('~', '-')).map(log_file.parse_props) match { case Some((SESSION_NAME, session) :: props) => for (theory <- Markup.Name.unapply(props)) yield (session, theory -> Markup.Timing_Properties.get(props)) case _ => None
}
}
var chapter = Map.empty[String, String] var groups = Map.empty[String, List[String]] var hostnames = Map.empty[String, String] var threads = Map.empty[String, Int] var timing = Map.empty[String, Timing] var ml_timing = Map.empty[String, Timing] var started = Map.empty[String, Option[Time]] var sources = Map.empty[String, String] var heap_sizes = Map.empty[String, Space] var theory_timings = Map.empty[String, Map[String, Timing]] var ml_statistics = Map.empty[String, List[Properties.T]] var errors = Map.empty[String, List[String]]
for (line <- log_file.lines) {
line match { case Session_No_Groups(Chapter_Name(chapt, name)) =>
chapter += (name -> chapt)
groups += (name -> Nil)
case Session_Groups(Chapter_Name(chapt, name), grps) =>
chapter += (name -> chapt)
groups += (name -> Word.explode(grps))
case Session_Started1(name) =>
started += (name -> None)
case Session_Started2(name, hostname) =>
started += (name -> None)
hostnames += (name -> hostname)
case Session_Started3(name, Value.Int(t1), Value.Int(t2), Value.Int(t3)) =>
started += (name -> Some(Time.hms(t1, t2, t3)))
case Session_Started4(name, Value.Int(t1), Value.Int(t2), Value.Int(t3), hostname) =>
started += (name -> Some(Time.hms(t1, t2, t3)))
hostnames += (name -> hostname)
case Session_Finished1(name,
Value.Int(e1), Value.Int(e2), Value.Int(e3),
Value.Int(c1), Value.Int(c2), Value.Int(c3)) => val elapsed = Time.hms(e1, e2, e3) val cpu = Time.hms(c1, c2, c3)
timing += (name -> Timing(elapsed, cpu, Time.zero))
case Session_Finished2(name,
Value.Int(e1), Value.Int(e2), Value.Int(e3)) => val elapsed = Time.hms(e1, e2, e3)
timing += (name -> Timing(elapsed, Time.zero, Time.zero))
case Session_Timing(name,
Value.Int(t), Value.Double(e), Value.Double(c), Value.Double(g)) => val elapsed = Time.seconds(e) val cpu = Time.seconds(c) val gc = Time.seconds(g)
ml_timing += (name -> Timing(elapsed, cpu, gc))
threads += (name -> t)
case Sources(name, s) =>
sources += (name -> s)
case Heap(name, Value.Long(size)) =>
heap_sizes += (name -> Space.bytes(size))
case _ if Protocol.Theory_Timing_Marker.test_yxml(line) =>
line match { case Theory_Timing(name, theory_timing) =>
theory_timings += (name -> (theory_timings.getOrElse(name, Map.empty) + theory_timing)) case _ => log_file.err("malformed theory_timing " + quote(line))
}
case _ if parse_ml_statistics && Protocol.ML_Statistics_Marker.test_yxml(line) =>
Protocol.ML_Statistics_Marker.unapply(line).map(log_file.parse_props) match { case Some((SESSION_NAME, name) :: props) =>
ml_statistics += (name -> (props :: ml_statistics.getOrElse(name, Nil))) case _ => log_file.err("malformed ML_statistics " + quote(line))
}
case _ if Protocol.Error_Message_Marker.test_yxml(line) =>
Protocol.Error_Message_Marker.unapply(line).map(log_file.parse_props) match { case Some(List((SESSION_NAME, name), (Markup.CONTENT, msg))) =>
errors += (name -> (msg :: errors.getOrElse(name, Nil))) case _ => log_file.err("malformed error message " + quote(line))
}
object Column { val log_name = SQL.Column.string("log_name").make_primary_key val session_name = SQL.Column.string("session_name").make_primary_key val theory_name = SQL.Column.string("theory_name").make_primary_key val chapter = SQL.Column.string("chapter") val groups = SQL.Column.string("groups") val hostname = SQL.Column.string("hostname") val threads = SQL.Column.int("threads") val session_start = SQL.Column.long("session_start") val timing_elapsed = SQL.Column.long("timing_elapsed") val timing_cpu = SQL.Column.long("timing_cpu") val timing_gc = SQL.Column.long("timing_gc") val timing_factor = SQL.Column.double("timing_factor") val ml_timing_elapsed = SQL.Column.long("ml_timing_elapsed") val ml_timing_cpu = SQL.Column.long("ml_timing_cpu") val ml_timing_gc = SQL.Column.long("ml_timing_gc") val ml_timing_factor = SQL.Column.double("ml_timing_factor") val theory_timing_elapsed = SQL.Column.long("theory_timing_elapsed") val theory_timing_cpu = SQL.Column.long("theory_timing_cpu") val theory_timing_gc = SQL.Column.long("theory_timing_gc") val heap_size = SQL.Column.long("heap_size") val status = SQL.Column.string("status") val errors = SQL.Column.bytes("errors") val sources = SQL.Column.string("sources") val ml_statistics = SQL.Column.bytes("ml_statistics") val known = SQL.Column.bool("known")
def select_recent_log_names(days: Int = 0): PostgreSQL.Source = { val table1 = meta_info_table val table2 = recent_pull_date_table(days = days)
table1.select(List(Column.log_name), distinct = true, sql =
SQL.join_inner + table2.query_named + " ON " + Prop.isabelle_version(table1) + " = " + Prop.isabelle_version(table2))
}
/* universal view on main data */
val universal_table: SQL.Table = { val afp_pull_date = Column.pull_date(afp = true) val version1 = Prop.isabelle_version val version2 = Prop.afp_version val table1 = meta_info_table val table2 = pull_date_table(afp = true) val table3 = pull_date_table()
def store(options: Options, cache: XML.Cache = XML.Cache.make()): Store = new Store(options, cache)
class Store private[Build_Log](val options: Options, val cache: XML.Cache) { overridedef toString: String = { val s =
Exn.result { open_database() } match { case Exn.Res(db) => val db_name = db.toString
db.close() "database = " + db_name case Exn.Exn(_) => "no database"
} "Store(" + s + ")"
}
db.execute_query_statement(select_recent_versions, List.from[Entry],
{ res => val known = res.bool(Column.known) val isabelle_version = res.string(Prop.isabelle_version) val afp_version = if (afp) proper_string(res.string(Prop.afp_version)) else None val pull_date = res.date(Column.pull_date(afp))
Entry(known, isabelle_version, afp_version, pull_date)
})
}
def unknown_runs(filter: History.Run => Boolean = _ => true): List[History.Run] = { var rest = entries val result = new mutable.ListBuffer[History.Run] while (rest.nonEmpty) { val (a, b) = Library.take_prefix[History.Entry](_.unknown, rest.dropWhile(_.known)) val run = History.Run(a) if (!run.is_empty && filter(run)) result += run
rest = b
}
result.toList
}
}
/** maintain build_log database **/
def build_log_database(options: Options, logs: List[Path],
progress: Progress = new Progress,
vacuum: Boolean = false,
ml_statistics: Boolean = false,
snapshot: Option[Path] = None
): Unit = { val store = Build_Log.store(options)
val log_files = Log_File.find_files(logs.map(_.file))
using(store.open_database()) { db => if (vacuum) db.vacuum()
progress.echo("Updating database " + db + " ...") val errors =
store.write_info(db, log_files, ml_statistics = ml_statistics, progress = progress)
if (errors.isEmpty) { for (path <- snapshot) {
progress.echo("Writing database snapshot " + path.expand)
store.snapshot_database(db, path)
}
} else {
error(cat_lines(List.from( for ((name, rev_errs) <- errors.iterator_list) yield { val err = "The error(s) above occurred in " + quote(name)
cat_lines((err :: rev_errs).reverse)
}
)))
}
}
}
/* Isabelle tool wrapper */
val isabelle_tool = Isabelle_Tool("build_log_database", "update build_log database from log files",
Scala_Project.here,
{ args => var ml_statistics: Boolean = false var snapshot: Option[Path] = None var vacuum = false var logs: List[Path] = Nil var options = Options.init() var verbose = false
val getopts = Getopts("""
Usage: isabelle build_log_database [OPTIONS]
Options are:
-M include ML statistics
-S FILE snapshot to SQLite db file
-V vacuum cleaning of database
-d LOG include log file start location
-o OPTION override Isabelle system OPTION (via NAME=VAL or NAME)
-v verbose
Update the build_log database server from log files, which are recursively
collected from given start locations (files or directories). """, "M" -> (_ => ml_statistics = true), "S:" -> (arg => snapshot = Some(Path.explode(arg))), "V" -> (_ => vacuum = true), "d:" -> (arg => logs = logs ::: List(Path.explode(arg))), "o:" -> (arg => options = options + arg), "v" -> (_ => verbose = true))
val more_args = getopts(args) if (more_args.nonEmpty) getopts.usage()
val progress = new Console_Progress(verbose = verbose)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.