Last active
January 3, 2023 10:47
-
-
Save parkghost/5397740 to your computer and use it in GitHub Desktop.
a toolkit to help profiling go programmes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"os" | |
"runtime" | |
"runtime/debug" | |
"runtime/pprof" | |
"strconv" | |
"sync/atomic" | |
"syscall" | |
"time" | |
) | |
var heapProfileCounter int32 | |
var startTime = time.Now() | |
var pid int | |
func init() { | |
pid = os.Getpid() | |
} | |
func StartCPUProfile() { | |
f, err := os.Create("cpu-" + strconv.Itoa(pid) + ".pprof") | |
if err != nil { | |
log.Fatal(err) | |
} | |
pprof.StartCPUProfile(f) | |
} | |
func StopCPUProfile() { | |
pprof.StopCPUProfile() | |
} | |
func StartBlockProfile(rate int) { | |
runtime.SetBlockProfileRate(rate) | |
} | |
func StopBlockProfile() { | |
filename := "block-" + strconv.Itoa(pid) + ".pprof" | |
f, err := os.Create(filename) | |
if err != nil { | |
log.Fatal(err) | |
} | |
if err = pprof.Lookup("block").WriteTo(f, 0); err != nil { | |
log.Fatalf(" can't write %s: %s", filename, err) | |
} | |
f.Close() | |
} | |
func SetMemProfileRate(rate int) { | |
runtime.MemProfileRate = rate | |
} | |
func GC() { | |
runtime.GC() | |
} | |
func DumpHeap() { | |
filename := "heap-" + strconv.Itoa(pid) + "-" + strconv.Itoa(int(atomic.AddInt32(&heapProfileCounter, 1))) + ".pprof" | |
f, err := os.Create(filename) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "testing: %s", err) | |
return | |
} | |
if err = pprof.WriteHeapProfile(f); err != nil { | |
fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", filename, err) | |
} | |
f.Close() | |
} | |
func showSystemStat(interval time.Duration, count int) { | |
usage1 := &syscall.Rusage{} | |
var lastUtime int64 | |
var lastStime int64 | |
counter := 0 | |
for { | |
//http://man7.org/linux/man-pages/man3/vtimes.3.html | |
syscall.Getrusage(syscall.RUSAGE_SELF, usage1) | |
utime := usage1.Utime.Nano() | |
stime := usage1.Stime.Nano() | |
userCPUUtil := float64(utime-lastUtime) * 100 / float64(interval) | |
sysCPUUtil := float64(stime-lastStime) * 100 / float64(interval) | |
memUtil := usage1.Maxrss * 1024 | |
lastUtime = utime | |
lastStime = stime | |
if counter > 0 { | |
fmt.Printf("cpu: %3.2f%% us %3.2f%% sy, mem:%s \n", userCPUUtil, sysCPUUtil, toH(uint64(memUtil))) | |
} | |
counter += 1 | |
if count >= 1 && count < counter { | |
return | |
} | |
time.Sleep(interval) | |
} | |
} | |
func ShowSystemStat(seconds int) { | |
go func() { | |
interval := time.Duration(seconds) * time.Second | |
showSystemStat(interval, 0) | |
}() | |
} | |
func PrintSystemStats() { | |
interval := time.Duration(1) * time.Second | |
showSystemStat(interval, 1) | |
} | |
func ShowGCStat() { | |
go func() { | |
var numGC int64 | |
interval := time.Duration(100) * time.Millisecond | |
gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)} | |
memStats := &runtime.MemStats{} | |
for { | |
debug.ReadGCStats(gcstats) | |
if gcstats.NumGC > numGC { | |
runtime.ReadMemStats(memStats) | |
printGC(memStats, gcstats) | |
numGC = gcstats.NumGC | |
} | |
time.Sleep(interval) | |
} | |
}() | |
} | |
func PrintGCSummary() { | |
memStats := &runtime.MemStats{} | |
runtime.ReadMemStats(memStats) | |
gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)} | |
debug.ReadGCStats(gcstats) | |
printGC(memStats, gcstats) | |
} | |
func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats) { | |
if gcstats.NumGC > 0 { | |
lastPause := gcstats.Pause[0] | |
elapsed := time.Now().Sub(startTime) | |
overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100 | |
allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() | |
fmt.Printf("NumGC:%d Pause:%s Pause(Avg):%s Overhead:%3.2f%% Alloc:%s Sys:%s Alloc(Rate):%s/s Histogram:%s %s %s \n", | |
gcstats.NumGC, | |
toS(lastPause), | |
toS(avg(gcstats.Pause)), | |
overhead, | |
toH(memStats.Alloc), | |
toH(memStats.Sys), | |
toH(uint64(allocatedRate)), | |
toS(gcstats.PauseQuantiles[94]), | |
toS(gcstats.PauseQuantiles[98]), | |
toS(gcstats.PauseQuantiles[99])) | |
} else { | |
// while GC has disabled | |
elapsed := time.Now().Sub(startTime) | |
allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() | |
fmt.Printf("Alloc:%s Sys:%s Alloc(Rate):%s/s\n", | |
toH(memStats.Alloc), | |
toH(memStats.Sys), | |
toH(uint64(allocatedRate))) | |
} | |
} | |
func avg(items []time.Duration) time.Duration { | |
var sum time.Duration | |
for _, item := range items { | |
sum += item | |
} | |
return time.Duration(int64(sum) / int64(len(items))) | |
} | |
// human readable format | |
func toH(bytes uint64) string { | |
switch { | |
case bytes < 1024: | |
return fmt.Sprintf("%dB", bytes) | |
case bytes < 1024*1024: | |
return fmt.Sprintf("%.2fK", float64(bytes)/1024) | |
case bytes < 1024*1024*1024: | |
return fmt.Sprintf("%.2fM", float64(bytes)/1024/1024) | |
default: | |
return fmt.Sprintf("%.2fG", float64(bytes)/1024/1024/1024) | |
} | |
} | |
// short string format | |
func toS(d time.Duration) string { | |
u := uint64(d) | |
if u < uint64(time.Second) { | |
switch { | |
case u == 0: | |
return "0" | |
case u < uint64(time.Microsecond): | |
return fmt.Sprintf("%.2fns", float64(u)) | |
case u < uint64(time.Millisecond): | |
return fmt.Sprintf("%.2fus", float64(u)/1000) | |
default: | |
return fmt.Sprintf("%.2fms", float64(u)/1000/1000) | |
} | |
} else { | |
switch { | |
case u < uint64(time.Minute): | |
return fmt.Sprintf("%.2fs", float64(u)/1000/1000/1000) | |
case u < uint64(time.Hour): | |
return fmt.Sprintf("%.2fm", float64(u)/1000/1000/1000/60) | |
default: | |
return fmt.Sprintf("%.2fh", float64(u)/1000/1000/1000/60/60) | |
} | |
} | |
} |
@xtaci
Thanks for your comment and Sorry for the late reply.
I just saw your comment today and fix it at reversion 5.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
src/helper/profilingtool.go:87: invalid operation: utime - lastUtime (mismatched types int32 and int64)
src/helper/profilingtool.go:88: invalid operation: stime - lastStime (mismatched types int32 and int64)
src/helper/profilingtool.go:91: cannot use utime (type int32) as type int64 in assignment
src/helper/profilingtool.go:92: cannot use stime (type int32) as type int64 in assignment
compiles failed under 32-bit ubuntu