blob: 31395e89b63446a4b80845cf97dd3ae567edaa89 [file] [log] [blame]
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux
// +build linux
package driver
import (
"bufio"
"bytes"
"io"
"log"
"os"
"os/exec"
"regexp"
"runtime"
"strconv"
"strings"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
const rssMultiplier = 1 << 10
// Runs the cmd under perf. Returns filename of the profile. Any errors are ignored.
func RunUnderProfiler(args ...string) (string, string) {
cmd := exec.Command("perf", append([]string{"record", "-o", "perf.data"}, args...)...)
out, err := cmd.CombinedOutput()
if err != nil {
log.Printf("Failed to execute 'perf record %v': %v\n%v", args, err, string(out))
return "", ""
}
perf1 := perfReport("--sort", "comm")
perf2 := perfReport()
return perf1, perf2
}
func perfReport(args ...string) string {
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command("perf", append([]string{"report", "--stdio"}, args...)...)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
log.Printf("Failed to execute 'perf report': %v\n%v", err, stderr.String())
return ""
}
f, err := os.Create(tempFilename("perf.txt"))
if err != nil {
log.Printf("Failed to create profile file: %v", err)
return ""
}
defer f.Close()
ff := bufio.NewWriter(f)
defer ff.Flush()
// Strip lines starting with #, and limit output to 100 lines.
r := bufio.NewReader(&stdout)
for n := 0; n < 100; {
ln, err := r.ReadBytes('\n')
if err == io.EOF {
break
}
if err != nil {
log.Printf("Failed to scan profile: %v", err)
return ""
}
if len(ln) == 0 || ln[0] == '#' {
continue
}
ff.Write(ln)
n++
}
return f.Name()
}
// Size runs size command on the file. Returns filename with output. Any errors are ignored.
func Size(file string) string {
resf, err := os.Create(tempFilename("size.txt"))
if err != nil {
log.Printf("Failed to create output file: %v", err)
return ""
}
defer resf.Close()
var stderr bytes.Buffer
cmd := exec.Command("size", "-A", file)
cmd.Stdout = resf
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
log.Printf("Failed to execute 'size -m %v': %v\n%v", file, err, stderr.String())
return ""
}
return resf.Name()
}
func getVMPeak() uint64 {
data, err := os.ReadFile("/proc/self/status")
if err != nil {
log.Printf("Failed to read /proc/self/status: %v", err)
return 0
}
re := regexp.MustCompile("VmPeak:[ \t]*([0-9]+) kB")
match := re.FindSubmatch(data)
if match == nil {
log.Printf("No VmPeak in /proc/self/status")
return 0
}
v, err := strconv.ParseUint(string(match[1]), 10, 64)
if err != nil {
log.Printf("Failed to parse VmPeak in /proc/self/status: %v", string(match[1]))
return 0
}
return v * 1024
}
func setProcessAffinity(v int) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
_, _, errno := syscall.Syscall(uintptr(unix.SYS_SCHED_SETAFFINITY), uintptr(syscall.Getpid()), uintptr(unsafe.Sizeof(v)), uintptr(unsafe.Pointer(&v)))
if errno != 0 {
log.Printf("failed to set affinity to %v: %v", v, errno.Error())
return
}
// Re-exec the process w/o affinity flag.
var args []string
for i := 0; i < len(os.Args); i++ {
a := os.Args[i]
if strings.HasPrefix(a, "-affinity") {
if a == "-affinity" {
i++ // also skip the value
}
continue
}
args = append(args, a)
}
if err := syscall.Exec(os.Args[0], args, os.Environ()); err != nil {
log.Printf("failed to exec: %v", err)
}
}