├── README.md ├── LICENSE └── main.go /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | go get -u rsc.io/gocachelogstat 3 | gocachelogstat 4 | ``` 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | 5 | // Gocachelogstat prints basic statistics about the go build cache. 6 | // The goal is to inform the decision about cache expiration policy. 7 | // 8 | // Please run: 9 | // 10 | // go get -u rsc.io/gocachelogstat 11 | // gocachelogstat 12 | // 13 | package main 14 | 15 | import ( 16 | "bytes" 17 | "fmt" 18 | "io/ioutil" 19 | "log" 20 | "os/exec" 21 | "path/filepath" 22 | "sort" 23 | "strconv" 24 | "strings" 25 | ) 26 | 27 | type entry struct { 28 | created int64 29 | lastReused int64 30 | size int64 31 | reused bool 32 | data *entry 33 | } 34 | 35 | func main() { 36 | log.SetPrefix("gocachelogstat:") 37 | log.SetFlags(0) 38 | 39 | out, err := exec.Command("go", "env", "GOCACHE").CombinedOutput() 40 | if err != nil { 41 | log.Fatalf("go env GOCACHE: %v\n%s", err, out) 42 | } 43 | dir := strings.TrimSpace(string(out)) 44 | if dir == "" { 45 | log.Fatalf("go env GOCACHE: no output (old Go version?)") 46 | } 47 | if dir == "off" { 48 | log.Fatalf("go env GOCACHE: GOCACHE=off") 49 | } 50 | 51 | data, err := ioutil.ReadFile(filepath.Join(dir, "log.txt")) 52 | if err != nil { 53 | log.Fatal(err) 54 | } 55 | 56 | var totalA, totalReusedA, totalD, totalReusedD int64 57 | 58 | var reuseA, reuseD, reuseDeltaA, reuseDeltaD []int 59 | var firstTime, lastTime int64 60 | cache := make(map[string]*entry) 61 | for _, line := range bytes.Split(data, []byte("\n")) { 62 | f := strings.Fields(string(line)) 63 | if len(f) == 0 { 64 | continue 65 | } 66 | if len(f) < 3 || f[1] == "put" && len(f) != 5 { 67 | log.Fatalf("invalid log.txt line: %v", string(line)) 68 | } 69 | t, err := strconv.ParseInt(f[0], 10, 64) 70 | if err != nil { 71 | log.Fatalf("invalid log.txt time: %v", string(line)) 72 | } 73 | if firstTime == 0 { 74 | firstTime = t 75 | } 76 | lastTime = t 77 | switch f[1] { 78 | case "put": 79 | size, err := strconv.ParseInt(f[4], 10, 64) 80 | if err != nil { 81 | log.Fatalf("invalid log.txt size: %v", string(line)) 82 | } 83 | e1 := cache[f[3]+"-d"] 84 | if e1 == nil { 85 | e1 = new(entry) 86 | e1.created = t 87 | e1.size = size 88 | cache[f[3]+"-d"] = e1 89 | totalD += size 90 | } 91 | e := cache[f[2]+"-a"] 92 | if e == nil { 93 | e = new(entry) 94 | e.created = t 95 | e.size = 154 96 | e.data = e1 97 | cache[f[2]+"-a"] = e 98 | totalA += 154 99 | } 100 | 101 | case "get", "miss": 102 | e := cache[f[2]+"-a"] 103 | if e == nil { 104 | continue 105 | } 106 | if e.lastReused == 0 { 107 | totalReusedA += e.size 108 | e.lastReused = e.created 109 | } 110 | if e.data.lastReused == 0 { 111 | totalReusedD += e.data.size 112 | e.data.lastReused = e.data.created 113 | } 114 | reuseA = append(reuseA, int(t-e.created)) 115 | reuseD = append(reuseD, int(t-e.data.created)) 116 | reuseDeltaA = append(reuseDeltaA, int(t-e.lastReused)) 117 | reuseDeltaD = append(reuseDeltaD, int(t-e.data.lastReused)) 118 | 119 | e.lastReused = t 120 | e.data.lastReused = t 121 | } 122 | } 123 | 124 | sort.Ints(reuseA) 125 | sort.Ints(reuseD) 126 | sort.Ints(reuseDeltaA) 127 | sort.Ints(reuseDeltaD) 128 | 129 | fmt.Printf("Please add the following output (including the quotes) to https://golang.org/issue/22990\n\n") 130 | fmt.Printf("```\n") 131 | defer fmt.Printf("```\n") 132 | 133 | fmt.Printf("cache age: %.2f days\n", float64(lastTime-firstTime)/86400) 134 | printCache("action", totalA, totalReusedA, reuseA, reuseDeltaA) 135 | printCache("data", totalD, totalReusedD, reuseD, reuseDeltaD) 136 | } 137 | 138 | func printCache(name string, total, totalReused int64, reuse, reuseDelta []int) { 139 | fmt.Printf("%s cache: %d bytes, %d reused\n", name, total, totalReused) 140 | if len(reuse) == 0 { 141 | fmt.Printf("\tno reuse\n") 142 | } else { 143 | fmt.Printf("\treuse time percentiles\n") 144 | for i := 10; i <= 90; i += 10 { 145 | j := len(reuse) * i / 100 146 | fmt.Printf("\t\t%d%% %.2f days\n", i, float64(reuse[j])/86400) 147 | } 148 | fmt.Printf("\t\t95%% %.2f days\n", float64(reuse[len(reuse)*95/100])/86400) 149 | fmt.Printf("\t\t99%% %.2f days\n", float64(reuse[len(reuse)*99/100])/86400) 150 | fmt.Printf("\t\t99.9%% %.2f days\n", float64(reuse[len(reuse)*999/1000])/86400) 151 | fmt.Printf("\t\tmax %.2f days\n", float64(reuse[len(reuse)-1])/86400) 152 | fmt.Printf("\treuse time delta percentiles\n") 153 | for i := 10; i <= 90; i += 10 { 154 | j := len(reuseDelta) * i / 100 155 | fmt.Printf("\t\t%d%% %.2f days\n", i, float64(reuseDelta[j])/86400) 156 | } 157 | fmt.Printf("\t\t95%% %.2f days\n", float64(reuseDelta[len(reuse)*95/100])/86400) 158 | fmt.Printf("\t\t99%% %.2f days\n", float64(reuseDelta[len(reuse)*99/100])/86400) 159 | fmt.Printf("\t\t99.9%% %.2f days\n", float64(reuseDelta[len(reuse)*999/1000])/86400) 160 | fmt.Printf("\t\tmax %.2f days\n", float64(reuseDelta[len(reuse)-1])/86400) 161 | } 162 | } 163 | --------------------------------------------------------------------------------