aboutsummaryrefslogtreecommitdiff
path: root/tools/schedlatency
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2024-03-06 06:29:24 -0800
committerFranck Cuny <franck@fcuny.net>2024-03-06 06:29:24 -0800
commit1e4a5aa09c1c8f43722c9c260f011398799a8e8f (patch)
treecd73e0fb8ba53bd21cee6ccf2dcc85639bbbb93f /tools/schedlatency
parentset correct git email in the profiles (diff)
downloadinfra-1e4a5aa09c1c8f43722c9c260f011398799a8e8f.tar.gz
rename `tools` to `packages` to follow convention
The convention is to use `pkgs` or `packages` for overlays and definition of custom packages. Since I'm already using `pkg` for go, I prefer to use `packages` for my scripts.
Diffstat (limited to 'tools/schedlatency')
-rw-r--r--tools/schedlatency/go.mod3
-rw-r--r--tools/schedlatency/main.go254
2 files changed, 0 insertions, 257 deletions
diff --git a/tools/schedlatency/go.mod b/tools/schedlatency/go.mod
deleted file mode 100644
index 9a073ac..0000000
--- a/tools/schedlatency/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module golang.fcuny.net/schedlatency
-
-go 1.17
diff --git a/tools/schedlatency/main.go b/tools/schedlatency/main.go
deleted file mode 100644
index 7dd709e..0000000
--- a/tools/schedlatency/main.go
+++ /dev/null
@@ -1,254 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "strconv"
- "strings"
- "time"
-)
-
-type SchedStat struct {
- Pid int `json:"pid"`
- RunTicks int `json:"run_ticks"`
- WaitTicks int `json:"wait_ticks"`
- SlicesRan int `json:"ran_slices"`
- AverageRun float64 `json:"avg_run"`
- AverageWait float64 `json:"avg_wait"`
-}
-
-func usage() {
- fmt.Fprintf(os.Stderr, "usage: %s <pid>\n", os.Args[0])
-}
-
-func main() {
- if len(os.Args) == 1 {
- usage()
- os.Exit(1)
- }
-
- input := os.Args[1]
- pid, err := strconv.Atoi(input)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to convert %s to a PID: %v", input, err)
- os.Exit(1)
- }
-
- p := Proc{
- PID: pid,
- }
- oran := 0
- owait_ticks := 0
- orun_ticks := 0
- for {
- stat, err := p.SchedStat()
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to get schedstat for %d: %v\n", p.PID, err)
- os.Exit(1)
- }
- diff := stat.SlicesRan - oran
- var avgrun, avgwait float64
-
- if diff > 0 {
- avgrun = float64((stat.RunTicks - orun_ticks) / diff)
- avgwait = float64((stat.WaitTicks - owait_ticks) / diff)
- } else {
- avgrun = 0
- avgwait = 0
- }
-
- stat.AverageRun = avgrun
- stat.AverageWait = avgwait
-
- out, err := json.Marshal(stat)
- if err != nil {
- fmt.Fprintln(err)
- os.Exit(1)
- }
- fmt.Println(string(out))
- oran = stat.SlicesRan
- orun_ticks = stat.RunTicks
- owait_ticks = stat.WaitTicks
- time.Sleep(5 * time.Second)
- }
-}
-
-// This the the path that contains the scheduler statistics.
-// Note that they are not populated unless the value for
-// /proc/sys/kernel/sched_schedstats is 1
-const procSchedStat = "/proc/schedstat"
-
-var idleness = []string{"idle", "busy", "newlyIdle"}
-
-type ProcSchedStat struct {
- RunTicks int `json:"run_ticks"`
- WaitTicks int `json:"wait_ticks"`
- SlicesRan int `json:"ran_slices"`
- AverageRun float64 `json:"avg_run"`
- AverageWait float64 `json:"avg_wait"`
-}
-
-// SchedCPUStat contains the load balancer statistics for a CPU.
-type SchedCPUStat struct {
- YieldCount uint64 `json:"yield_count"`
- SchedulerCount uint64 `json:"sched_count"`
- SchedulerGoIdle uint64 `json:"sched_go_idle"`
- TryToWakeUp uint64 `json:"try_to_wake"`
- TryToWakeUpLocal uint64 `json:"try_to_wake_local"`
- Running uint64 `json:"running"`
- Waiting uint64 `json:"waiting"`
- Slices uint64 `json:"slices"`
- Domains map[string]SchedDomain `json:"domains"`
-}
-
-// SchedLoadBalance contains the load balancer statistics for a domain
-// in a given domain.
-type SchedLoadBalance struct {
- LBCount uint64 `json:"lb_count"`
- LBBalanced uint64 `json:"lb_balanced"`
- LBFailed uint64 `json:"lb_failed"`
- LBImbalanced uint64 `json:"lb_imbalanced"`
- LBGained uint64 `json:"lb_gained"`
- LBHotGain uint64 `json:"lb_hot_gain"`
- LBNoBusyQueue uint64 `json:"lb_no_busy_queue"`
- LBNoBusyGroup uint64 `json:"lb_no_busy_group"`
-}
-
-// SchedDomain contains the statistics for a domain.
-type SchedDomain struct {
- LoadBalancers map[string]SchedLoadBalance `json:"lbs"`
- ActiveLoadBalanceCount uint64 `json:"active_lb_count"`
- ActiveLoadBalanceFailed uint64 `json:"active_lb_failed"`
- ActiveLoadBalancePushed uint64 `json:"active_lb_pushed"`
- TryToWakeUpRemote uint64 `json:"try_to_wake_up_remote"`
- TryToWakeUpMoveAffine uint64 `json:"try_to_wake_up_move_affine"`
- TryToWakeUpMoveBalance uint64 `json:"try_to_wake_up_move_balance"`
-}
-
-// Proc provides information about a running process.
-type Proc struct {
- // The process ID.
- PID int
-}
-
-// SchedStat returns scheduler statistics for the process.
-// The information available are:
-// 1. time spent on the cpu
-// 2. time spent waiting on a runqueue
-// 3. # of timeslices run on this cpu
-func (p Proc) SchedStat() (ProcSchedStat, error) {
- path := fmt.Sprintf("/proc/%d/schedstat", p.PID)
- b, err := ioutil.ReadFile(path)
- if err != nil {
- return ProcSchedStat{}, err
- }
- content := string(b)
- stats := strings.Fields(content)
-
- run_ticks, err := strconv.Atoi(stats[0])
- if err != nil {
- return ProcSchedStat{}, err
- }
-
- wait_ticks, err := strconv.Atoi(stats[1])
- if err != nil {
- return ProcSchedStat{}, err
- }
-
- nran, err := strconv.Atoi(stats[2])
- if err != nil {
- return ProcSchedStat{}, err
- }
-
- stat := ProcSchedStat{
- RunTicks: run_ticks,
- WaitTicks: wait_ticks,
- SlicesRan: nran,
- }
- return stat, nil
-}
-
-// ReadSchedstat returns statistics from the scheduler.
-// Information about the statistics can be found at
-// https://www.kernel.org/doc/html/latest/scheduler/sched-stats.html.
-func ReadSchedStat() (map[string]SchedCPUStat, error) {
- b, err := ioutil.ReadFile(procSchedStat)
- if err != nil {
- return nil, fmt.Errorf("procfs: failed to open %s: %v", procSchedStat, err)
- }
- content := string(b)
-
- cpus := map[string]SchedCPUStat{}
-
- lines := strings.Split(content, "\n")
-
- var currentCpu string
-
- // The first line is the version of the stats
- // TODO(fcuny): we should check which version is used, because the
- // format changes.
- for _, line := range lines[2:] {
- // The format is as follow:
- // cpu<N> 1 2 3 4 5 6 7 8 9
- // domain<N> <cpumask> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
- if strings.HasPrefix(line, "cpu") {
- // meaning of the fields: https://www.kernel.org/doc/html/latest/scheduler/sched-stats.html#cpu-statistics
- fields := strings.Fields(line)
- cpuStat := SchedCPUStat{
- YieldCount: convertField(fields[1]),
- SchedulerCount: convertField(fields[3]),
- SchedulerGoIdle: convertField(fields[4]),
- TryToWakeUp: convertField(fields[5]),
- TryToWakeUpLocal: convertField(fields[6]),
- Running: convertField(fields[7]),
- Waiting: convertField(fields[8]),
- Slices: convertField(fields[9]),
- Domains: map[string]SchedDomain{},
- }
- currentCpu = fields[0]
- cpus[currentCpu] = cpuStat
- } else if strings.HasPrefix(line, "domain") {
- // meaning of the fields: https://www.kernel.org/doc/html/latest/scheduler/sched-stats.html#domain-statistics
- fields := strings.Fields(line)
- i := 2
- lbs := map[string]SchedLoadBalance{}
- for _, idle := range idleness {
- lb := SchedLoadBalance{
- LBCount: convertField(fields[i]),
- LBBalanced: convertField(fields[i+1]),
- LBFailed: convertField(fields[i+2]),
- LBImbalanced: convertField(fields[i+3]),
- LBGained: convertField(fields[i+4]),
- LBHotGain: convertField(fields[i+5]),
- LBNoBusyQueue: convertField(fields[i+6]),
- LBNoBusyGroup: convertField(fields[i+7]),
- }
- i = i + 8
- lbs[idle] = lb
- }
- domain := SchedDomain{
- LoadBalancers: lbs,
- ActiveLoadBalanceCount: convertField(fields[26]),
- ActiveLoadBalanceFailed: convertField(fields[27]),
- ActiveLoadBalancePushed: convertField(fields[28]),
- TryToWakeUpRemote: convertField(fields[35]),
- TryToWakeUpMoveAffine: convertField(fields[36]),
- TryToWakeUpMoveBalance: convertField(fields[37]),
- }
- c := cpus[currentCpu]
- c.Domains[fields[0]] = domain
- cpus[currentCpu] = c
- }
- }
- return cpus, nil
-}
-
-func convertField(field string) uint64 {
- val, err := strconv.ParseUint(field, 10, 64)
- if err != nil {
- return 0
- }
- return val
-}