Repository: microsoft/retina
File: pkg/telemetry/perf_unix.go (Go)

Code before:
}

func NewPerfProfile() (*PerfProfile, error) {
	p := &PerfProfile{}
	var usage syscall.Rusage
	err := syscall.Getrusage(syscall.RUSAGE_SELF, &usage)
	if err != nil {
		return nil, fmt.Errorf("failed to get rusage during init: %w", err)
	}
	p.usage = &usage

	return p, nil
}

func (p *PerfProfile) GetMemoryUsage() map[string]string {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	props := map[string]string{
		allocatedmem: strconv.FormatUint(bToMb(m.Alloc), 10),
		sysmem:       strconv.FormatUint(bToMb(m.Sys), 10),
		heapallocmem: strconv.FormatUint(bToMb(m.HeapAlloc), 10),
		heapobjects:  strconv.FormatUint(m.HeapObjects, 10),
		heapsys:      strconv.FormatUint(bToMb(m.HeapSys), 10),
		stackinuse:   strconv.FormatUint(bToMb(m.StackInuse), 10),
		stacksys:     strconv.FormatUint(bToMb(m.StackSys), 10),
		goroutines:   strconv.Itoa(runtime.NumGoroutine()),
	}
	return props
}

func (p *PerfProfile) GetCPUUsage() (map[string]string, error) { //nolint unnamed results are fine
	props := make(map[string]string)
	if p.usage == nil {
		return props, ErrNotInitialized
	}

	p.perflock.Lock()
	defer p.perflock.Unlock()
	var currentUsage syscall.Rusage
	err := syscall.Getrusage(syscall.RUSAGE_SELF, &currentUsage)
	if err != nil {
		return props, fmt.Errorf("failed to get rusage: %w", err)
	}

	userTime := (currentUsage.Utime.Sec - p.usage.Utime.Sec)
	userTime += int64(currentUsage.Utime.Usec-p.usage.Utime.Usec) >> microsecondBitShift

	sysTime := currentUsage.Stime.Sec - p.usage.Stime.Sec
	sysTime += int64(currentUsage.Stime.Usec-p.usage.Stime.Usec) >> microsecondBitShift

	p.usage = &currentUsage

Review comment (suggestion):
Since we don't want to enable `gctrace`, can we add GC telemetry here? Like `GCSys`, `NextGC` , `LastGC`, `NumGC`, `NumForcedGC`.

Code after:
}

func NewPerfProfile() (*PerfProfile, error) {
	p := &PerfProfile{}
	var usage syscall.Rusage
	err := syscall.Getrusage(syscall.RUSAGE_SELF, &usage)
	if err != nil {
		return nil, fmt.Errorf("failed to get rusage during init: %w", err)
	}
	p.usage = &usage

	return p, nil
}

func (p *PerfProfile) GetMemoryUsage() map[string]string {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	props := map[string]string{
		sysmem:      strconv.FormatUint(bToMb(m.Sys), 10),
		othersysmem: strconv.FormatUint(bToMb(m.OtherSys), 10),
		heapalloc:   strconv.FormatUint(bToMb(m.HeapAlloc), 10),
		heapobjects: strconv.FormatUint(m.HeapObjects, 10),
		heapidle:    strconv.FormatUint(bToMb(m.HeapIdle), 10),
		heapinuse:   strconv.FormatUint(bToMb(m.HeapInuse), 10),
		heapsys:     strconv.FormatUint(bToMb(m.HeapSys), 10),
		stackinuse:  strconv.FormatUint(bToMb(m.StackInuse), 10),
		stacksys:    strconv.FormatUint(bToMb(m.StackSys), 10),
		nextgc:      strconv.FormatUint(bToMb(m.NextGC), 10),
		goroutines:  strconv.Itoa(runtime.NumGoroutine()),
	}
	return props
}

func (p *PerfProfile) GetCPUUsage() (map[string]string, error) { //nolint unnamed results are fine
	props := make(map[string]string)
	if p.usage == nil {
		return props, ErrNotInitialized
	}

	p.perflock.Lock()
	defer p.perflock.Unlock()
	var currentUsage syscall.Rusage
	err := syscall.Getrusage(syscall.RUSAGE_SELF, &currentUsage)
	if err != nil {
		return props, fmt.Errorf("failed to get rusage: %w", err)
	}

	userTime := (currentUsage.Utime.Sec - p.usage.Utime.Sec)
	userTime += int64(currentUsage.Utime.Usec-p.usage.Utime.Usec) >> microsecondBitShift

	sysTime := currentUsage.Stime.Sec - p.usage.Stime.Sec