github.com/chenfeining/golangci-lint@v1.0.2-0.20230730162517-14c6c67868df/pkg/commands/root.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "os" 6 "runtime" 7 "runtime/pprof" 8 "runtime/trace" 9 "strconv" 10 11 "github.com/spf13/cobra" 12 "github.com/spf13/pflag" 13 14 "github.com/chenfeining/golangci-lint/pkg/config" 15 "github.com/chenfeining/golangci-lint/pkg/exitcodes" 16 "github.com/chenfeining/golangci-lint/pkg/logutils" 17 ) 18 19 const ( 20 // envHelpRun value: "1". 21 envHelpRun = "HELP_RUN" 22 envMemProfileRate = "GL_MEM_PROFILE_RATE" 23 ) 24 25 func (e *Executor) persistentPreRun(_ *cobra.Command, _ []string) error { 26 if e.cfg.Run.PrintVersion { 27 _ = printVersion(logutils.StdOut, e.buildInfo) 28 os.Exit(exitcodes.Success) // a return nil is not enough to stop the process because we are inside the `preRun`. 29 } 30 31 runtime.GOMAXPROCS(e.cfg.Run.Concurrency) 32 33 if e.cfg.Run.CPUProfilePath != "" { 34 f, err := os.Create(e.cfg.Run.CPUProfilePath) 35 if err != nil { 36 return fmt.Errorf("can't create file %s: %w", e.cfg.Run.CPUProfilePath, err) 37 } 38 if err := pprof.StartCPUProfile(f); err != nil { 39 return fmt.Errorf("can't start CPU profiling: %w", err) 40 } 41 } 42 43 if e.cfg.Run.MemProfilePath != "" { 44 if rate := os.Getenv(envMemProfileRate); rate != "" { 45 runtime.MemProfileRate, _ = strconv.Atoi(rate) 46 } 47 } 48 49 if e.cfg.Run.TracePath != "" { 50 f, err := os.Create(e.cfg.Run.TracePath) 51 if err != nil { 52 return fmt.Errorf("can't create file %s: %w", e.cfg.Run.TracePath, err) 53 } 54 if err = trace.Start(f); err != nil { 55 return fmt.Errorf("can't start tracing: %w", err) 56 } 57 } 58 59 return nil 60 } 61 62 func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) error { 63 if e.cfg.Run.CPUProfilePath != "" { 64 pprof.StopCPUProfile() 65 } 66 67 if e.cfg.Run.MemProfilePath != "" { 68 f, err := os.Create(e.cfg.Run.MemProfilePath) 69 if err != nil { 70 return fmt.Errorf("can't create file %s: %w", e.cfg.Run.MemProfilePath, err) 71 } 72 73 var ms runtime.MemStats 74 runtime.ReadMemStats(&ms) 75 printMemStats(&ms, e.log) 76 77 if err := pprof.WriteHeapProfile(f); err != nil { 78 return fmt.Errorf("cCan't write heap profile: %w", err) 79 } 80 _ = f.Close() 81 } 82 83 if e.cfg.Run.TracePath != "" { 84 trace.Stop() 85 } 86 87 os.Exit(e.exitCode) 88 89 return nil 90 } 91 92 func printMemStats(ms *runtime.MemStats, logger logutils.Log) { 93 logger.Infof("Mem stats: alloc=%s total_alloc=%s sys=%s "+ 94 "heap_alloc=%s heap_sys=%s heap_idle=%s heap_released=%s heap_in_use=%s "+ 95 "stack_in_use=%s stack_sys=%s "+ 96 "mspan_sys=%s mcache_sys=%s buck_hash_sys=%s gc_sys=%s other_sys=%s "+ 97 "mallocs_n=%d frees_n=%d heap_objects_n=%d gc_cpu_fraction=%.2f", 98 formatMemory(ms.Alloc), formatMemory(ms.TotalAlloc), formatMemory(ms.Sys), 99 formatMemory(ms.HeapAlloc), formatMemory(ms.HeapSys), 100 formatMemory(ms.HeapIdle), formatMemory(ms.HeapReleased), formatMemory(ms.HeapInuse), 101 formatMemory(ms.StackInuse), formatMemory(ms.StackSys), 102 formatMemory(ms.MSpanSys), formatMemory(ms.MCacheSys), formatMemory(ms.BuckHashSys), 103 formatMemory(ms.GCSys), formatMemory(ms.OtherSys), 104 ms.Mallocs, ms.Frees, ms.HeapObjects, ms.GCCPUFraction) 105 } 106 107 func formatMemory(memBytes uint64) string { 108 const Kb = 1024 109 const Mb = Kb * 1024 110 111 if memBytes < Kb { 112 return fmt.Sprintf("%db", memBytes) 113 } 114 if memBytes < Mb { 115 return fmt.Sprintf("%dkb", memBytes/Kb) 116 } 117 return fmt.Sprintf("%dmb", memBytes/Mb) 118 } 119 120 func getDefaultConcurrency() int { 121 if os.Getenv(envHelpRun) == "1" { 122 // Make stable concurrency for README help generating builds. 123 const prettyConcurrency = 8 124 return prettyConcurrency 125 } 126 127 return runtime.NumCPU() 128 } 129 130 func (e *Executor) initRoot() { 131 rootCmd := &cobra.Command{ 132 Use: "golangci-lint", 133 Short: "golangci-lint is a smart linters runner.", 134 Long: `Smart, fast linters runner.`, 135 Args: cobra.NoArgs, 136 RunE: func(cmd *cobra.Command, _ []string) error { 137 return cmd.Help() 138 }, 139 PersistentPreRunE: e.persistentPreRun, 140 PersistentPostRunE: e.persistentPostRun, 141 } 142 143 initRootFlagSet(rootCmd.PersistentFlags(), e.cfg, e.needVersionOption()) 144 e.rootCmd = rootCmd 145 } 146 147 func (e *Executor) needVersionOption() bool { 148 return e.buildInfo.Date != "" 149 } 150 151 func initRootFlagSet(fs *pflag.FlagSet, cfg *config.Config, needVersionOption bool) { 152 fs.BoolVarP(&cfg.Run.IsVerbose, "verbose", "v", false, wh("verbose output")) 153 154 var silent bool 155 fs.BoolVarP(&silent, "silent", "s", false, wh("disables congrats outputs")) 156 if err := fs.MarkHidden("silent"); err != nil { 157 panic(err) 158 } 159 err := fs.MarkDeprecated("silent", 160 "now golangci-lint by default is silent: it doesn't print Congrats message") 161 if err != nil { 162 panic(err) 163 } 164 165 fs.StringVar(&cfg.Run.CPUProfilePath, "cpu-profile-path", "", wh("Path to CPU profile output file")) 166 fs.StringVar(&cfg.Run.MemProfilePath, "mem-profile-path", "", wh("Path to memory profile output file")) 167 fs.StringVar(&cfg.Run.TracePath, "trace-path", "", wh("Path to trace output file")) 168 fs.IntVarP(&cfg.Run.Concurrency, "concurrency", "j", getDefaultConcurrency(), wh("Concurrency (default NumCPU)")) 169 if needVersionOption { 170 fs.BoolVar(&cfg.Run.PrintVersion, "version", false, wh("Print version")) 171 } 172 173 fs.StringVar(&cfg.Output.Color, "color", "auto", wh("Use color when printing; can be 'always', 'auto', or 'never'")) 174 }