commands: Add an option to print memory usage at intervals
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 25 Jun 2020 09:44:27 +0000 (11:44 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 25 Jun 2020 10:19:21 +0000 (12:19 +0200)
Use it with `hugo --print-mem

commands/commands.go
commands/hugo.go

index 66fd9caa43d3313748a4146b206783fe226f3653..09e0c84553df3c71842d82a63db469af3acf79ff 100644 (file)
@@ -212,6 +212,7 @@ type hugoBuilderCommon struct {
        memprofile   string
        mutexprofile string
        traceprofile string
+       printm       bool
 
        // TODO(bep) var vs string
        logging    bool
@@ -299,6 +300,7 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
        cmd.Flags().BoolP("path-warnings", "", false, "print warnings on duplicate target paths etc.")
        cmd.Flags().StringVarP(&cc.cpuprofile, "profile-cpu", "", "", "write cpu profile to `file`")
        cmd.Flags().StringVarP(&cc.memprofile, "profile-mem", "", "", "write memory profile to `file`")
+       cmd.Flags().BoolVarP(&cc.printm, "print-mem", "", false, "print memory usage to screen at intervals")
        cmd.Flags().StringVarP(&cc.mutexprofile, "profile-mutex", "", "", "write Mutex profile to `file`")
        cmd.Flags().StringVarP(&cc.traceprofile, "trace", "", "", "write trace to `file` (not useful in general)")
 
index b7392bc420c81ff3d9c0a8d1655a919f66a3f828..5442c32d708a447a8f745af11cb34a9e8166e747 100644 (file)
@@ -82,6 +82,7 @@ func (r Response) IsUserError() bool {
 // Execute adds all child commands to the root command HugoCmd and sets flags appropriately.
 // The args are usually filled with os.Args[1:].
 func Execute(args []string) Response {
+
        hugoCmd := newCommandsBuilder().addAll().build()
        cmd := hugoCmd.getCommand()
        cmd.SetArgs(args)
@@ -427,7 +428,37 @@ func (c *commandeer) initMutexProfile() (func(), error) {
 
 }
 
+func (c *commandeer) initMemTicker() func() {
+       memticker := time.NewTicker(5 * time.Second)
+       quit := make(chan struct{})
+       printMem := func() {
+               var m runtime.MemStats
+               runtime.ReadMemStats(&m)
+               fmt.Printf("\n\nAlloc = %v\nTotalAlloc = %v\nSys = %v\nNumGC = %v\n\n", formatByteCount(m.Alloc), formatByteCount(m.TotalAlloc), formatByteCount(m.Sys), m.NumGC)
+
+       }
+
+       go func() {
+               for {
+                       select {
+                       case <-memticker.C:
+                               printMem()
+                       case <-quit:
+                               memticker.Stop()
+                               printMem()
+                               return
+                       }
+
+               }
+       }()
+
+       return func() {
+               close(quit)
+       }
+}
+
 func (c *commandeer) initProfiling() (func(), error) {
+
        stopCPUProf, err := c.initCPUProfile()
        if err != nil {
                return nil, err
@@ -443,6 +474,11 @@ func (c *commandeer) initProfiling() (func(), error) {
                return nil, err
        }
 
+       var stopMemTicker func()
+       if c.h.printm {
+               stopMemTicker = c.initMemTicker()
+       }
+
        return func() {
                c.initMemProfile()
 
@@ -456,6 +492,10 @@ func (c *commandeer) initProfiling() (func(), error) {
                if stopTraceProf != nil {
                        stopTraceProf()
                }
+
+               if stopMemTicker != nil {
+                       stopMemTicker()
+               }
        }, nil
 }
 
@@ -1175,3 +1215,17 @@ func pickOneWriteOrCreatePath(events []fsnotify.Event) string {
 
        return name
 }
+
+func formatByteCount(b uint64) string {
+       const unit = 1000
+       if b < unit {
+               return fmt.Sprintf("%d B", b)
+       }
+       div, exp := int64(unit), 0
+       for n := b / unit; n >= unit; n /= unit {
+               div *= unit
+               exp++
+       }
+       return fmt.Sprintf("%.1f %cB",
+               float64(b)/float64(div), "kMGTPE"[exp])
+}