1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main
6
7import (
8	"cmp"
9	"encoding/binary"
10	"flag"
11	"fmt"
12	"io"
13	"log"
14	"os"
15	"slices"
16	"text/tabwriter"
17
18	"internal/trace/event"
19	"internal/trace/raw"
20)
21
22func init() {
23	flag.Usage = func() {
24		fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [mode]\n", os.Args[0])
25		fmt.Fprintf(flag.CommandLine.Output(), "\n")
26		fmt.Fprintf(flag.CommandLine.Output(), "Accepts a trace at stdin.\n")
27		fmt.Fprintf(flag.CommandLine.Output(), "\n")
28		fmt.Fprintf(flag.CommandLine.Output(), "Supported modes:")
29		fmt.Fprintf(flag.CommandLine.Output(), "\n")
30		fmt.Fprintf(flag.CommandLine.Output(), "* size  - dumps size stats\n")
31		fmt.Fprintf(flag.CommandLine.Output(), "\n")
32		flag.PrintDefaults()
33	}
34	log.SetFlags(0)
35}
36
37func main() {
38	log.SetPrefix("")
39	flag.Parse()
40
41	if flag.NArg() != 1 {
42		log.Print("missing mode argument")
43		flag.Usage()
44		os.Exit(1)
45	}
46	var err error
47	switch mode := flag.Arg(0); mode {
48	case "size":
49		err = printSizeStats(os.Stdin)
50	default:
51		log.Printf("unknown mode %q", mode)
52		flag.Usage()
53		os.Exit(1)
54	}
55	if err != nil {
56		log.Fatalf("error: %v", err)
57		os.Exit(1)
58	}
59}
60
61func printSizeStats(r io.Reader) error {
62	cr := countingReader{Reader: r}
63	tr, err := raw.NewReader(&cr)
64	if err != nil {
65		return err
66	}
67	type eventStats struct {
68		typ   event.Type
69		count int
70		bytes int
71	}
72	var stats [256]eventStats
73	for i := range stats {
74		stats[i].typ = event.Type(i)
75	}
76	eventsRead := 0
77	for {
78		e, err := tr.ReadEvent()
79		if err == io.EOF {
80			break
81		}
82		if err != nil {
83			return err
84		}
85		s := &stats[e.Ev]
86		s.count++
87		s.bytes += encodedSize(&e)
88		eventsRead++
89	}
90	slices.SortFunc(stats[:], func(a, b eventStats) int {
91		return cmp.Compare(b.bytes, a.bytes)
92	})
93	specs := tr.Version().Specs()
94	w := tabwriter.NewWriter(os.Stdout, 3, 8, 2, ' ', 0)
95	fmt.Fprintf(w, "Event\tBytes\t%%\tCount\t%%\n")
96	fmt.Fprintf(w, "-\t-\t-\t-\t-\n")
97	for i := range stats {
98		stat := &stats[i]
99		name := ""
100		if int(stat.typ) >= len(specs) {
101			name = fmt.Sprintf("<unknown (%d)>", stat.typ)
102		} else {
103			name = specs[stat.typ].Name
104		}
105		bytesPct := float64(stat.bytes) / float64(cr.bytesRead) * 100
106		countPct := float64(stat.count) / float64(eventsRead) * 100
107		fmt.Fprintf(w, "%s\t%d\t%.2f%%\t%d\t%.2f%%\n", name, stat.bytes, bytesPct, stat.count, countPct)
108	}
109	w.Flush()
110	return nil
111}
112
113func encodedSize(e *raw.Event) int {
114	size := 1
115	var buf [binary.MaxVarintLen64]byte
116	for _, arg := range e.Args {
117		size += binary.PutUvarint(buf[:], arg)
118	}
119	spec := e.Version.Specs()[e.Ev]
120	if spec.HasData {
121		size += binary.PutUvarint(buf[:], uint64(len(e.Data)))
122		size += len(e.Data)
123	}
124	return size
125}
126
127type countingReader struct {
128	io.Reader
129	bytesRead int
130}
131
132func (r *countingReader) Read(b []byte) (int, error) {
133	n, err := r.Reader.Read(b)
134	r.bytesRead += n
135	return n, err
136}
137