]>
Commit | Line | Data |
---|---|---|
7a938933 ILT |
1 | // Copyright 2010 The Go Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style | |
3 | // license that can be found in the LICENSE file. | |
4 | ||
5 | // Package pprof writes runtime profiling data in the format expected | |
6 | // by the pprof visualization tool. | |
7 | // For more information about pprof, see | |
8 | // http://code.google.com/p/google-perftools/. | |
9 | package pprof | |
10 | ||
11 | import ( | |
12 | "bufio" | |
501699af | 13 | "bytes" |
7a938933 ILT |
14 | "fmt" |
15 | "io" | |
7a938933 | 16 | "runtime" |
501699af ILT |
17 | "sort" |
18 | "strings" | |
8039ca76 | 19 | "sync" |
501699af | 20 | "text/tabwriter" |
7a938933 ILT |
21 | ) |
22 | ||
593f74bb ILT |
23 | // BUG(rsc): A bug in the OS X Snow Leopard 64-bit kernel prevents |
24 | // CPU profiling from giving accurate results on that system. | |
506cf9aa | 25 | |
501699af ILT |
26 | // A Profile is a collection of stack traces showing the call sequences |
27 | // that led to instances of a particular event, such as allocation. | |
28 | // Packages can create and maintain their own profiles; the most common | |
29 | // use is for tracking resources that must be explicitly closed, such as files | |
30 | // or network connections. | |
31 | // | |
32 | // A Profile's methods can be called from multiple goroutines simultaneously. | |
33 | // | |
34 | // Each Profile has a unique name. A few profiles are predefined: | |
35 | // | |
36 | // goroutine - stack traces of all current goroutines | |
37 | // heap - a sampling of all heap allocations | |
38 | // threadcreate - stack traces that led to the creation of new OS threads | |
4ccad563 | 39 | // block - stack traces that led to blocking on synchronization primitives |
501699af ILT |
40 | // |
41 | // These predefine profiles maintain themselves and panic on an explicit | |
42 | // Add or Remove method call. | |
43 | // | |
44 | // The CPU profile is not available as a Profile. It has a special API, | |
45 | // the StartCPUProfile and StopCPUProfile functions, because it streams | |
46 | // output to a writer during profiling. | |
47 | // | |
48 | type Profile struct { | |
49 | name string | |
50 | mu sync.Mutex | |
51 | m map[interface{}][]uintptr | |
52 | count func() int | |
53 | write func(io.Writer, int) error | |
54 | } | |
55 | ||
56 | // profiles records all registered profiles. | |
57 | var profiles struct { | |
58 | mu sync.Mutex | |
59 | m map[string]*Profile | |
60 | } | |
61 | ||
62 | var goroutineProfile = &Profile{ | |
63 | name: "goroutine", | |
64 | count: countGoroutine, | |
65 | write: writeGoroutine, | |
66 | } | |
67 | ||
68 | var threadcreateProfile = &Profile{ | |
69 | name: "threadcreate", | |
70 | count: countThreadCreate, | |
71 | write: writeThreadCreate, | |
72 | } | |
73 | ||
74 | var heapProfile = &Profile{ | |
75 | name: "heap", | |
76 | count: countHeap, | |
77 | write: writeHeap, | |
78 | } | |
79 | ||
4ccad563 ILT |
80 | var blockProfile = &Profile{ |
81 | name: "block", | |
82 | count: countBlock, | |
83 | write: writeBlock, | |
84 | } | |
85 | ||
501699af ILT |
86 | func lockProfiles() { |
87 | profiles.mu.Lock() | |
88 | if profiles.m == nil { | |
89 | // Initial built-in profiles. | |
90 | profiles.m = map[string]*Profile{ | |
91 | "goroutine": goroutineProfile, | |
92 | "threadcreate": threadcreateProfile, | |
93 | "heap": heapProfile, | |
4ccad563 | 94 | "block": blockProfile, |
501699af ILT |
95 | } |
96 | } | |
97 | } | |
98 | ||
99 | func unlockProfiles() { | |
100 | profiles.mu.Unlock() | |
101 | } | |
102 | ||
103 | // NewProfile creates a new profile with the given name. | |
104 | // If a profile with that name already exists, NewProfile panics. | |
105 | // The convention is to use a 'import/path.' prefix to create | |
106 | // separate name spaces for each package. | |
107 | func NewProfile(name string) *Profile { | |
108 | lockProfiles() | |
109 | defer unlockProfiles() | |
110 | if name == "" { | |
111 | panic("pprof: NewProfile with empty name") | |
112 | } | |
113 | if profiles.m[name] != nil { | |
114 | panic("pprof: NewProfile name already in use: " + name) | |
115 | } | |
116 | p := &Profile{ | |
117 | name: name, | |
118 | m: map[interface{}][]uintptr{}, | |
119 | } | |
120 | profiles.m[name] = p | |
121 | return p | |
122 | } | |
123 | ||
124 | // Lookup returns the profile with the given name, or nil if no such profile exists. | |
125 | func Lookup(name string) *Profile { | |
126 | lockProfiles() | |
127 | defer unlockProfiles() | |
128 | return profiles.m[name] | |
129 | } | |
130 | ||
131 | // Profiles returns a slice of all the known profiles, sorted by name. | |
132 | func Profiles() []*Profile { | |
133 | lockProfiles() | |
134 | defer unlockProfiles() | |
135 | ||
136 | var all []*Profile | |
137 | for _, p := range profiles.m { | |
138 | all = append(all, p) | |
139 | } | |
140 | ||
141 | sort.Sort(byName(all)) | |
142 | return all | |
143 | } | |
144 | ||
145 | type byName []*Profile | |
146 | ||
147 | func (x byName) Len() int { return len(x) } | |
148 | func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } | |
149 | func (x byName) Less(i, j int) bool { return x[i].name < x[j].name } | |
150 | ||
151 | // Name returns this profile's name, which can be passed to Lookup to reobtain the profile. | |
152 | func (p *Profile) Name() string { | |
153 | return p.name | |
154 | } | |
155 | ||
156 | // Count returns the number of execution stacks currently in the profile. | |
157 | func (p *Profile) Count() int { | |
158 | p.mu.Lock() | |
159 | defer p.mu.Unlock() | |
160 | if p.count != nil { | |
161 | return p.count() | |
162 | } | |
163 | return len(p.m) | |
164 | } | |
165 | ||
166 | // Add adds the current execution stack to the profile, associated with value. | |
593f74bb | 167 | // Add stores value in an internal map, so value must be suitable for use as |
501699af ILT |
168 | // a map key and will not be garbage collected until the corresponding |
169 | // call to Remove. Add panics if the profile already contains a stack for value. | |
170 | // | |
171 | // The skip parameter has the same meaning as runtime.Caller's skip | |
172 | // and controls where the stack trace begins. Passing skip=0 begins the | |
173 | // trace in the function calling Add. For example, given this | |
174 | // execution stack: | |
175 | // | |
176 | // Add | |
177 | // called from rpc.NewClient | |
178 | // called from mypkg.Run | |
179 | // called from main.main | |
180 | // | |
181 | // Passing skip=0 begins the stack trace at the call to Add inside rpc.NewClient. | |
182 | // Passing skip=1 begins the stack trace at the call to NewClient inside mypkg.Run. | |
183 | // | |
184 | func (p *Profile) Add(value interface{}, skip int) { | |
185 | if p.name == "" { | |
186 | panic("pprof: use of uninitialized Profile") | |
187 | } | |
188 | if p.write != nil { | |
189 | panic("pprof: Add called on built-in Profile " + p.name) | |
190 | } | |
191 | ||
192 | stk := make([]uintptr, 32) | |
193 | n := runtime.Callers(skip+1, stk[:]) | |
194 | ||
195 | p.mu.Lock() | |
196 | defer p.mu.Unlock() | |
197 | if p.m[value] != nil { | |
198 | panic("pprof: Profile.Add of duplicate value") | |
199 | } | |
200 | p.m[value] = stk[:n] | |
201 | } | |
202 | ||
203 | // Remove removes the execution stack associated with value from the profile. | |
204 | // It is a no-op if the value is not in the profile. | |
205 | func (p *Profile) Remove(value interface{}) { | |
206 | p.mu.Lock() | |
207 | defer p.mu.Unlock() | |
208 | delete(p.m, value) | |
209 | } | |
210 | ||
211 | // WriteTo writes a pprof-formatted snapshot of the profile to w. | |
212 | // If a write to w returns an error, WriteTo returns that error. | |
213 | // Otherwise, WriteTo returns nil. | |
214 | // | |
215 | // The debug parameter enables additional output. | |
216 | // Passing debug=0 prints only the hexadecimal addresses that pprof needs. | |
217 | // Passing debug=1 adds comments translating addresses to function names | |
218 | // and line numbers, so that a programmer can read the profile without tools. | |
219 | // | |
220 | // The predefined profiles may assign meaning to other debug values; | |
221 | // for example, when printing the "goroutine" profile, debug=2 means to | |
222 | // print the goroutine stacks in the same form that a Go program uses | |
223 | // when dying due to an unrecovered panic. | |
224 | func (p *Profile) WriteTo(w io.Writer, debug int) error { | |
225 | if p.name == "" { | |
226 | panic("pprof: use of zero Profile") | |
227 | } | |
228 | if p.write != nil { | |
229 | return p.write(w, debug) | |
230 | } | |
231 | ||
232 | // Obtain consistent snapshot under lock; then process without lock. | |
233 | var all [][]uintptr | |
234 | p.mu.Lock() | |
235 | for _, stk := range p.m { | |
236 | all = append(all, stk) | |
237 | } | |
238 | p.mu.Unlock() | |
239 | ||
240 | // Map order is non-deterministic; make output deterministic. | |
241 | sort.Sort(stackProfile(all)) | |
242 | ||
243 | return printCountProfile(w, debug, p.name, stackProfile(all)) | |
244 | } | |
245 | ||
246 | type stackProfile [][]uintptr | |
247 | ||
248 | func (x stackProfile) Len() int { return len(x) } | |
249 | func (x stackProfile) Stack(i int) []uintptr { return x[i] } | |
250 | func (x stackProfile) Swap(i, j int) { x[i], x[j] = x[j], x[i] } | |
251 | func (x stackProfile) Less(i, j int) bool { | |
252 | t, u := x[i], x[j] | |
253 | for k := 0; k < len(t) && k < len(u); k++ { | |
254 | if t[k] != u[k] { | |
255 | return t[k] < u[k] | |
256 | } | |
257 | } | |
258 | return len(t) < len(u) | |
259 | } | |
260 | ||
261 | // A countProfile is a set of stack traces to be printed as counts | |
262 | // grouped by stack trace. There are multiple implementations: | |
263 | // all that matters is that we can find out how many traces there are | |
264 | // and obtain each trace in turn. | |
265 | type countProfile interface { | |
266 | Len() int | |
267 | Stack(i int) []uintptr | |
268 | } | |
269 | ||
270 | // printCountProfile prints a countProfile at the specified debug level. | |
271 | func printCountProfile(w io.Writer, debug int, name string, p countProfile) error { | |
272 | b := bufio.NewWriter(w) | |
273 | var tw *tabwriter.Writer | |
274 | w = b | |
275 | if debug > 0 { | |
276 | tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) | |
277 | w = tw | |
278 | } | |
279 | ||
280 | fmt.Fprintf(w, "%s profile: total %d\n", name, p.Len()) | |
281 | ||
282 | // Build count of each stack. | |
283 | var buf bytes.Buffer | |
284 | key := func(stk []uintptr) string { | |
285 | buf.Reset() | |
286 | fmt.Fprintf(&buf, "@") | |
287 | for _, pc := range stk { | |
288 | fmt.Fprintf(&buf, " %#x", pc) | |
289 | } | |
290 | return buf.String() | |
291 | } | |
292 | m := map[string]int{} | |
293 | n := p.Len() | |
294 | for i := 0; i < n; i++ { | |
295 | m[key(p.Stack(i))]++ | |
296 | } | |
297 | ||
298 | // Print stacks, listing count on first occurrence of a unique stack. | |
299 | for i := 0; i < n; i++ { | |
300 | stk := p.Stack(i) | |
301 | s := key(stk) | |
302 | if count := m[s]; count != 0 { | |
303 | fmt.Fprintf(w, "%d %s\n", count, s) | |
304 | if debug > 0 { | |
305 | printStackRecord(w, stk, false) | |
306 | } | |
307 | delete(m, s) | |
308 | } | |
309 | } | |
310 | ||
311 | if tw != nil { | |
312 | tw.Flush() | |
313 | } | |
314 | return b.Flush() | |
315 | } | |
316 | ||
317 | // printStackRecord prints the function + source line information | |
318 | // for a single stack trace. | |
319 | func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) { | |
320 | show := allFrames | |
321 | for _, pc := range stk { | |
322 | f := runtime.FuncForPC(pc) | |
323 | if f == nil { | |
324 | show = true | |
325 | fmt.Fprintf(w, "#\t%#x\n", pc) | |
326 | } else { | |
327 | file, line := f.FileLine(pc) | |
328 | name := f.Name() | |
329 | // Hide runtime.goexit and any runtime functions at the beginning. | |
330 | // This is useful mainly for allocation traces. | |
331 | if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") { | |
332 | continue | |
333 | } | |
334 | show = true | |
335 | fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", pc, f.Name(), pc-f.Entry(), file, line) | |
336 | } | |
337 | } | |
338 | if !show { | |
339 | // We didn't print anything; do it again, | |
340 | // and this time include runtime functions. | |
341 | printStackRecord(w, stk, true) | |
342 | return | |
343 | } | |
344 | fmt.Fprintf(w, "\n") | |
345 | } | |
346 | ||
347 | // Interface to system profiles. | |
348 | ||
349 | type byInUseBytes []runtime.MemProfileRecord | |
350 | ||
351 | func (x byInUseBytes) Len() int { return len(x) } | |
352 | func (x byInUseBytes) Swap(i, j int) { x[i], x[j] = x[j], x[i] } | |
353 | func (x byInUseBytes) Less(i, j int) bool { return x[i].InUseBytes() > x[j].InUseBytes() } | |
354 | ||
355 | // WriteHeapProfile is shorthand for Lookup("heap").WriteTo(w, 0). | |
356 | // It is preserved for backwards compatibility. | |
2fd401c8 | 357 | func WriteHeapProfile(w io.Writer) error { |
501699af ILT |
358 | return writeHeap(w, 0) |
359 | } | |
360 | ||
361 | // countHeap returns the number of records in the heap profile. | |
362 | func countHeap() int { | |
4ccad563 | 363 | n, _ := runtime.MemProfile(nil, true) |
501699af ILT |
364 | return n |
365 | } | |
366 | ||
bd2e46c8 | 367 | // writeHeap writes the current runtime heap profile to w. |
501699af | 368 | func writeHeap(w io.Writer, debug int) error { |
4ccad563 | 369 | // Find out how many records there are (MemProfile(nil, true)), |
7a938933 ILT |
370 | // allocate that many records, and get the data. |
371 | // There's a race—more records might be added between | |
372 | // the two calls—so allocate a few extra records for safety | |
373 | // and also try again if we're very unlucky. | |
374 | // The loop should only execute one iteration in the common case. | |
375 | var p []runtime.MemProfileRecord | |
4ccad563 | 376 | n, ok := runtime.MemProfile(nil, true) |
7a938933 ILT |
377 | for { |
378 | // Allocate room for a slightly bigger profile, | |
379 | // in case a few more entries have been added | |
380 | // since the call to MemProfile. | |
381 | p = make([]runtime.MemProfileRecord, n+50) | |
4ccad563 | 382 | n, ok = runtime.MemProfile(p, true) |
7a938933 ILT |
383 | if ok { |
384 | p = p[0:n] | |
385 | break | |
386 | } | |
387 | // Profile grew; try again. | |
388 | } | |
389 | ||
501699af ILT |
390 | sort.Sort(byInUseBytes(p)) |
391 | ||
392 | b := bufio.NewWriter(w) | |
393 | var tw *tabwriter.Writer | |
394 | w = b | |
395 | if debug > 0 { | |
396 | tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) | |
397 | w = tw | |
398 | } | |
399 | ||
7a938933 ILT |
400 | var total runtime.MemProfileRecord |
401 | for i := range p { | |
402 | r := &p[i] | |
403 | total.AllocBytes += r.AllocBytes | |
404 | total.AllocObjects += r.AllocObjects | |
405 | total.FreeBytes += r.FreeBytes | |
406 | total.FreeObjects += r.FreeObjects | |
407 | } | |
408 | ||
409 | // Technically the rate is MemProfileRate not 2*MemProfileRate, | |
410 | // but early versions of the C++ heap profiler reported 2*MemProfileRate, | |
411 | // so that's what pprof has come to expect. | |
501699af | 412 | fmt.Fprintf(w, "heap profile: %d: %d [%d: %d] @ heap/%d\n", |
7a938933 ILT |
413 | total.InUseObjects(), total.InUseBytes(), |
414 | total.AllocObjects, total.AllocBytes, | |
415 | 2*runtime.MemProfileRate) | |
416 | ||
417 | for i := range p { | |
418 | r := &p[i] | |
501699af | 419 | fmt.Fprintf(w, "%d: %d [%d: %d] @", |
7a938933 ILT |
420 | r.InUseObjects(), r.InUseBytes(), |
421 | r.AllocObjects, r.AllocBytes) | |
422 | for _, pc := range r.Stack() { | |
501699af ILT |
423 | fmt.Fprintf(w, " %#x", pc) |
424 | } | |
425 | fmt.Fprintf(w, "\n") | |
426 | if debug > 0 { | |
427 | printStackRecord(w, r.Stack(), false) | |
7a938933 | 428 | } |
7a938933 ILT |
429 | } |
430 | ||
431 | // Print memstats information too. | |
501699af ILT |
432 | // Pprof will ignore, but useful for people |
433 | if debug > 0 { | |
434 | s := new(runtime.MemStats) | |
435 | runtime.ReadMemStats(s) | |
436 | fmt.Fprintf(w, "\n# runtime.MemStats\n") | |
437 | fmt.Fprintf(w, "# Alloc = %d\n", s.Alloc) | |
438 | fmt.Fprintf(w, "# TotalAlloc = %d\n", s.TotalAlloc) | |
439 | fmt.Fprintf(w, "# Sys = %d\n", s.Sys) | |
440 | fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups) | |
441 | fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs) | |
4ccad563 | 442 | fmt.Fprintf(w, "# Frees = %d\n", s.Frees) |
501699af ILT |
443 | |
444 | fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc) | |
445 | fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys) | |
446 | fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle) | |
447 | fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse) | |
4ccad563 ILT |
448 | fmt.Fprintf(w, "# HeapReleased = %d\n", s.HeapReleased) |
449 | fmt.Fprintf(w, "# HeapObjects = %d\n", s.HeapObjects) | |
501699af ILT |
450 | |
451 | fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys) | |
452 | fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys) | |
453 | fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys) | |
454 | fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys) | |
455 | ||
456 | fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC) | |
457 | fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs) | |
458 | fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC) | |
459 | fmt.Fprintf(w, "# EnableGC = %v\n", s.EnableGC) | |
460 | fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC) | |
461 | } | |
462 | ||
463 | if tw != nil { | |
464 | tw.Flush() | |
7a938933 ILT |
465 | } |
466 | return b.Flush() | |
467 | } | |
8039ca76 | 468 | |
501699af ILT |
469 | // countThreadCreate returns the size of the current ThreadCreateProfile. |
470 | func countThreadCreate() int { | |
471 | n, _ := runtime.ThreadCreateProfile(nil) | |
472 | return n | |
473 | } | |
474 | ||
475 | // writeThreadCreate writes the current runtime ThreadCreateProfile to w. | |
476 | func writeThreadCreate(w io.Writer, debug int) error { | |
477 | return writeRuntimeProfile(w, debug, "threadcreate", runtime.ThreadCreateProfile) | |
478 | } | |
479 | ||
480 | // countGoroutine returns the number of goroutines. | |
481 | func countGoroutine() int { | |
482 | return runtime.NumGoroutine() | |
483 | } | |
484 | ||
485 | // writeGoroutine writes the current runtime GoroutineProfile to w. | |
486 | func writeGoroutine(w io.Writer, debug int) error { | |
487 | if debug >= 2 { | |
488 | return writeGoroutineStacks(w) | |
489 | } | |
490 | return writeRuntimeProfile(w, debug, "goroutine", runtime.GoroutineProfile) | |
491 | } | |
492 | ||
493 | func writeGoroutineStacks(w io.Writer) error { | |
494 | // We don't know how big the buffer needs to be to collect | |
495 | // all the goroutines. Start with 1 MB and try a few times, doubling each time. | |
496 | // Give up and use a truncated trace if 64 MB is not enough. | |
497 | buf := make([]byte, 1<<20) | |
498 | for i := 0; ; i++ { | |
499 | n := runtime.Stack(buf, true) | |
500 | if n < len(buf) { | |
501 | buf = buf[:n] | |
502 | break | |
503 | } | |
504 | if len(buf) >= 64<<20 { | |
505 | // Filled 64 MB - stop there. | |
506 | break | |
507 | } | |
508 | buf = make([]byte, 2*len(buf)) | |
509 | } | |
510 | _, err := w.Write(buf) | |
511 | return err | |
512 | } | |
513 | ||
514 | func writeRuntimeProfile(w io.Writer, debug int, name string, fetch func([]runtime.StackRecord) (int, bool)) error { | |
515 | // Find out how many records there are (fetch(nil)), | |
cbb6491d | 516 | // allocate that many records, and get the data. |
501699af | 517 | // There's a race—more records might be added between |
cbb6491d ILT |
518 | // the two calls—so allocate a few extra records for safety |
519 | // and also try again if we're very unlucky. | |
520 | // The loop should only execute one iteration in the common case. | |
501699af ILT |
521 | var p []runtime.StackRecord |
522 | n, ok := fetch(nil) | |
cbb6491d ILT |
523 | for { |
524 | // Allocate room for a slightly bigger profile, | |
525 | // in case a few more entries have been added | |
526 | // since the call to ThreadProfile. | |
501699af ILT |
527 | p = make([]runtime.StackRecord, n+10) |
528 | n, ok = fetch(p) | |
cbb6491d ILT |
529 | if ok { |
530 | p = p[0:n] | |
531 | break | |
532 | } | |
533 | // Profile grew; try again. | |
534 | } | |
535 | ||
501699af | 536 | return printCountProfile(w, debug, name, runtimeProfile(p)) |
cbb6491d ILT |
537 | } |
538 | ||
501699af ILT |
539 | type runtimeProfile []runtime.StackRecord |
540 | ||
541 | func (p runtimeProfile) Len() int { return len(p) } | |
542 | func (p runtimeProfile) Stack(i int) []uintptr { return p[i].Stack() } | |
543 | ||
8039ca76 ILT |
544 | var cpu struct { |
545 | sync.Mutex | |
546 | profiling bool | |
547 | done chan bool | |
548 | } | |
549 | ||
550 | // StartCPUProfile enables CPU profiling for the current process. | |
551 | // While profiling, the profile will be buffered and written to w. | |
552 | // StartCPUProfile returns an error if profiling is already enabled. | |
2fd401c8 | 553 | func StartCPUProfile(w io.Writer) error { |
8039ca76 ILT |
554 | // The runtime routines allow a variable profiling rate, |
555 | // but in practice operating systems cannot trigger signals | |
556 | // at more than about 500 Hz, and our processing of the | |
557 | // signal is not cheap (mostly getting the stack trace). | |
558 | // 100 Hz is a reasonable choice: it is frequent enough to | |
559 | // produce useful data, rare enough not to bog down the | |
560 | // system, and a nice round number to make it easy to | |
561 | // convert sample counts to seconds. Instead of requiring | |
562 | // each client to specify the frequency, we hard code it. | |
563 | const hz = 100 | |
564 | ||
565 | // Avoid queueing behind StopCPUProfile. | |
566 | // Could use TryLock instead if we had it. | |
567 | if cpu.profiling { | |
568 | return fmt.Errorf("cpu profiling already in use") | |
569 | } | |
570 | ||
571 | cpu.Lock() | |
572 | defer cpu.Unlock() | |
573 | if cpu.done == nil { | |
574 | cpu.done = make(chan bool) | |
575 | } | |
576 | // Double-check. | |
577 | if cpu.profiling { | |
578 | return fmt.Errorf("cpu profiling already in use") | |
579 | } | |
580 | cpu.profiling = true | |
581 | runtime.SetCPUProfileRate(hz) | |
582 | go profileWriter(w) | |
583 | return nil | |
584 | } | |
585 | ||
586 | func profileWriter(w io.Writer) { | |
587 | for { | |
588 | data := runtime.CPUProfile() | |
589 | if data == nil { | |
590 | break | |
591 | } | |
592 | w.Write(data) | |
593 | } | |
594 | cpu.done <- true | |
595 | } | |
596 | ||
597 | // StopCPUProfile stops the current CPU profile, if any. | |
598 | // StopCPUProfile only returns after all the writes for the | |
599 | // profile have completed. | |
600 | func StopCPUProfile() { | |
601 | cpu.Lock() | |
602 | defer cpu.Unlock() | |
603 | ||
604 | if !cpu.profiling { | |
605 | return | |
606 | } | |
607 | cpu.profiling = false | |
608 | runtime.SetCPUProfileRate(0) | |
609 | <-cpu.done | |
610 | } | |
4ccad563 ILT |
611 | |
612 | type byCycles []runtime.BlockProfileRecord | |
613 | ||
614 | func (x byCycles) Len() int { return len(x) } | |
615 | func (x byCycles) Swap(i, j int) { x[i], x[j] = x[j], x[i] } | |
616 | func (x byCycles) Less(i, j int) bool { return x[i].Cycles > x[j].Cycles } | |
617 | ||
618 | // countBlock returns the number of records in the blocking profile. | |
619 | func countBlock() int { | |
620 | n, _ := runtime.BlockProfile(nil) | |
621 | return n | |
622 | } | |
623 | ||
624 | // writeBlock writes the current blocking profile to w. | |
625 | func writeBlock(w io.Writer, debug int) error { | |
626 | var p []runtime.BlockProfileRecord | |
627 | n, ok := runtime.BlockProfile(nil) | |
628 | for { | |
629 | p = make([]runtime.BlockProfileRecord, n+50) | |
630 | n, ok = runtime.BlockProfile(p) | |
631 | if ok { | |
632 | p = p[:n] | |
633 | break | |
634 | } | |
635 | } | |
636 | ||
637 | sort.Sort(byCycles(p)) | |
638 | ||
639 | b := bufio.NewWriter(w) | |
640 | var tw *tabwriter.Writer | |
641 | w = b | |
642 | if debug > 0 { | |
643 | tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) | |
644 | w = tw | |
645 | } | |
646 | ||
647 | fmt.Fprintf(w, "--- contention:\n") | |
648 | fmt.Fprintf(w, "cycles/second=%v\n", runtime_cyclesPerSecond()) | |
649 | for i := range p { | |
650 | r := &p[i] | |
651 | fmt.Fprintf(w, "%v %v @", r.Cycles, r.Count) | |
652 | for _, pc := range r.Stack() { | |
653 | fmt.Fprintf(w, " %#x", pc) | |
654 | } | |
655 | fmt.Fprint(w, "\n") | |
656 | if debug > 0 { | |
657 | printStackRecord(w, r.Stack(), false) | |
658 | } | |
659 | } | |
660 | ||
661 | if tw != nil { | |
662 | tw.Flush() | |
663 | } | |
664 | return b.Flush() | |
665 | } | |
666 | ||
667 | func runtime_cyclesPerSecond() int64 |