| author | wenzelm | 
| Tue, 14 Mar 2017 16:20:07 +0100 | |
| changeset 65232 | ca571c8c0788 | 
| parent 64220 | e7cbf81ec4b7 | 
| child 65646 | 014dbbe5331f | 
| permissions | -rw-r--r-- | 
| 64161 | 1 | /* Title: Pure/Admin/build_stats.scala | 
| 63686 | 2 | Author: Makarius | 
| 3 | ||
| 4 | Statistics from session build output. | |
| 5 | */ | |
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 9 | ||
| 10 | object Build_Stats | |
| 11 | {
 | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 12 | /* presentation */ | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 13 | |
| 63700 | 14 | private val default_history_length = 100 | 
| 15 | private val default_size = (800, 600) | |
| 16 | private val default_only_sessions = Set.empty[String] | |
| 17 | private val default_elapsed_threshold = Time.zero | |
| 63706 | 18 | private val default_ml_timing: Option[Boolean] = None | 
| 63700 | 19 | |
| 63703 | 20 | def present_job(job: String, dir: Path, | 
| 63700 | 21 | history_length: Int = default_history_length, | 
| 22 | size: (Int, Int) = default_size, | |
| 23 | only_sessions: Set[String] = default_only_sessions, | |
| 63706 | 24 | elapsed_threshold: Time = default_elapsed_threshold, | 
| 25 | ml_timing: Option[Boolean] = default_ml_timing): List[String] = | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 26 |   {
 | 
| 64054 | 27 | val job_infos = CI_API.build_job_builds(job).sortBy(_.timestamp).reverse.take(history_length) | 
| 28 |     if (job_infos.isEmpty) error("No build infos for job " + quote(job))
 | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 29 | |
| 64054 | 30 | val all_infos = | 
| 31 | Par_List.map((job_info: CI_API.Job_Info) => | |
| 64106 | 32 | (job_info.timestamp / 1000, job_info.read_main_log.parse_build_info), job_infos) | 
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 33 | val all_sessions = | 
| 64054 | 34 | (Set.empty[String] /: all_infos)( | 
| 64085 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 35 |         { case (s, (_, info)) => s ++ info.sessions.keySet })
 | 
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 36 | |
| 64085 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 37 | def check_threshold(info: Build_Log.Build_Info, session: String): Boolean = | 
| 64089 | 38 |     {
 | 
| 39 | val t = info.timing(session) | |
| 40 | !t.is_zero && t.elapsed >= elapsed_threshold | |
| 41 | } | |
| 63700 | 42 | |
| 63703 | 43 | val sessions = | 
| 44 |       for {
 | |
| 45 | session <- (if (only_sessions.isEmpty) all_sessions else all_sessions & only_sessions) | |
| 64054 | 46 |         if all_infos.filter({ case (_, info) => check_threshold(info, session) }).length >= 3
 | 
| 63703 | 47 | } yield session | 
| 48 | ||
| 49 | Isabelle_System.mkdirs(dir) | |
| 50 |     for (session <- sessions) {
 | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 51 |       Isabelle_System.with_tmp_file(session, "png") { data_file =>
 | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 52 |         Isabelle_System.with_tmp_file(session, "gnuplot") { plot_file =>
 | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 53 | val data = | 
| 64085 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 54 |             for { (t, info) <- all_infos if info.finished(session) }
 | 
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 55 |             yield {
 | 
| 64085 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 56 | val timing1 = info.timing(session) | 
| 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 57 | val timing2 = info.ml_timing(session) | 
| 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 58 | List(t.toString, | 
| 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 59 | timing1.elapsed.minutes, | 
| 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 60 | timing1.cpu.minutes, | 
| 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 61 | timing2.elapsed.minutes, | 
| 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 62 | timing2.cpu.minutes, | 
| 
1c451e5c145f
clarified parse_build_info: isabelle build output;
 wenzelm parents: 
64062diff
changeset | 63 |                 timing2.gc.minutes).mkString(" ")
 | 
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 64 | } | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 65 | File.write(data_file, cat_lines(data)) | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 66 | |
| 63706 | 67 | val plots1 = | 
| 68 | List( | |
| 63707 
b7aab1a6cf0d
clarified presentation order, according to typical amounts;
 wenzelm parents: 
63706diff
changeset | 69 | """ using 1:3 smooth sbezier title "cpu time (smooth)" """, | 
| 
b7aab1a6cf0d
clarified presentation order, according to typical amounts;
 wenzelm parents: 
63706diff
changeset | 70 | """ using 1:3 smooth csplines title "cpu time" """, | 
| 63706 | 71 | """ using 1:2 smooth sbezier title "elapsed time (smooth)" """, | 
| 63707 
b7aab1a6cf0d
clarified presentation order, according to typical amounts;
 wenzelm parents: 
63706diff
changeset | 72 | """ using 1:2 smooth csplines title "elapsed time" """) | 
| 63706 | 73 | val plots2 = | 
| 74 | List( | |
| 63707 
b7aab1a6cf0d
clarified presentation order, according to typical amounts;
 wenzelm parents: 
63706diff
changeset | 75 | """ using 1:5 smooth sbezier title "ML cpu time (smooth)" """, | 
| 
b7aab1a6cf0d
clarified presentation order, according to typical amounts;
 wenzelm parents: 
63706diff
changeset | 76 | """ using 1:5 smooth csplines title "ML cpu time" """, | 
| 63706 | 77 | """ using 1:4 smooth sbezier title "ML elapsed time (smooth)" """, | 
| 78 | """ using 1:4 smooth csplines title "ML elapsed time" """, | |
| 79 | """ using 1:6 smooth sbezier title "ML gc time (smooth)" """, | |
| 80 | """ using 1:6 smooth csplines title "ML gc time" """) | |
| 81 | val plots = | |
| 82 |             ml_timing match {
 | |
| 83 | case None => plots1 | |
| 84 | case Some(false) => plots1 ::: plots2 | |
| 85 | case Some(true) => plots2 | |
| 86 | } | |
| 87 | ||
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 88 | File.write(plot_file, """ | 
| 63700 | 89 | set terminal png size """ + size._1 + "," + size._2 + """ | 
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 90 | set output """ + quote(File.standard_path(dir + Path.basic(session + ".png"))) + """ | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 91 | set xdata time | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 92 | set timefmt "%s" | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 93 | set format x "%d-%b" | 
| 63701 | 94 | set xlabel """ + quote(session) + """ noenhanced | 
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 95 | set key left top | 
| 64220 | 96 | plot [] [0:] """ + plots.map(s => quote(data_file.implode) + " " + s).mkString(", ") + "\n")
 | 
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 97 |           val result = Isabelle_System.bash("\"$ISABELLE_GNUPLOT\" " + File.bash_path(plot_file))
 | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 98 |           if (result.rc != 0) {
 | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 99 |             Output.error_message("Session " + session + ": gnuplot error")
 | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 100 | result.print | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 101 | } | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 102 | } | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 103 | } | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 104 | } | 
| 63703 | 105 | |
| 106 | sessions.toList.sorted | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 107 | } | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 108 | |
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 109 | |
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 110 | /* Isabelle tool wrapper */ | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 111 | |
| 63703 | 112 | private val html_header = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> | 
| 113 | <html> | |
| 114 | <head><title>Performance statistics from session build output</title></head> | |
| 115 | <body> | |
| 116 | """ | |
| 117 | private val html_footer = """ | |
| 118 | </body> | |
| 119 | </html> | |
| 120 | """ | |
| 121 | ||
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 122 | val isabelle_tool = | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 123 |     Isabelle_Tool("build_stats", "present statistics from session build output", args =>
 | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 124 |     {
 | 
| 63703 | 125 |       var target_dir = Path.explode("stats")
 | 
| 63706 | 126 | var ml_timing = default_ml_timing | 
| 63700 | 127 | var only_sessions = default_only_sessions | 
| 128 | var elapsed_threshold = default_elapsed_threshold | |
| 129 | var history_length = default_history_length | |
| 130 | var size = default_size | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 131 | |
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 132 |       val getopts = Getopts("""
 | 
| 63700 | 133 | Usage: isabelle build_stats [OPTIONS] [JOBS ...] | 
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 134 | |
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 135 | Options are: | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 136 | -D DIR target directory (default "stats") | 
| 63706 | 137 | -M only ML timing | 
| 63700 | 138 | -S SESSIONS only given SESSIONS (comma separated) | 
| 139 | -T THRESHOLD only sessions with elapsed time >= THRESHOLD (minutes) | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 140 | -l LENGTH length of history (default 100) | 
| 63706 | 141 | -m include ML timing | 
| 63700 | 142 | -s WxH size of PNG image (default 800x600) | 
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 143 | |
| 63700 | 144 | Present statistics from session build output of the given JOBS, from Jenkins | 
| 145 | continuous build service specified as URL via ISABELLE_JENKINS_ROOT. | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 146 | """, | 
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 147 | "D:" -> (arg => target_dir = Path.explode(arg)), | 
| 63706 | 148 | "M" -> (_ => ml_timing = Some(true)), | 
| 63700 | 149 |         "S:" -> (arg => only_sessions = space_explode(',', arg).toSet),
 | 
| 63805 | 150 | "T:" -> (arg => elapsed_threshold = Time.minutes(Value.Double.parse(arg))), | 
| 151 | "l:" -> (arg => history_length = Value.Int.parse(arg)), | |
| 63706 | 152 | "m" -> (_ => ml_timing = Some(false)), | 
| 63700 | 153 | "s:" -> (arg => | 
| 63805 | 154 |           space_explode('x', arg).map(Value.Int.parse(_)) match {
 | 
| 63700 | 155 | case List(w, h) if w > 0 && h > 0 => size = (w, h) | 
| 156 |             case _ => error("Error bad PNG image size: " + quote(arg))
 | |
| 157 | })) | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 158 | |
| 63700 | 159 | val jobs = getopts(args) | 
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 160 | val all_jobs = CI_API.build_jobs() | 
| 63700 | 161 | val bad_jobs = jobs.filterNot(all_jobs.contains(_)).sorted | 
| 162 | ||
| 163 | if (jobs.isEmpty) | |
| 63984 | 164 |         error("No build jobs given. Available jobs: " + all_jobs.sorted.mkString(" "))
 | 
| 63700 | 165 | |
| 166 | if (bad_jobs.nonEmpty) | |
| 63984 | 167 |         error("Unknown build jobs: " + bad_jobs.mkString(" ") +
 | 
| 168 |           "\nAvailable jobs: " + all_jobs.sorted.mkString(" "))
 | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 169 | |
| 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 170 |       for (job <- jobs) {
 | 
| 63703 | 171 | val dir = target_dir + Path.basic(job) | 
| 172 | Output.writeln(dir.implode) | |
| 63706 | 173 | val sessions = | 
| 174 | present_job(job, dir, history_length, size, only_sessions, elapsed_threshold, ml_timing) | |
| 63703 | 175 |         File.write(dir + Path.basic("index.html"),
 | 
| 176 | html_header + "\n<h1>" + HTML.output(job) + "</h1>\n" + | |
| 177 | cat_lines( | |
| 178 | sessions.map(session => | |
| 179 | """<br/><img src=""" + quote(HTML.output(session + ".png")) + """><br/>""")) + | |
| 180 | "\n" + html_footer) | |
| 63688 
cc57255bf6ae
gnuplot presentation similar to former isatest-statistics;
 wenzelm parents: 
63686diff
changeset | 181 | } | 
| 63703 | 182 | |
| 183 |       File.write(target_dir + Path.basic("index.html"),
 | |
| 184 | html_header + "\n<ul>\n" + | |
| 185 | cat_lines( | |
| 186 | jobs.map(job => """<li> <a href=""" + quote(HTML.output(job + "/index.html")) + """>""" + | |
| 187 | HTML.output(job) + """</a> </li>""")) + | |
| 188 | "\n</ul>\n" + html_footer) | |
| 64161 | 189 | }, admin = true) | 
| 63686 | 190 | } |