| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package chromiumbuildstats | 5 package chromiumbuildstats |
| 6 | 6 |
| 7 // ninja_log.go provides /ninja_log endpoints. | 7 // ninja_log.go provides /ninja_log endpoints. |
| 8 | 8 |
| 9 import ( | 9 import ( |
| 10 "bufio" | 10 "bufio" |
| 11 "compress/gzip" | 11 "compress/gzip" |
| 12 "encoding/json" | 12 "encoding/json" |
| 13 "fmt" | 13 "fmt" |
| 14 "html/template" | 14 "html/template" |
| 15 "net/http" | 15 "net/http" |
| 16 "path" | 16 "path" |
| 17 "sort" |
| 17 "strings" | 18 "strings" |
| 18 | 19 |
| 19 "appengine" | 20 "appengine" |
| 20 | 21 |
| 21 "github.com/golang/oauth2/google" | 22 "github.com/golang/oauth2/google" |
| 22 | 23 |
| 23 "chromegomalog" | 24 "chromegomalog" |
| 24 "ninjalog" | 25 "ninjalog" |
| 25 ) | 26 ) |
| 26 | 27 |
| 27 type outputFunc func(http.ResponseWriter, string, *ninjalog.NinjaLog) error | 28 type outputFunc func(http.ResponseWriter, string, *ninjalog.NinjaLog) error |
| 28 | 29 |
| 29 var ( | 30 var ( |
| 30 outputs = map[string]outputFunc{ | 31 outputs = map[string]outputFunc{ |
| 31 "lastbuild": outputFunc(lastBuild), | 32 "lastbuild": outputFunc(lastBuild), |
| 33 "table": outputFunc(table), |
| 32 "metadata.json": outputFunc(metadataJSON), | 34 "metadata.json": outputFunc(metadataJSON), |
| 33 "trace.json": outputFunc(traceJSON), | 35 "trace.json": outputFunc(traceJSON), |
| 34 } | 36 } |
| 35 | 37 |
| 36 // chromegomalog.URL(path) won't be accessible by user. | 38 // chromegomalog.URL(path) won't be accessible by user. |
| 37 indexTmpl = template.Must(template.New("index").Parse(` | 39 indexTmpl = template.Must(template.New("index").Parse(` |
| 38 <html> | 40 <html> |
| 39 <head> | 41 <head> |
| 40 <title>{{.Path}}</title> | 42 <title>{{.Path}}</title> |
| 41 </head> | 43 </head> |
| 42 <body> | 44 <body> |
| 43 <h1>{{.Path}}</h1> | 45 <h1>{{.Path}}</h1> |
| 44 <ul> | 46 <ul> |
| 45 <li><a href="/file/{{.Path}}">original file</a> | 47 <li><a href="/file/{{.Path}}">original file</a> |
| 46 <li><a href="{{.Filename}}/lastbuild">.ninja_log</a> | 48 <li><a href="{{.Filename}}/lastbuild">.ninja_log</a> |
| 49 <li><a href="{{.Filename}}/table">.ninja_log in table format</a> |
| 47 <li><a href="{{.Filename}}/metadata.json">metadata.json</a> | 50 <li><a href="{{.Filename}}/metadata.json">metadata.json</a> |
| 48 <li><a href="{{.Filename}}/trace.json">trace.json</a> | 51 <li><a href="{{.Filename}}/trace.json">trace.json</a> |
| 49 </ul> | 52 </ul> |
| 50 </body> | 53 </body> |
| 51 </html> | 54 </html> |
| 52 `)) | 55 `)) |
| 56 |
| 57 tableTmpl = template.Must(template.New("table").Parse(` |
| 58 <html> |
| 59 <head> |
| 60 <title>{{.Filename}}</title> |
| 61 </head> |
| 62 <body> |
| 63 <h1>{{.Filename}}</h1> |
| 64 Platform: {{.Metadata.Platform}} |
| 65 Cmdline: {{.Metadata.Cmdline}} |
| 66 Exit:{{.Metadata.Exit}} |
| 67 <hr /> |
| 68 <table border=1> |
| 69 <tr> |
| 70 <th>n |
| 71 <th>duration |
| 72 <th>start |
| 73 <th>end |
| 74 <th>restat |
| 75 <th>output |
| 76 </tr> |
| 77 {{range $i, $step := .Steps}} |
| 78 <tr> |
| 79 <td><a name="{{$i}}" href="#{{$i}}">{{$i}}</a> |
| 80 <td>{{$step.Duration}} |
| 81 <td>{{$step.Start}} |
| 82 <td>{{$step.End}} |
| 83 <td>{{if gt $step.Restat 0}}{{$step.Restat}}{{end}} |
| 84 <td>{{$step.Out}} |
| 85 </tr> |
| 86 {{end}} |
| 87 </table> |
| 88 </html> |
| 89 `)) |
| 53 ) | 90 ) |
| 54 | 91 |
| 55 func init() { | 92 func init() { |
| 56 http.Handle("/ninja_log/", http.StripPrefix("/ninja_log/", http.HandlerF
unc(ninjaLogHandler))) | 93 http.Handle("/ninja_log/", http.StripPrefix("/ninja_log/", http.HandlerF
unc(ninjaLogHandler))) |
| 57 } | 94 } |
| 58 | 95 |
| 59 // ninjaLogHandler handles /<path>/<format> for ninja_log file in gs://chrome-go
ma-log/<path> | 96 // ninjaLogHandler handles /<path>/<format> for ninja_log file in gs://chrome-go
ma-log/<path> |
| 60 func ninjaLogHandler(w http.ResponseWriter, req *http.Request) { | 97 func ninjaLogHandler(w http.ResponseWriter, req *http.Request) { |
| 61 ctx := appengine.NewContext(req) | 98 ctx := appengine.NewContext(req) |
| 62 | 99 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 96 logPath := path.Dir(reqPath) | 133 logPath := path.Dir(reqPath) |
| 97 return logPath, f, nil | 134 return logPath, f, nil |
| 98 } | 135 } |
| 99 } | 136 } |
| 100 if !strings.HasPrefix(basename, "ninja_log.") { | 137 if !strings.HasPrefix(basename, "ninja_log.") { |
| 101 return "", nil, fmt.Errorf("unexpected path %s", reqPath) | 138 return "", nil, fmt.Errorf("unexpected path %s", reqPath) |
| 102 } | 139 } |
| 103 return strings.TrimSuffix(reqPath, "/"), outputFunc(indexPage), nil | 140 return strings.TrimSuffix(reqPath, "/"), outputFunc(indexPage), nil |
| 104 } | 141 } |
| 105 | 142 |
| 106 func ninjalogFetch(client *http.Client, path string) (*ninjalog.NinjaLog, http.H
eader, error) { | 143 func ninjalogFetch(client *http.Client, logPath string) (*ninjalog.NinjaLog, htt
p.Header, error) { |
| 107 » resp, err := chromegomalog.Fetch(client, path) | 144 » resp, err := chromegomalog.Fetch(client, logPath) |
| 108 if err != nil { | 145 if err != nil { |
| 109 return nil, nil, err | 146 return nil, nil, err |
| 110 } | 147 } |
| 111 defer resp.Body.Close() | 148 defer resp.Body.Close() |
| 112 const bufsize = 512 * 1024 | 149 const bufsize = 512 * 1024 |
| 113 rd, err := gzip.NewReader(bufio.NewReaderSize(resp.Body, bufsize)) | 150 rd, err := gzip.NewReader(bufio.NewReaderSize(resp.Body, bufsize)) |
| 114 if err != nil { | 151 if err != nil { |
| 115 return nil, nil, err | 152 return nil, nil, err |
| 116 } | 153 } |
| 117 » nl, err := ninjalog.Parse(rd) | 154 » nl, err := ninjalog.Parse(logPath, rd) |
| 118 return nl, resp.Header, nil | 155 return nl, resp.Header, nil |
| 119 } | 156 } |
| 120 | 157 |
| 121 func indexPage(w http.ResponseWriter, logPath string, njl *ninjalog.NinjaLog) er
ror { | 158 func indexPage(w http.ResponseWriter, logPath string, njl *ninjalog.NinjaLog) er
ror { |
| 122 w.Header().Set("Content-Type", "text/html") | 159 w.Header().Set("Content-Type", "text/html") |
| 123 w.WriteHeader(http.StatusOK) | 160 w.WriteHeader(http.StatusOK) |
| 124 data := struct { | 161 data := struct { |
| 125 Path string | 162 Path string |
| 126 Filename string | 163 Filename string |
| 127 }{ | 164 }{ |
| 128 Path: logPath, | 165 Path: logPath, |
| 129 » » Filename: path.Base(logPath), | 166 » » Filename: path.Base(njl.Filename), |
| 130 } | 167 } |
| 131 return indexTmpl.Execute(w, data) | 168 return indexTmpl.Execute(w, data) |
| 132 } | 169 } |
| 133 | 170 |
| 134 func lastBuild(w http.ResponseWriter, logPath string, njl *ninjalog.NinjaLog) er
ror { | 171 func lastBuild(w http.ResponseWriter, logPath string, njl *ninjalog.NinjaLog) er
ror { |
| 135 w.Header().Set("Content-Type", "text/plain") | 172 w.Header().Set("Content-Type", "text/plain") |
| 136 w.WriteHeader(http.StatusOK) | 173 w.WriteHeader(http.StatusOK) |
| 137 return ninjalog.Dump(w, njl.Steps) | 174 return ninjalog.Dump(w, njl.Steps) |
| 138 } | 175 } |
| 139 | 176 |
| 177 func table(w http.ResponseWriter, logPath string, njl *ninjalog.NinjaLog) error
{ |
| 178 w.Header().Set("Content-Type", "text/html") |
| 179 w.WriteHeader(http.StatusOK) |
| 180 sort.Sort(sort.Reverse(ninjalog.ByDuration{Steps: njl.Steps})) |
| 181 return tableTmpl.Execute(w, njl) |
| 182 } |
| 183 |
| 140 func metadataJSON(w http.ResponseWriter, logPath string, njl *ninjalog.NinjaLog)
error { | 184 func metadataJSON(w http.ResponseWriter, logPath string, njl *ninjalog.NinjaLog)
error { |
| 141 js, err := json.Marshal(njl.Metadata) | 185 js, err := json.Marshal(njl.Metadata) |
| 142 if err != nil { | 186 if err != nil { |
| 143 return err | 187 return err |
| 144 } | 188 } |
| 145 w.Header().Set("Content-Type", "application/json") | 189 w.Header().Set("Content-Type", "application/json") |
| 146 w.WriteHeader(http.StatusOK) | 190 w.WriteHeader(http.StatusOK) |
| 147 _, err = w.Write(js) | 191 _, err = w.Write(js) |
| 148 return err | 192 return err |
| 149 } | 193 } |
| 150 | 194 |
| 151 func traceJSON(w http.ResponseWriter, logPath string, njl *ninjalog.NinjaLog) er
ror { | 195 func traceJSON(w http.ResponseWriter, logPath string, njl *ninjalog.NinjaLog) er
ror { |
| 152 steps := ninjalog.Dedup(njl.Steps) | 196 steps := ninjalog.Dedup(njl.Steps) |
| 153 flow := ninjalog.Flow(steps) | 197 flow := ninjalog.Flow(steps) |
| 154 traces := ninjalog.ToTraces(flow, 1) | 198 traces := ninjalog.ToTraces(flow, 1) |
| 155 js, err := json.Marshal(traces) | 199 js, err := json.Marshal(traces) |
| 156 if err != nil { | 200 if err != nil { |
| 157 return err | 201 return err |
| 158 } | 202 } |
| 159 w.Header().Set("Content-Type", "application/json") | 203 w.Header().Set("Content-Type", "application/json") |
| 160 w.WriteHeader(http.StatusOK) | 204 w.WriteHeader(http.StatusOK) |
| 161 _, err = w.Write(js) | 205 _, err = w.Write(js) |
| 162 return err | 206 return err |
| 163 } | 207 } |
| OLD | NEW |