@@ -9,9 +9,11 @@ import (
9
9
"encoding/json"
10
10
"flag"
11
11
"fmt"
12
+ "go/token"
12
13
"io/fs"
13
14
"os"
14
15
"path/filepath"
16
+ "reflect"
15
17
"runtime"
16
18
"strings"
17
19
"sync"
@@ -24,10 +26,13 @@ import (
24
26
"golang.org/x/tools/gopls/internal/lsp/filecache"
25
27
"golang.org/x/tools/gopls/internal/lsp/protocol"
26
28
"golang.org/x/tools/gopls/internal/lsp/source"
29
+ "golang.org/x/tools/internal/event"
27
30
)
28
31
29
32
type stats struct {
30
33
app * Application
34
+
35
+ Anon bool `flag:"anon" help:"hide any fields that may contain user names, file names, or source code"`
31
36
}
32
37
33
38
func (s * stats ) Name () string { return "stats" }
@@ -41,14 +46,17 @@ Load the workspace for the current directory, and output a JSON summary of
41
46
workspace information relevant to performance. As a side effect, this command
42
47
populates the gopls file cache for the current workspace.
43
48
49
+ By default, this command may include output that refers to the location or
50
+ content of user code. When the -anon flag is set, fields that may refer to user
51
+ code are hidden.
52
+
44
53
Example:
45
- $ gopls stats
54
+ $ gopls stats -anon
46
55
` )
47
56
printFlagDefaults (f )
48
57
}
49
58
50
59
func (s * stats ) Run (ctx context.Context , args ... string ) error {
51
-
52
60
// This undocumented environment variable allows
53
61
// the cmd integration test to trigger a call to bug.Report.
54
62
if msg := os .Getenv ("TEST_GOPLS_BUG" ); msg != "" {
@@ -65,6 +73,10 @@ func (s *stats) Run(ctx context.Context, args ...string) error {
65
73
return fmt .Errorf ("the stats subcommand does not work with -remote" )
66
74
}
67
75
76
+ if ! s .app .Verbose {
77
+ event .SetExporter (nil ) // don't log errors to stderr
78
+ }
79
+
68
80
stats := GoplsStats {
69
81
GOOS : runtime .GOOS ,
70
82
GOARCH : runtime .GOARCH ,
@@ -139,12 +151,10 @@ func (s *stats) Run(ctx context.Context, args ...string) error {
139
151
140
152
// Gather bug reports produced by any process using
141
153
// this executable and persisted in the cache.
142
- stats .BugReports = []string {} // non-nil for JSON
143
154
do ("Gathering bug reports" , func () error {
144
- cacheDir , reports := filecache .BugReports ()
145
- stats .CacheDir = cacheDir
146
- for _ , report := range reports {
147
- stats .BugReports = append (stats .BugReports , string (report ))
155
+ stats .CacheDir , stats .BugReports = filecache .BugReports ()
156
+ if stats .BugReports == nil {
157
+ stats .BugReports = []goplsbug.Bug {} // non-nil for JSON
148
158
}
149
159
return nil
150
160
})
@@ -186,25 +196,51 @@ func (s *stats) Run(ctx context.Context, args ...string) error {
186
196
return err
187
197
}
188
198
189
- data , err := json .MarshalIndent (stats , "" , " " )
199
+ // Filter JSON output to fields that are consistent with s.Anon.
200
+ okFields := make (map [string ]interface {})
201
+ {
202
+ v := reflect .ValueOf (stats )
203
+ t := v .Type ()
204
+ for i := 0 ; i < t .NumField (); i ++ {
205
+ f := t .Field (i )
206
+ if ! token .IsExported (f .Name ) {
207
+ continue
208
+ }
209
+ if s .Anon && f .Tag .Get ("anon" ) != "ok" {
210
+ // Fields that can be served with -anon must be explicitly marked as OK.
211
+ continue
212
+ }
213
+ vf := v .FieldByName (f .Name )
214
+ okFields [f .Name ] = vf .Interface ()
215
+ }
216
+ }
217
+ data , err := json .MarshalIndent (okFields , "" , " " )
190
218
if err != nil {
191
219
return err
192
220
}
221
+
193
222
os .Stdout .Write (data )
194
223
fmt .Println ()
195
224
return nil
196
225
}
197
226
227
+ // GoplsStats holds information extracted from a gopls session in the current
228
+ // workspace.
229
+ //
230
+ // Fields that should be printed with the -anon flag should be explicitly
231
+ // marked as `anon:"ok"`. Only fields that cannot refer to user files or code
232
+ // should be marked as such.
198
233
type GoplsStats struct {
199
- GOOS , GOARCH , GOPLSCACHE string
200
- GoVersion string
201
- GoplsVersion string
202
- InitialWorkspaceLoadDuration string // in time.Duration string form
234
+ GOOS , GOARCH string `anon:"ok"`
235
+ GOPLSCACHE string
236
+ GoVersion string `anon:"ok"`
237
+ GoplsVersion string `anon:"ok"`
238
+ InitialWorkspaceLoadDuration string `anon:"ok"` // in time.Duration string form
203
239
CacheDir string
204
- BugReports []string
205
- MemStats command.MemStatsResult
206
- WorkspaceStats command.WorkspaceStatsResult
207
- DirStats dirStats
240
+ BugReports []goplsbug. Bug
241
+ MemStats command.MemStatsResult `anon:"ok"`
242
+ WorkspaceStats command.WorkspaceStatsResult `anon:"ok"`
243
+ DirStats dirStats `anon:"ok"`
208
244
}
209
245
210
246
type dirStats struct {
0 commit comments