@@ -32,6 +32,7 @@ import (
32
32
"os"
33
33
"path/filepath"
34
34
"sort"
35
+ "strings"
35
36
"sync"
36
37
"sync/atomic"
37
38
"time"
@@ -203,7 +204,11 @@ func writeFileNoTrunc(filename string, data []byte, perm os.FileMode) error {
203
204
return err
204
205
}
205
206
206
- const casKind = "cas" // kind for CAS (content-addressable store) files
207
+ // reserved kind strings
208
+ const (
209
+ casKind = "cas" // content-addressable store files
210
+ bugKind = "bug" // gopls bug reports
211
+ )
207
212
208
213
var iolimit = make (chan struct {}, 128 ) // counting semaphore to limit I/O concurrency in Set.
209
214
@@ -233,15 +238,19 @@ func SetBudget(new int64) (old int64) {
233
238
//
234
239
// A typical cache file has a name such as:
235
240
//
236
- // $HOME/Library/Caches / gopls / VVVVVVVV / kind / KK / KKKK...KKKK
241
+ // $HOME/Library/Caches / gopls / VVVVVVVV / KK / KKKK...KKKK - kind
237
242
//
238
243
// The portions separated by spaces are as follows:
239
244
// - The user's preferred cache directory; the default value varies by OS.
240
245
// - The constant "gopls".
241
246
// - The "version", 32 bits of the digest of the gopls executable.
242
- // - The kind or purpose of this cache subtree (e.g. "analysis").
243
247
// - The first 8 bits of the key, to avoid huge directories.
244
248
// - The full 256 bits of the key.
249
+ // - The kind or purpose of this cache file (e.g. "analysis").
250
+ //
251
+ // The kind establishes a namespace for the keys. It is represented as
252
+ // a suffix, not a segment, as this significantly reduces the number
253
+ // of directories created, and thus the storage overhead.
245
254
//
246
255
// Previous iterations of the design aimed for the invariant that once
247
256
// a file is written, its contents are never modified, though it may
@@ -290,16 +299,14 @@ func SetBudget(new int64) (old int64) {
290
299
// the entire gopls directory so that newer binaries can clean up
291
300
// after older ones: in the development cycle especially, new
292
301
// new versions may be created frequently.
293
- //
294
- // TODO(adonovan): opt: use "VVVVVVVV / KK / KKKK...KKKK-kind" to
295
- // avoid creating 256 directories per distinct kind (+ cas).
296
302
func filename (kind string , key [32 ]byte ) (string , error ) {
297
- hex := fmt .Sprintf ("%x" , key )
303
+ base := fmt .Sprintf ("%x-%s " , key , kind )
298
304
dir , err := getCacheDir ()
299
305
if err != nil {
300
306
return "" , err
301
307
}
302
- return filepath .Join (dir , kind , hex [:2 ], hex ), nil
308
+ // Keep the BugReports function consistent with this one.
309
+ return filepath .Join (dir , base [:2 ], base ), nil
303
310
}
304
311
305
312
// getCacheDir returns the persistent cache directory of all processes
@@ -526,8 +533,6 @@ func gc(goplsDir string) {
526
533
}
527
534
}
528
535
529
- const bugKind = "bug" // reserved kind for gopls bug reports
530
-
531
536
func init () {
532
537
// Register a handler to durably record this process's first
533
538
// assertion failure in the cache so that we can ask users to
@@ -544,29 +549,35 @@ func init() {
544
549
545
550
// BugReports returns a new unordered array of the contents
546
551
// of all cached bug reports produced by this executable.
547
- func BugReports () [][]byte {
552
+ // It also returns the location of the cache directory
553
+ // used by this process (or "" on initialization error).
554
+ func BugReports () (string , [][]byte ) {
555
+ // To test this logic, run:
556
+ // $ TEST_GOPLS_BUG=oops gopls stats # trigger a bug
557
+ // $ gopls stats # list the bugs
558
+
548
559
dir , err := getCacheDir ()
549
560
if err != nil {
550
- return nil // ignore initialization errors
561
+ return "" , nil // ignore initialization errors
551
562
}
552
563
var result [][]byte
553
- _ = filepath .Walk (filepath .Join (dir , bugKind ),
554
- func (path string , info fs.FileInfo , err error ) error {
555
- if err != nil {
556
- return nil // ignore readdir/stat errors
564
+ _ = filepath .Walk (dir , func (path string , info fs.FileInfo , err error ) error {
565
+ if err != nil {
566
+ return nil // ignore readdir/stat errors
567
+ }
568
+ // Parse the key from each "XXXX-bug" cache file name.
569
+ if ! info .IsDir () && strings .HasSuffix (path , bugKind ) {
570
+ var key [32 ]byte
571
+ n , err := hex .Decode (key [:], []byte (filepath .Base (path )[:len (key )* 2 ]))
572
+ if err != nil || n != len (key ) {
573
+ return nil // ignore malformed file names
557
574
}
558
- if ! info .IsDir () {
559
- var key [32 ]byte
560
- n , err := hex .Decode (key [:], []byte (filepath .Base (path )))
561
- if err != nil || n != len (key ) {
562
- return nil // ignore malformed file names
563
- }
564
- content , err := Get (bugKind , key )
565
- if err == nil { // ignore read errors
566
- result = append (result , content )
567
- }
575
+ content , err := Get (bugKind , key )
576
+ if err == nil { // ignore read errors
577
+ result = append (result , content )
568
578
}
569
- return nil
570
- })
571
- return result
579
+ }
580
+ return nil
581
+ })
582
+ return dir , result
572
583
}
0 commit comments