--- /dev/null
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package parse
+
+import (
+ "fmt"
+ "log"
+ "strings"
+)
+
+// Rlog contains the processed logs
+type Rlog struct {
+ Logs []*Logmsg // In the order in the log file
+ ServerCall map[string]*Logmsg // ID->Request, client->server
+ ServerReply map[string]*Logmsg // ID->Response, server->client (includes Errors)
+ ClientCall map[string]*Logmsg
+ ClientReply map[string]*Logmsg
+ ClientNotifs []*Logmsg
+ ServerNotifs []*Logmsg
+ Histogram *LogHist
+}
+
+func newRlog(x []*Logmsg) *Rlog {
+ return &Rlog{Logs: x,
+ ServerCall: make(map[string]*Logmsg),
+ ServerReply: make(map[string]*Logmsg),
+ ClientCall: make(map[string]*Logmsg),
+ ClientReply: make(map[string]*Logmsg),
+ ClientNotifs: []*Logmsg{},
+ ServerNotifs: []*Logmsg{},
+ Histogram: &LogHist{},
+ }
+}
+
+// Counts returns a one-line summary of an Rlog
+func (r *Rlog) Counts() string {
+ return fmt.Sprintf("logs:%d srvC:%d srvR:%d clC:%d clR:%d clN:%d srvN:%d",
+ len(r.Logs),
+ len(r.ServerCall), len(r.ServerReply), len(r.ClientCall), len(r.ClientReply),
+ len(r.ClientNotifs), len(r.ServerNotifs))
+}
+
+// ToRlog reads a log file and returns a *Rlog
+func ToRlog(fname string) (*Rlog, error) {
+ x, err := ReadLogs(fname)
+ if err != nil {
+ return nil, err
+ }
+ ans := newRlog(x)
+ for _, l := range x {
+ switch l.Type {
+ case ClRequest:
+ ans.ServerCall[l.ID] = l
+ case ClResponse:
+ ans.ServerReply[l.ID] = l
+ if l.Type != ReportErr {
+ n := 0
+ fmt.Sscanf(l.Elapsed, "%d", &n)
+ ans.Histogram.add(n)
+ }
+ case SvRequest:
+ ans.ClientCall[l.ID] = l
+ case SvResponse:
+ ans.ClientReply[l.ID] = l
+ case ToClient:
+ ans.ClientNotifs = append(ans.ClientNotifs, l)
+ case ToServer:
+ ans.ServerNotifs = append(ans.ServerNotifs, l)
+ case ReportErr:
+ ans.ServerReply[l.ID] = l
+ l.Method = ans.ServerCall[l.ID].Method // Method not in log message
+ default:
+ log.Fatalf("eh? %s/%s (%s)", l.Type, l.Method, l.ID)
+ }
+ }
+ return ans, nil
+}
+
+// LogHist gets ints, and puts them into buckets:
+// <=10, <=30, 100, 300, 1000, ...
+// It produces a historgram of elapsed times in milliseconds
+type LogHist struct {
+ cnts []int
+}
+
+func (l *LogHist) add(n int) {
+ if n < 0 {
+ n = 0
+ }
+ bucket := 0
+ for ; n > 0; n /= 10 {
+ if n < 10 {
+ break
+ }
+ if n < 30 {
+ bucket++
+ break
+ }
+ bucket += 2
+ }
+ if len(l.cnts) <= bucket {
+ for j := len(l.cnts); j < bucket+10; j++ {
+ l.cnts = append(l.cnts, 0)
+ }
+ }
+ l.cnts[bucket]++
+}
+
+// String returns a string describing a histogram
+func (l *LogHist) String() string {
+ top := len(l.cnts) - 1
+ for ; top > 0 && l.cnts[top] == 0; top-- {
+ }
+ labs := []string{"10", "30"}
+ out := strings.Builder{}
+ out.WriteByte('[')
+ for i := 0; i <= top; i++ {
+ label := labs[i%2]
+ labs[i%2] += "0"
+ fmt.Fprintf(&out, "%s:%d ", label, l.cnts[i])
+ }
+ out.WriteByte(']')
+ return out.String()
+}