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  }