...

Source file src/runtime/cpuprof.go

     1	// Copyright 2011 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	// CPU profiling.
     6	//
     7	// The signal handler for the profiling clock tick adds a new stack trace
     8	// to a log of recent traces. The log is read by a user goroutine that
     9	// turns it into formatted profile data. If the reader does not keep up
    10	// with the log, those writes will be recorded as a count of lost records.
    11	// The actual profile buffer is in profbuf.go.
    12	
    13	package runtime
    14	
    15	import (
    16		"runtime/internal/atomic"
    17		"runtime/internal/sys"
    18		"unsafe"
    19	)
    20	
    21	const maxCPUProfStack = 64
    22	
    23	type cpuProfile struct {
    24		lock mutex
    25		on   bool     // profiling is on
    26		log  *profBuf // profile events written here
    27	
    28		// extra holds extra stacks accumulated in addNonGo
    29		// corresponding to profiling signals arriving on
    30		// non-Go-created threads. Those stacks are written
    31		// to log the next time a normal Go thread gets the
    32		// signal handler.
    33		// Assuming the stacks are 2 words each (we don't get
    34		// a full traceback from those threads), plus one word
    35		// size for framing, 100 Hz profiling would generate
    36		// 300 words per second.
    37		// Hopefully a normal Go thread will get the profiling
    38		// signal at least once every few seconds.
    39		extra      [1000]uintptr
    40		numExtra   int
    41		lostExtra  uint64 // count of frames lost because extra is full
    42		lostAtomic uint64 // count of frames lost because of being in atomic64 on mips/arm; updated racily
    43	}
    44	
    45	var cpuprof cpuProfile
    46	
    47	// SetCPUProfileRate sets the CPU profiling rate to hz samples per second.
    48	// If hz <= 0, SetCPUProfileRate turns off profiling.
    49	// If the profiler is on, the rate cannot be changed without first turning it off.
    50	//
    51	// Most clients should use the runtime/pprof package or
    52	// the testing package's -test.cpuprofile flag instead of calling
    53	// SetCPUProfileRate directly.
    54	func SetCPUProfileRate(hz int) {
    55		// Clamp hz to something reasonable.
    56		if hz < 0 {
    57			hz = 0
    58		}
    59		if hz > 1000000 {
    60			hz = 1000000
    61		}
    62	
    63		lock(&cpuprof.lock)
    64		if hz > 0 {
    65			if cpuprof.on || cpuprof.log != nil {
    66				print("runtime: cannot set cpu profile rate until previous profile has finished.\n")
    67				unlock(&cpuprof.lock)
    68				return
    69			}
    70	
    71			cpuprof.on = true
    72			cpuprof.log = newProfBuf(1, 1<<17, 1<<14)
    73			hdr := [1]uint64{uint64(hz)}
    74			cpuprof.log.write(nil, nanotime(), hdr[:], nil)
    75			setcpuprofilerate(int32(hz))
    76		} else if cpuprof.on {
    77			setcpuprofilerate(0)
    78			cpuprof.on = false
    79			cpuprof.addExtra()
    80			cpuprof.log.close()
    81		}
    82		unlock(&cpuprof.lock)
    83	}
    84	
    85	// add adds the stack trace to the profile.
    86	// It is called from signal handlers and other limited environments
    87	// and cannot allocate memory or acquire locks that might be
    88	// held at the time of the signal, nor can it use substantial amounts
    89	// of stack.
    90	//go:nowritebarrierrec
    91	func (p *cpuProfile) add(gp *g, stk []uintptr) {
    92		// Simple cas-lock to coordinate with setcpuprofilerate.
    93		for !atomic.Cas(&prof.signalLock, 0, 1) {
    94			osyield()
    95		}
    96	
    97		if prof.hz != 0 { // implies cpuprof.log != nil
    98			if p.numExtra > 0 || p.lostExtra > 0 || p.lostAtomic > 0 {
    99				p.addExtra()
   100			}
   101			hdr := [1]uint64{1}
   102			// Note: write "knows" that the argument is &gp.labels,
   103			// because otherwise its write barrier behavior may not
   104			// be correct. See the long comment there before
   105			// changing the argument here.
   106			cpuprof.log.write(&gp.labels, nanotime(), hdr[:], stk)
   107		}
   108	
   109		atomic.Store(&prof.signalLock, 0)
   110	}
   111	
   112	// addNonGo adds the non-Go stack trace to the profile.
   113	// It is called from a non-Go thread, so we cannot use much stack at all,
   114	// nor do anything that needs a g or an m.
   115	// In particular, we can't call cpuprof.log.write.
   116	// Instead, we copy the stack into cpuprof.extra,
   117	// which will be drained the next time a Go thread
   118	// gets the signal handling event.
   119	//go:nosplit
   120	//go:nowritebarrierrec
   121	func (p *cpuProfile) addNonGo(stk []uintptr) {
   122		// Simple cas-lock to coordinate with SetCPUProfileRate.
   123		// (Other calls to add or addNonGo should be blocked out
   124		// by the fact that only one SIGPROF can be handled by the
   125		// process at a time. If not, this lock will serialize those too.)
   126		for !atomic.Cas(&prof.signalLock, 0, 1) {
   127			osyield()
   128		}
   129	
   130		if cpuprof.numExtra+1+len(stk) < len(cpuprof.extra) {
   131			i := cpuprof.numExtra
   132			cpuprof.extra[i] = uintptr(1 + len(stk))
   133			copy(cpuprof.extra[i+1:], stk)
   134			cpuprof.numExtra += 1 + len(stk)
   135		} else {
   136			cpuprof.lostExtra++
   137		}
   138	
   139		atomic.Store(&prof.signalLock, 0)
   140	}
   141	
   142	// addExtra adds the "extra" profiling events,
   143	// queued by addNonGo, to the profile log.
   144	// addExtra is called either from a signal handler on a Go thread
   145	// or from an ordinary goroutine; either way it can use stack
   146	// and has a g. The world may be stopped, though.
   147	func (p *cpuProfile) addExtra() {
   148		// Copy accumulated non-Go profile events.
   149		hdr := [1]uint64{1}
   150		for i := 0; i < p.numExtra; {
   151			p.log.write(nil, 0, hdr[:], p.extra[i+1:i+int(p.extra[i])])
   152			i += int(p.extra[i])
   153		}
   154		p.numExtra = 0
   155	
   156		// Report any lost events.
   157		if p.lostExtra > 0 {
   158			hdr := [1]uint64{p.lostExtra}
   159			lostStk := [2]uintptr{
   160				funcPC(_LostExternalCode) + sys.PCQuantum,
   161				funcPC(_ExternalCode) + sys.PCQuantum,
   162			}
   163			p.log.write(nil, 0, hdr[:], lostStk[:])
   164			p.lostExtra = 0
   165		}
   166	
   167		if p.lostAtomic > 0 {
   168			hdr := [1]uint64{p.lostAtomic}
   169			lostStk := [2]uintptr{
   170				funcPC(_LostSIGPROFDuringAtomic64) + sys.PCQuantum,
   171				funcPC(_System) + sys.PCQuantum,
   172			}
   173			p.log.write(nil, 0, hdr[:], lostStk[:])
   174			p.lostAtomic = 0
   175		}
   176	
   177	}
   178	
   179	// CPUProfile panics.
   180	// It formerly provided raw access to chunks of
   181	// a pprof-format profile generated by the runtime.
   182	// The details of generating that format have changed,
   183	// so this functionality has been removed.
   184	//
   185	// Deprecated: Use the runtime/pprof package,
   186	// or the handlers in the net/http/pprof package,
   187	// or the testing package's -test.cpuprofile flag instead.
   188	func CPUProfile() []byte {
   189		panic("CPUProfile no longer available")
   190	}
   191	
   192	//go:linkname runtime_pprof_runtime_cyclesPerSecond runtime/pprof.runtime_cyclesPerSecond
   193	func runtime_pprof_runtime_cyclesPerSecond() int64 {
   194		return tickspersecond()
   195	}
   196	
   197	// readProfile, provided to runtime/pprof, returns the next chunk of
   198	// binary CPU profiling stack trace data, blocking until data is available.
   199	// If profiling is turned off and all the profile data accumulated while it was
   200	// on has been returned, readProfile returns eof=true.
   201	// The caller must save the returned data and tags before calling readProfile again.
   202	//
   203	//go:linkname runtime_pprof_readProfile runtime/pprof.readProfile
   204	func runtime_pprof_readProfile() ([]uint64, []unsafe.Pointer, bool) {
   205		lock(&cpuprof.lock)
   206		log := cpuprof.log
   207		unlock(&cpuprof.lock)
   208		data, tags, eof := log.read(profBufBlocking)
   209		if len(data) == 0 && eof {
   210			lock(&cpuprof.lock)
   211			cpuprof.log = nil
   212			unlock(&cpuprof.lock)
   213		}
   214		return data, tags, eof
   215	}
   216	

View as plain text