forked from fcgmedia/profile
-
Notifications
You must be signed in to change notification settings - Fork 1
/
profile.go
169 lines (146 loc) · 3.66 KB
/
profile.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Package profile provides a simple way to manage runtime/pprof
// profiling of your Go application.
package profile
import (
"io/ioutil"
"log"
"os"
"os/signal"
"path/filepath"
"runtime"
"runtime/pprof"
)
// Config controls the operation of the profile package.
type Config struct {
// Quiet suppresses informational messages during profiling.
Quiet bool
// CPUProfile controls if cpu profiling will be enabled.
// It defaults to false.
CPUProfile bool
// MemProfile controls if memory profiling will be enabled.
// It defaults to false.
MemProfile bool
// BlockProfile controls if block (contention) profiling will
// be enabled.
// It defaults to false.
BlockProfile bool
// ProfilePath controls the base path where various profiling
// files are written. If blank, the base path will be generated
// by ioutil.TempDir.
ProfilePath string
// NoShutdownHook controls whether the profiling package should
// hook SIGINT to write profiles cleanly.
// Programs with more sophisticated signal handling should set
// this to true and ensure the Stop() function returned from Start()
// is called during shutdown.
NoShutdownHook bool
}
var zeroConfig Config
const memProfileRate = 4096
func defaultConfig() *Config { return &zeroConfig }
var (
CPUProfile = &Config{
CPUProfile: true,
}
MemProfile = &Config{
MemProfile: true,
}
BlockProfile = &Config{
BlockProfile: true,
}
)
type profile struct {
path string
*Config
closers []func()
}
func (p *profile) Stop() {
for _, c := range p.closers {
c()
}
}
// Start starts a new profiling session configured using *Config.
// The caller should call the Stop method on the value returned
// to cleanly stop profiling.
// Passing a nil *Config is the same as passing a *Config with
// defaults chosen.
func Start(cfg *Config) interface {
Stop()
} {
if cfg == nil {
cfg = defaultConfig()
}
path := cfg.ProfilePath
var err error
if path == "" {
path, err = ioutil.TempDir("", "profile")
} else {
err = os.MkdirAll(path, 0777)
}
if err != nil {
log.Fatalf("profile: could not create initial output directory: %v", err)
}
prof := &profile{
path: path,
Config: cfg,
}
if prof.CPUProfile {
fn := filepath.Join(prof.path, "cpu.pprof")
f, err := os.Create(fn)
if err != nil {
log.Fatalf("profile: could not create cpu profile %q: %v", fn, err)
}
if !prof.Quiet {
log.Printf("profile: cpu profiling enabled, %s", fn)
}
pprof.StartCPUProfile(f)
prof.closers = append(prof.closers, func() {
pprof.StopCPUProfile()
f.Close()
})
}
if prof.MemProfile {
fn := filepath.Join(prof.path, "mem.pprof")
f, err := os.Create(fn)
if err != nil {
log.Fatalf("profile: could not create memory profile %q: %v", fn, err)
}
old := runtime.MemProfileRate
runtime.MemProfileRate = memProfileRate
if !prof.Quiet {
log.Printf("profile: memory profiling enabled, %s", fn)
}
prof.closers = append(prof.closers, func() {
pprof.Lookup("heap").WriteTo(f, 0)
f.Close()
runtime.MemProfileRate = old
})
}
if prof.BlockProfile {
fn := filepath.Join(prof.path, "block.pprof")
f, err := os.Create(fn)
if err != nil {
log.Fatalf("profile: could not create block profile %q: %v", fn, err)
}
runtime.SetBlockProfileRate(1)
if !prof.Quiet {
log.Printf("profile: block profiling enabled, %s", fn)
}
prof.closers = append(prof.closers, func() {
pprof.Lookup("block").WriteTo(f, 0)
f.Close()
runtime.SetBlockProfileRate(0)
})
}
if !prof.NoShutdownHook {
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
log.Println("profile: caught interrupt, stopping profiles")
prof.Stop()
os.Exit(0)
}()
}
return prof
}