func profile()

in starlark/profile.go [189:358]


func profile(w io.Writer) {
	// Field numbers from pprof protocol.
	// See https://github.com/google/pprof/blob/master/proto/profile.proto
	const (
		Profile_sample_type    = 1  // repeated ValueType
		Profile_sample         = 2  // repeated Sample
		Profile_mapping        = 3  // repeated Mapping
		Profile_location       = 4  // repeated Location
		Profile_function       = 5  // repeated Function
		Profile_string_table   = 6  // repeated string
		Profile_time_nanos     = 9  // int64
		Profile_duration_nanos = 10 // int64
		Profile_period_type    = 11 // ValueType
		Profile_period         = 12 // int64

		ValueType_type = 1 // int64
		ValueType_unit = 2 // int64

		Sample_location_id = 1 // repeated uint64
		Sample_value       = 2 // repeated int64
		Sample_label       = 3 // repeated Label

		Label_key      = 1 // int64
		Label_str      = 2 // int64
		Label_num      = 3 // int64
		Label_num_unit = 4 // int64

		Location_id         = 1 // uint64
		Location_mapping_id = 2 // uint64
		Location_address    = 3 // uint64
		Location_line       = 4 // repeated Line

		Line_function_id = 1 // uint64
		Line_line        = 2 // int64

		Function_id          = 1 // uint64
		Function_name        = 2 // int64
		Function_system_name = 3 // int64
		Function_filename    = 4 // int64
		Function_start_line  = 5 // int64
	)

	bufw := bufio.NewWriter(w) // write file in 4KB (not 240B flate-sized) chunks
	gz := gzip.NewWriter(bufw)
	enc := protoEncoder{w: gz}

	// strings
	stringIndex := make(map[string]int64)
	str := func(s string) int64 {
		i, ok := stringIndex[s]
		if !ok {
			i = int64(len(stringIndex))
			enc.string(Profile_string_table, s)
			stringIndex[s] = i
		}
		return i
	}
	str("") // entry 0

	// functions
	//
	// function returns the ID of a Callable for use in Line.FunctionId.
	// The ID is the same as the function's logical address,
	// which is supplied by the caller to avoid the need to recompute it.
	functionId := make(map[uintptr]uint64)
	function := func(fn Callable, addr uintptr) uint64 {
		id, ok := functionId[addr]
		if !ok {
			id = uint64(addr)

			var pos syntax.Position
			if fn, ok := fn.(callableWithPosition); ok {
				pos = fn.Position()
			}

			name := fn.Name()
			if name == "<toplevel>" {
				name = pos.Filename()
			}

			nameIndex := str(name)

			fun := new(bytes.Buffer)
			funenc := protoEncoder{w: fun}
			funenc.uint(Function_id, id)
			funenc.int(Function_name, nameIndex)
			funenc.int(Function_system_name, nameIndex)
			funenc.int(Function_filename, str(pos.Filename()))
			funenc.int(Function_start_line, int64(pos.Line))
			enc.bytes(Profile_function, fun.Bytes())

			functionId[addr] = id
		}
		return id
	}

	// locations
	//
	// location returns the ID of the location denoted by fr.
	// For Starlark frames, this is the Frame pc.
	locationId := make(map[uintptr]uint64)
	location := func(fr profFrame) uint64 {
		fnAddr := profFuncAddr(fr.fn)

		// For Starlark functions, the frame position
		// represents the current PC value.
		// Mix it into the low bits of the address.
		// This is super hacky and may result in collisions
		// in large functions or if functions are numerous.
		// TODO(adonovan): fix: try making this cleaner by treating
		// each bytecode segment as a Profile.Mapping.
		pcAddr := fnAddr
		if _, ok := fr.fn.(*Function); ok {
			pcAddr = (pcAddr << 16) ^ uintptr(fr.pc)
		}

		id, ok := locationId[pcAddr]
		if !ok {
			id = uint64(pcAddr)

			line := new(bytes.Buffer)
			lineenc := protoEncoder{w: line}
			lineenc.uint(Line_function_id, function(fr.fn, fnAddr))
			lineenc.int(Line_line, int64(fr.pos.Line))
			loc := new(bytes.Buffer)
			locenc := protoEncoder{w: loc}
			locenc.uint(Location_id, id)
			locenc.uint(Location_address, uint64(pcAddr))
			locenc.bytes(Location_line, line.Bytes())
			enc.bytes(Profile_location, loc.Bytes())

			locationId[pcAddr] = id
		}
		return id
	}

	wallNanos := new(bytes.Buffer)
	wnenc := protoEncoder{w: wallNanos}
	wnenc.int(ValueType_type, str("wall"))
	wnenc.int(ValueType_unit, str("nanoseconds"))

	// informational fields of Profile
	enc.bytes(Profile_sample_type, wallNanos.Bytes())
	enc.int(Profile_period, quantum.Nanoseconds())     // magnitude of sampling period
	enc.bytes(Profile_period_type, wallNanos.Bytes())  // dimension and unit of period
	enc.int(Profile_time_nanos, time.Now().UnixNano()) // start (real) time of profile

	startNano := nanotime()

	// Read profile events from the channel
	// until it is closed by StopProfiler.
	for e := range profiler.events {
		sample := new(bytes.Buffer)
		sampleenc := protoEncoder{w: sample}
		sampleenc.int(Sample_value, e.time.Nanoseconds()) // wall nanoseconds
		for _, fr := range e.stack {
			sampleenc.uint(Sample_location_id, location(fr))
		}
		enc.bytes(Profile_sample, sample.Bytes())
	}

	endNano := nanotime()
	enc.int(Profile_duration_nanos, endNano-startNano)

	err := gz.Close() // Close reports any prior write error
	if flushErr := bufw.Flush(); err == nil {
		err = flushErr
	}
	profiler.done <- err
}