xref: /aosp_15_r20/external/starlark-go/starlark/profile.go (revision 4947cdc739c985f6d86941e22894f5cefe7c9e9a)
1*4947cdc7SCole Faust// Copyright 2019 The Bazel Authors. All rights reserved.
2*4947cdc7SCole Faust// Use of this source code is governed by a BSD-style
3*4947cdc7SCole Faust// license that can be found in the LICENSE file.
4*4947cdc7SCole Faust
5*4947cdc7SCole Faustpackage starlark
6*4947cdc7SCole Faust
7*4947cdc7SCole Faust// This file defines a simple execution-time profiler for Starlark.
8*4947cdc7SCole Faust// It measures the wall time spent executing Starlark code, and emits a
9*4947cdc7SCole Faust// gzipped protocol message in pprof format (github.com/google/pprof).
10*4947cdc7SCole Faust//
11*4947cdc7SCole Faust// When profiling is enabled, the interpreter calls the profiler to
12*4947cdc7SCole Faust// indicate the start and end of each "span" or time interval. A leaf
13*4947cdc7SCole Faust// function (whether Go or Starlark) has a single span. A function that
14*4947cdc7SCole Faust// calls another function has spans for each interval in which it is the
15*4947cdc7SCole Faust// top of the stack. (A LOAD instruction also ends a span.)
16*4947cdc7SCole Faust//
17*4947cdc7SCole Faust// At the start of a span, the interpreter records the current time in
18*4947cdc7SCole Faust// the thread's topmost frame. At the end of the span, it obtains the
19*4947cdc7SCole Faust// time again and subtracts the span start time. The difference is added
20*4947cdc7SCole Faust// to an accumulator variable in the thread. If the accumulator exceeds
21*4947cdc7SCole Faust// some fixed quantum (10ms, say), the profiler records the current call
22*4947cdc7SCole Faust// stack and sends it to the profiler goroutine, along with the number
23*4947cdc7SCole Faust// of quanta, which are subtracted. For example, if the accumulator
24*4947cdc7SCole Faust// holds 3ms and then a completed span adds 25ms to it, its value is 28ms,
25*4947cdc7SCole Faust// which exceeeds 10ms. The profiler records a stack with the value 20ms
26*4947cdc7SCole Faust// (2 quanta), and the accumulator is left with 8ms.
27*4947cdc7SCole Faust//
28*4947cdc7SCole Faust// The profiler goroutine converts the stacks into the pprof format and
29*4947cdc7SCole Faust// emits a gzip-compressed protocol message to the designated output
30*4947cdc7SCole Faust// file. We use a hand-written streaming proto encoder to avoid
31*4947cdc7SCole Faust// dependencies on pprof and proto, and to avoid the need to
32*4947cdc7SCole Faust// materialize the profile data structure in memory.
33*4947cdc7SCole Faust//
34*4947cdc7SCole Faust// A limitation of this profiler is that it measures wall time, which
35*4947cdc7SCole Faust// does not necessarily correspond to CPU time. A CPU profiler requires
36*4947cdc7SCole Faust// that only running (not runnable) threads are sampled; this is
37*4947cdc7SCole Faust// commonly achieved by having the kernel deliver a (PROF) signal to an
38*4947cdc7SCole Faust// arbitrary running thread, through setitimer(2). The CPU profiler in the
39*4947cdc7SCole Faust// Go runtime uses this mechanism, but it is not possible for a Go
40*4947cdc7SCole Faust// application to register a SIGPROF handler, nor is it possible for a
41*4947cdc7SCole Faust// Go handler for some other signal to read the stack pointer of
42*4947cdc7SCole Faust// the interrupted thread.
43*4947cdc7SCole Faust//
44*4947cdc7SCole Faust// Two caveats:
45*4947cdc7SCole Faust// (1) it is tempting to send the leaf Frame directly to the profiler
46*4947cdc7SCole Faust// goroutine instead of making a copy of the stack, since a Frame is a
47*4947cdc7SCole Faust// spaghetti stack--a linked list. However, as soon as execution
48*4947cdc7SCole Faust// resumes, the stack's Frame.pc values may be mutated, so Frames are
49*4947cdc7SCole Faust// not safe to share with the asynchronous profiler goroutine.
50*4947cdc7SCole Faust// (2) it is tempting to use Callables as keys in a map when tabulating
51*4947cdc7SCole Faust// the pprof protocols's Function entities. However, we cannot assume
52*4947cdc7SCole Faust// that Callables are valid map keys, and furthermore we must not
53*4947cdc7SCole Faust// pin function values in memory indefinitely as this may cause lambda
54*4947cdc7SCole Faust// values to keep their free variables live much longer than necessary.
55*4947cdc7SCole Faust
56*4947cdc7SCole Faust// TODO(adonovan):
57*4947cdc7SCole Faust// - make Start/Stop fully thread-safe.
58*4947cdc7SCole Faust// - fix the pc hack.
59*4947cdc7SCole Faust// - experiment with other values of quantum.
60*4947cdc7SCole Faust
61*4947cdc7SCole Faustimport (
62*4947cdc7SCole Faust	"bufio"
63*4947cdc7SCole Faust	"bytes"
64*4947cdc7SCole Faust	"compress/gzip"
65*4947cdc7SCole Faust	"encoding/binary"
66*4947cdc7SCole Faust	"fmt"
67*4947cdc7SCole Faust	"io"
68*4947cdc7SCole Faust	"log"
69*4947cdc7SCole Faust	"reflect"
70*4947cdc7SCole Faust	"sync/atomic"
71*4947cdc7SCole Faust	"time"
72*4947cdc7SCole Faust	"unsafe"
73*4947cdc7SCole Faust
74*4947cdc7SCole Faust	"go.starlark.net/syntax"
75*4947cdc7SCole Faust)
76*4947cdc7SCole Faust
77*4947cdc7SCole Faust// StartProfile enables time profiling of all Starlark threads,
78*4947cdc7SCole Faust// and writes a profile in pprof format to w.
79*4947cdc7SCole Faust// It must be followed by a call to StopProfiler to stop
80*4947cdc7SCole Faust// the profiler and finalize the profile.
81*4947cdc7SCole Faust//
82*4947cdc7SCole Faust// StartProfile returns an error if profiling was already enabled.
83*4947cdc7SCole Faust//
84*4947cdc7SCole Faust// StartProfile must not be called concurrently with Starlark execution.
85*4947cdc7SCole Faustfunc StartProfile(w io.Writer) error {
86*4947cdc7SCole Faust	if !atomic.CompareAndSwapUint32(&profiler.on, 0, 1) {
87*4947cdc7SCole Faust		return fmt.Errorf("profiler already running")
88*4947cdc7SCole Faust	}
89*4947cdc7SCole Faust
90*4947cdc7SCole Faust	// TODO(adonovan): make the API fully concurrency-safe.
91*4947cdc7SCole Faust	// The main challenge is racy reads/writes of profiler.events,
92*4947cdc7SCole Faust	// and of send/close races on the channel it refers to.
93*4947cdc7SCole Faust	// It's easy to solve them with a mutex but harder to do
94*4947cdc7SCole Faust	// it efficiently.
95*4947cdc7SCole Faust
96*4947cdc7SCole Faust	profiler.events = make(chan *profEvent, 1)
97*4947cdc7SCole Faust	profiler.done = make(chan error)
98*4947cdc7SCole Faust
99*4947cdc7SCole Faust	go profile(w)
100*4947cdc7SCole Faust
101*4947cdc7SCole Faust	return nil
102*4947cdc7SCole Faust}
103*4947cdc7SCole Faust
104*4947cdc7SCole Faust// StopProfiler stops the profiler started by a prior call to
105*4947cdc7SCole Faust// StartProfile and finalizes the profile. It returns an error if the
106*4947cdc7SCole Faust// profile could not be completed.
107*4947cdc7SCole Faust//
108*4947cdc7SCole Faust// StopProfiler must not be called concurrently with Starlark execution.
109*4947cdc7SCole Faustfunc StopProfile() error {
110*4947cdc7SCole Faust	// Terminate the profiler goroutine and get its result.
111*4947cdc7SCole Faust	close(profiler.events)
112*4947cdc7SCole Faust	err := <-profiler.done
113*4947cdc7SCole Faust
114*4947cdc7SCole Faust	profiler.done = nil
115*4947cdc7SCole Faust	profiler.events = nil
116*4947cdc7SCole Faust	atomic.StoreUint32(&profiler.on, 0)
117*4947cdc7SCole Faust
118*4947cdc7SCole Faust	return err
119*4947cdc7SCole Faust}
120*4947cdc7SCole Faust
121*4947cdc7SCole Faust// globals
122*4947cdc7SCole Faustvar profiler struct {
123*4947cdc7SCole Faust	on     uint32          // nonzero => profiler running
124*4947cdc7SCole Faust	events chan *profEvent // profile events from interpreter threads
125*4947cdc7SCole Faust	done   chan error      // indicates profiler goroutine is ready
126*4947cdc7SCole Faust}
127*4947cdc7SCole Faust
128*4947cdc7SCole Faustfunc (thread *Thread) beginProfSpan() {
129*4947cdc7SCole Faust	if profiler.events == nil {
130*4947cdc7SCole Faust		return // profiling not enabled
131*4947cdc7SCole Faust	}
132*4947cdc7SCole Faust
133*4947cdc7SCole Faust	thread.frameAt(0).spanStart = nanotime()
134*4947cdc7SCole Faust}
135*4947cdc7SCole Faust
136*4947cdc7SCole Faust// TODO(adonovan): experiment with smaller values,
137*4947cdc7SCole Faust// which trade space and time for greater precision.
138*4947cdc7SCole Faustconst quantum = 10 * time.Millisecond
139*4947cdc7SCole Faust
140*4947cdc7SCole Faustfunc (thread *Thread) endProfSpan() {
141*4947cdc7SCole Faust	if profiler.events == nil {
142*4947cdc7SCole Faust		return // profiling not enabled
143*4947cdc7SCole Faust	}
144*4947cdc7SCole Faust
145*4947cdc7SCole Faust	// Add the span to the thread's accumulator.
146*4947cdc7SCole Faust	thread.proftime += time.Duration(nanotime() - thread.frameAt(0).spanStart)
147*4947cdc7SCole Faust	if thread.proftime < quantum {
148*4947cdc7SCole Faust		return
149*4947cdc7SCole Faust	}
150*4947cdc7SCole Faust
151*4947cdc7SCole Faust	// Only record complete quanta.
152*4947cdc7SCole Faust	n := thread.proftime / quantum
153*4947cdc7SCole Faust	thread.proftime -= n * quantum
154*4947cdc7SCole Faust
155*4947cdc7SCole Faust	// Copy the stack.
156*4947cdc7SCole Faust	// (We can't save thread.frame because its pc will change.)
157*4947cdc7SCole Faust	ev := &profEvent{
158*4947cdc7SCole Faust		thread: thread,
159*4947cdc7SCole Faust		time:   n * quantum,
160*4947cdc7SCole Faust	}
161*4947cdc7SCole Faust	ev.stack = ev.stackSpace[:0]
162*4947cdc7SCole Faust	for i := range thread.stack {
163*4947cdc7SCole Faust		fr := thread.frameAt(i)
164*4947cdc7SCole Faust		ev.stack = append(ev.stack, profFrame{
165*4947cdc7SCole Faust			pos: fr.Position(),
166*4947cdc7SCole Faust			fn:  fr.Callable(),
167*4947cdc7SCole Faust			pc:  fr.pc,
168*4947cdc7SCole Faust		})
169*4947cdc7SCole Faust	}
170*4947cdc7SCole Faust
171*4947cdc7SCole Faust	profiler.events <- ev
172*4947cdc7SCole Faust}
173*4947cdc7SCole Faust
174*4947cdc7SCole Fausttype profEvent struct {
175*4947cdc7SCole Faust	thread     *Thread // currently unused
176*4947cdc7SCole Faust	time       time.Duration
177*4947cdc7SCole Faust	stack      []profFrame
178*4947cdc7SCole Faust	stackSpace [8]profFrame // initial space for stack
179*4947cdc7SCole Faust}
180*4947cdc7SCole Faust
181*4947cdc7SCole Fausttype profFrame struct {
182*4947cdc7SCole Faust	fn  Callable        // don't hold this live for too long (prevents GC of lambdas)
183*4947cdc7SCole Faust	pc  uint32          // program counter (Starlark frames only)
184*4947cdc7SCole Faust	pos syntax.Position // position of pc within this frame
185*4947cdc7SCole Faust}
186*4947cdc7SCole Faust
187*4947cdc7SCole Faust// profile is the profiler goroutine.
188*4947cdc7SCole Faust// It runs until StopProfiler is called.
189*4947cdc7SCole Faustfunc profile(w io.Writer) {
190*4947cdc7SCole Faust	// Field numbers from pprof protocol.
191*4947cdc7SCole Faust	// See https://github.com/google/pprof/blob/master/proto/profile.proto
192*4947cdc7SCole Faust	const (
193*4947cdc7SCole Faust		Profile_sample_type    = 1  // repeated ValueType
194*4947cdc7SCole Faust		Profile_sample         = 2  // repeated Sample
195*4947cdc7SCole Faust		Profile_mapping        = 3  // repeated Mapping
196*4947cdc7SCole Faust		Profile_location       = 4  // repeated Location
197*4947cdc7SCole Faust		Profile_function       = 5  // repeated Function
198*4947cdc7SCole Faust		Profile_string_table   = 6  // repeated string
199*4947cdc7SCole Faust		Profile_time_nanos     = 9  // int64
200*4947cdc7SCole Faust		Profile_duration_nanos = 10 // int64
201*4947cdc7SCole Faust		Profile_period_type    = 11 // ValueType
202*4947cdc7SCole Faust		Profile_period         = 12 // int64
203*4947cdc7SCole Faust
204*4947cdc7SCole Faust		ValueType_type = 1 // int64
205*4947cdc7SCole Faust		ValueType_unit = 2 // int64
206*4947cdc7SCole Faust
207*4947cdc7SCole Faust		Sample_location_id = 1 // repeated uint64
208*4947cdc7SCole Faust		Sample_value       = 2 // repeated int64
209*4947cdc7SCole Faust		Sample_label       = 3 // repeated Label
210*4947cdc7SCole Faust
211*4947cdc7SCole Faust		Label_key      = 1 // int64
212*4947cdc7SCole Faust		Label_str      = 2 // int64
213*4947cdc7SCole Faust		Label_num      = 3 // int64
214*4947cdc7SCole Faust		Label_num_unit = 4 // int64
215*4947cdc7SCole Faust
216*4947cdc7SCole Faust		Location_id         = 1 // uint64
217*4947cdc7SCole Faust		Location_mapping_id = 2 // uint64
218*4947cdc7SCole Faust		Location_address    = 3 // uint64
219*4947cdc7SCole Faust		Location_line       = 4 // repeated Line
220*4947cdc7SCole Faust
221*4947cdc7SCole Faust		Line_function_id = 1 // uint64
222*4947cdc7SCole Faust		Line_line        = 2 // int64
223*4947cdc7SCole Faust
224*4947cdc7SCole Faust		Function_id          = 1 // uint64
225*4947cdc7SCole Faust		Function_name        = 2 // int64
226*4947cdc7SCole Faust		Function_system_name = 3 // int64
227*4947cdc7SCole Faust		Function_filename    = 4 // int64
228*4947cdc7SCole Faust		Function_start_line  = 5 // int64
229*4947cdc7SCole Faust	)
230*4947cdc7SCole Faust
231*4947cdc7SCole Faust	bufw := bufio.NewWriter(w) // write file in 4KB (not 240B flate-sized) chunks
232*4947cdc7SCole Faust	gz := gzip.NewWriter(bufw)
233*4947cdc7SCole Faust	enc := protoEncoder{w: gz}
234*4947cdc7SCole Faust
235*4947cdc7SCole Faust	// strings
236*4947cdc7SCole Faust	stringIndex := make(map[string]int64)
237*4947cdc7SCole Faust	str := func(s string) int64 {
238*4947cdc7SCole Faust		i, ok := stringIndex[s]
239*4947cdc7SCole Faust		if !ok {
240*4947cdc7SCole Faust			i = int64(len(stringIndex))
241*4947cdc7SCole Faust			enc.string(Profile_string_table, s)
242*4947cdc7SCole Faust			stringIndex[s] = i
243*4947cdc7SCole Faust		}
244*4947cdc7SCole Faust		return i
245*4947cdc7SCole Faust	}
246*4947cdc7SCole Faust	str("") // entry 0
247*4947cdc7SCole Faust
248*4947cdc7SCole Faust	// functions
249*4947cdc7SCole Faust	//
250*4947cdc7SCole Faust	// function returns the ID of a Callable for use in Line.FunctionId.
251*4947cdc7SCole Faust	// The ID is the same as the function's logical address,
252*4947cdc7SCole Faust	// which is supplied by the caller to avoid the need to recompute it.
253*4947cdc7SCole Faust	functionId := make(map[uintptr]uint64)
254*4947cdc7SCole Faust	function := func(fn Callable, addr uintptr) uint64 {
255*4947cdc7SCole Faust		id, ok := functionId[addr]
256*4947cdc7SCole Faust		if !ok {
257*4947cdc7SCole Faust			id = uint64(addr)
258*4947cdc7SCole Faust
259*4947cdc7SCole Faust			var pos syntax.Position
260*4947cdc7SCole Faust			if fn, ok := fn.(callableWithPosition); ok {
261*4947cdc7SCole Faust				pos = fn.Position()
262*4947cdc7SCole Faust			}
263*4947cdc7SCole Faust
264*4947cdc7SCole Faust			name := fn.Name()
265*4947cdc7SCole Faust			if name == "<toplevel>" {
266*4947cdc7SCole Faust				name = pos.Filename()
267*4947cdc7SCole Faust			}
268*4947cdc7SCole Faust
269*4947cdc7SCole Faust			nameIndex := str(name)
270*4947cdc7SCole Faust
271*4947cdc7SCole Faust			fun := new(bytes.Buffer)
272*4947cdc7SCole Faust			funenc := protoEncoder{w: fun}
273*4947cdc7SCole Faust			funenc.uint(Function_id, id)
274*4947cdc7SCole Faust			funenc.int(Function_name, nameIndex)
275*4947cdc7SCole Faust			funenc.int(Function_system_name, nameIndex)
276*4947cdc7SCole Faust			funenc.int(Function_filename, str(pos.Filename()))
277*4947cdc7SCole Faust			funenc.int(Function_start_line, int64(pos.Line))
278*4947cdc7SCole Faust			enc.bytes(Profile_function, fun.Bytes())
279*4947cdc7SCole Faust
280*4947cdc7SCole Faust			functionId[addr] = id
281*4947cdc7SCole Faust		}
282*4947cdc7SCole Faust		return id
283*4947cdc7SCole Faust	}
284*4947cdc7SCole Faust
285*4947cdc7SCole Faust	// locations
286*4947cdc7SCole Faust	//
287*4947cdc7SCole Faust	// location returns the ID of the location denoted by fr.
288*4947cdc7SCole Faust	// For Starlark frames, this is the Frame pc.
289*4947cdc7SCole Faust	locationId := make(map[uintptr]uint64)
290*4947cdc7SCole Faust	location := func(fr profFrame) uint64 {
291*4947cdc7SCole Faust		fnAddr := profFuncAddr(fr.fn)
292*4947cdc7SCole Faust
293*4947cdc7SCole Faust		// For Starlark functions, the frame position
294*4947cdc7SCole Faust		// represents the current PC value.
295*4947cdc7SCole Faust		// Mix it into the low bits of the address.
296*4947cdc7SCole Faust		// This is super hacky and may result in collisions
297*4947cdc7SCole Faust		// in large functions or if functions are numerous.
298*4947cdc7SCole Faust		// TODO(adonovan): fix: try making this cleaner by treating
299*4947cdc7SCole Faust		// each bytecode segment as a Profile.Mapping.
300*4947cdc7SCole Faust		pcAddr := fnAddr
301*4947cdc7SCole Faust		if _, ok := fr.fn.(*Function); ok {
302*4947cdc7SCole Faust			pcAddr = (pcAddr << 16) ^ uintptr(fr.pc)
303*4947cdc7SCole Faust		}
304*4947cdc7SCole Faust
305*4947cdc7SCole Faust		id, ok := locationId[pcAddr]
306*4947cdc7SCole Faust		if !ok {
307*4947cdc7SCole Faust			id = uint64(pcAddr)
308*4947cdc7SCole Faust
309*4947cdc7SCole Faust			line := new(bytes.Buffer)
310*4947cdc7SCole Faust			lineenc := protoEncoder{w: line}
311*4947cdc7SCole Faust			lineenc.uint(Line_function_id, function(fr.fn, fnAddr))
312*4947cdc7SCole Faust			lineenc.int(Line_line, int64(fr.pos.Line))
313*4947cdc7SCole Faust			loc := new(bytes.Buffer)
314*4947cdc7SCole Faust			locenc := protoEncoder{w: loc}
315*4947cdc7SCole Faust			locenc.uint(Location_id, id)
316*4947cdc7SCole Faust			locenc.uint(Location_address, uint64(pcAddr))
317*4947cdc7SCole Faust			locenc.bytes(Location_line, line.Bytes())
318*4947cdc7SCole Faust			enc.bytes(Profile_location, loc.Bytes())
319*4947cdc7SCole Faust
320*4947cdc7SCole Faust			locationId[pcAddr] = id
321*4947cdc7SCole Faust		}
322*4947cdc7SCole Faust		return id
323*4947cdc7SCole Faust	}
324*4947cdc7SCole Faust
325*4947cdc7SCole Faust	wallNanos := new(bytes.Buffer)
326*4947cdc7SCole Faust	wnenc := protoEncoder{w: wallNanos}
327*4947cdc7SCole Faust	wnenc.int(ValueType_type, str("wall"))
328*4947cdc7SCole Faust	wnenc.int(ValueType_unit, str("nanoseconds"))
329*4947cdc7SCole Faust
330*4947cdc7SCole Faust	// informational fields of Profile
331*4947cdc7SCole Faust	enc.bytes(Profile_sample_type, wallNanos.Bytes())
332*4947cdc7SCole Faust	enc.int(Profile_period, quantum.Nanoseconds())     // magnitude of sampling period
333*4947cdc7SCole Faust	enc.bytes(Profile_period_type, wallNanos.Bytes())  // dimension and unit of period
334*4947cdc7SCole Faust	enc.int(Profile_time_nanos, time.Now().UnixNano()) // start (real) time of profile
335*4947cdc7SCole Faust
336*4947cdc7SCole Faust	startNano := nanotime()
337*4947cdc7SCole Faust
338*4947cdc7SCole Faust	// Read profile events from the channel
339*4947cdc7SCole Faust	// until it is closed by StopProfiler.
340*4947cdc7SCole Faust	for e := range profiler.events {
341*4947cdc7SCole Faust		sample := new(bytes.Buffer)
342*4947cdc7SCole Faust		sampleenc := protoEncoder{w: sample}
343*4947cdc7SCole Faust		sampleenc.int(Sample_value, e.time.Nanoseconds()) // wall nanoseconds
344*4947cdc7SCole Faust		for _, fr := range e.stack {
345*4947cdc7SCole Faust			sampleenc.uint(Sample_location_id, location(fr))
346*4947cdc7SCole Faust		}
347*4947cdc7SCole Faust		enc.bytes(Profile_sample, sample.Bytes())
348*4947cdc7SCole Faust	}
349*4947cdc7SCole Faust
350*4947cdc7SCole Faust	endNano := nanotime()
351*4947cdc7SCole Faust	enc.int(Profile_duration_nanos, endNano-startNano)
352*4947cdc7SCole Faust
353*4947cdc7SCole Faust	err := gz.Close() // Close reports any prior write error
354*4947cdc7SCole Faust	if flushErr := bufw.Flush(); err == nil {
355*4947cdc7SCole Faust		err = flushErr
356*4947cdc7SCole Faust	}
357*4947cdc7SCole Faust	profiler.done <- err
358*4947cdc7SCole Faust}
359*4947cdc7SCole Faust
360*4947cdc7SCole Faust// nanotime returns the time in nanoseconds since epoch.
361*4947cdc7SCole Faust// It is implemented by runtime.nanotime using the linkname hack;
362*4947cdc7SCole Faust// runtime.nanotime is defined for all OSs/ARCHS and uses the
363*4947cdc7SCole Faust// monotonic system clock, which there is no portable way to access.
364*4947cdc7SCole Faust// Should that function ever go away, these alternatives exist:
365*4947cdc7SCole Faust//
366*4947cdc7SCole Faust// 	// POSIX only. REALTIME not MONOTONIC. 17ns.
367*4947cdc7SCole Faust// 	var tv syscall.Timeval
368*4947cdc7SCole Faust// 	syscall.Gettimeofday(&tv) // can't fail
369*4947cdc7SCole Faust// 	return tv.Nano()
370*4947cdc7SCole Faust//
371*4947cdc7SCole Faust// 	// Portable. REALTIME not MONOTONIC. 46ns.
372*4947cdc7SCole Faust// 	return time.Now().Nanoseconds()
373*4947cdc7SCole Faust//
374*4947cdc7SCole Faust//      // POSIX only. Adds a dependency.
375*4947cdc7SCole Faust//	import "golang.org/x/sys/unix"
376*4947cdc7SCole Faust//	var ts unix.Timespec
377*4947cdc7SCole Faust// 	unix.ClockGettime(CLOCK_MONOTONIC, &ts) // can't fail
378*4947cdc7SCole Faust//	return unix.TimespecToNsec(ts)
379*4947cdc7SCole Faust//
380*4947cdc7SCole Faust//go:linkname nanotime runtime.nanotime
381*4947cdc7SCole Faustfunc nanotime() int64
382*4947cdc7SCole Faust
383*4947cdc7SCole Faust// profFuncAddr returns the canonical "address"
384*4947cdc7SCole Faust// of a Callable for use by the profiler.
385*4947cdc7SCole Faustfunc profFuncAddr(fn Callable) uintptr {
386*4947cdc7SCole Faust	switch fn := fn.(type) {
387*4947cdc7SCole Faust	case *Builtin:
388*4947cdc7SCole Faust		return reflect.ValueOf(fn.fn).Pointer()
389*4947cdc7SCole Faust	case *Function:
390*4947cdc7SCole Faust		return uintptr(unsafe.Pointer(fn.funcode))
391*4947cdc7SCole Faust	}
392*4947cdc7SCole Faust
393*4947cdc7SCole Faust	// User-defined callable types are typically of
394*4947cdc7SCole Faust	// of kind pointer-to-struct. Handle them specially.
395*4947cdc7SCole Faust	if v := reflect.ValueOf(fn); v.Type().Kind() == reflect.Ptr {
396*4947cdc7SCole Faust		return v.Pointer()
397*4947cdc7SCole Faust	}
398*4947cdc7SCole Faust
399*4947cdc7SCole Faust	// Address zero is reserved by the protocol.
400*4947cdc7SCole Faust	// Use 1 for callables we don't recognize.
401*4947cdc7SCole Faust	log.Printf("Starlark profiler: no address for Callable %T", fn)
402*4947cdc7SCole Faust	return 1
403*4947cdc7SCole Faust}
404*4947cdc7SCole Faust
405*4947cdc7SCole Faust// We encode the protocol message by hand to avoid making
406*4947cdc7SCole Faust// the interpreter depend on both github.com/google/pprof
407*4947cdc7SCole Faust// and github.com/golang/protobuf.
408*4947cdc7SCole Faust//
409*4947cdc7SCole Faust// This also avoids the need to materialize a protocol message object
410*4947cdc7SCole Faust// tree of unbounded size and serialize it all at the end.
411*4947cdc7SCole Faust// The pprof format appears to have been designed to
412*4947cdc7SCole Faust// permit streaming implementations such as this one.
413*4947cdc7SCole Faust//
414*4947cdc7SCole Faust// See https://developers.google.com/protocol-buffers/docs/encoding.
415*4947cdc7SCole Fausttype protoEncoder struct {
416*4947cdc7SCole Faust	w   io.Writer // *bytes.Buffer or *gzip.Writer
417*4947cdc7SCole Faust	tmp [binary.MaxVarintLen64]byte
418*4947cdc7SCole Faust}
419*4947cdc7SCole Faust
420*4947cdc7SCole Faustfunc (e *protoEncoder) uvarint(x uint64) {
421*4947cdc7SCole Faust	n := binary.PutUvarint(e.tmp[:], x)
422*4947cdc7SCole Faust	e.w.Write(e.tmp[:n])
423*4947cdc7SCole Faust}
424*4947cdc7SCole Faust
425*4947cdc7SCole Faustfunc (e *protoEncoder) tag(field, wire uint) {
426*4947cdc7SCole Faust	e.uvarint(uint64(field<<3 | wire))
427*4947cdc7SCole Faust}
428*4947cdc7SCole Faust
429*4947cdc7SCole Faustfunc (e *protoEncoder) string(field uint, s string) {
430*4947cdc7SCole Faust	e.tag(field, 2) // length-delimited
431*4947cdc7SCole Faust	e.uvarint(uint64(len(s)))
432*4947cdc7SCole Faust	io.WriteString(e.w, s)
433*4947cdc7SCole Faust}
434*4947cdc7SCole Faust
435*4947cdc7SCole Faustfunc (e *protoEncoder) bytes(field uint, b []byte) {
436*4947cdc7SCole Faust	e.tag(field, 2) // length-delimited
437*4947cdc7SCole Faust	e.uvarint(uint64(len(b)))
438*4947cdc7SCole Faust	e.w.Write(b)
439*4947cdc7SCole Faust}
440*4947cdc7SCole Faust
441*4947cdc7SCole Faustfunc (e *protoEncoder) uint(field uint, x uint64) {
442*4947cdc7SCole Faust	e.tag(field, 0) // varint
443*4947cdc7SCole Faust	e.uvarint(x)
444*4947cdc7SCole Faust}
445*4947cdc7SCole Faust
446*4947cdc7SCole Faustfunc (e *protoEncoder) int(field uint, x int64) {
447*4947cdc7SCole Faust	e.tag(field, 0) // varint
448*4947cdc7SCole Faust	e.uvarint(uint64(x))
449*4947cdc7SCole Faust}
450