aboutsummaryrefslogtreecommitdiff
path: root/tools/scheddomain/main.go
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2022-06-19 14:28:01 -0700
committerFranck Cuny <franck@fcuny.net>2022-06-19 14:55:23 -0700
commit14556b646fa957a919759a58440a0d3b31b5644f (patch)
treea253af969a73fc1f6635870de8a50578241f7470 /tools/scheddomain/main.go
parentfeat(tools/git-bootstrap): initialize a git repository with my defaults (diff)
downloadinfra-14556b646fa957a919759a58440a0d3b31b5644f.tar.gz
feat(tools/scheddomain): add a tool to report on scheduler domains
Change-Id: Ia4e1aa1e7fc48b8bfb619aba9ba71037ffcc69f8 Reviewed-on: https://cl.fcuny.net/c/world/+/451 Tested-by: CI Reviewed-by: Franck Cuny <franck@fcuny.net>
Diffstat (limited to 'tools/scheddomain/main.go')
-rw-r--r--tools/scheddomain/main.go153
1 files changed, 153 insertions, 0 deletions
diff --git a/tools/scheddomain/main.go b/tools/scheddomain/main.go
new file mode 100644
index 0000000..1d0f5d3
--- /dev/null
+++ b/tools/scheddomain/main.go
@@ -0,0 +1,153 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+// https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux/+/v4.17/include/linux/sched/topology.h#20
+var SDFlags = map[string]uint64{
+ "SD_LOAD_BALANCE": 0x0001,
+ "SD_BALANCE_NEWIDLE": 0x0002,
+ "SD_BALANCE_EXEC": 0x0004,
+ "SD_BALANCE_FORK": 0x0008,
+ "SD_BALANCE_WAKE": 0x0010,
+ "SD_WAKE_AFFINE": 0x0020,
+ "SD_ASYM_CPUCAPACITY": 0x0040,
+ "SD_SHARE_CPUCAPACITY": 0x0080,
+ "SD_SHARE_POWERDOMAIN": 0x0100,
+ "SD_SHARE_PKG_RESOURCES": 0x0200,
+ "SD_SERIALIZE": 0x0400,
+ "SD_ASYM_PACKING": 0x0800,
+ "SD_PREFER_SIBLING": 0x1000,
+ "SD_OVERLAP": 0x2000,
+ "SD_NUMA": 0x4000,
+}
+
+type Scheduler map[string][]Domain
+
+type Domain struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Flags []string `json:"flags"`
+ Indexes map[string]string `json:"indexes"`
+}
+
+func main() {
+ cpus, err := CPUs()
+ if err != nil {
+ fmt.Fprint(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ if len(cpus) == 0 {
+ fmt.Fprint(os.Stderr, "there is no scheduler domains\n")
+ os.Exit(1)
+ }
+
+ sched := Scheduler{}
+ for _, cpu := range cpus {
+ _, cpuID := path.Split(cpu)
+ domains, err := domains(cpu)
+ if err != nil {
+ fmt.Fprint(os.Stderr, err)
+ os.Exit(1)
+ }
+ sched[cpuID] = domains
+ }
+ out, err := json.Marshal(sched)
+ if err != nil {
+ fmt.Fprint(os.Stderr, err)
+ os.Exit(1)
+ }
+ fmt.Println(string(out))
+}
+
+func domains(cpuPath string) ([]Domain, error) {
+ domainPath := fmt.Sprintf("%s/domain*", cpuPath)
+ availDomains, err := filepath.Glob(domainPath)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get domains under %s: %v", cpuPath, err)
+ }
+
+ domains := []Domain{}
+
+ if len(availDomains) == 0 {
+ return domains, nil
+ }
+
+ for _, d := range availDomains {
+ _, dName := path.Split(d)
+ dType := getContent(d, "name")
+ flags, err := domainFlags(d)
+ if err != nil {
+ return nil, err
+ }
+ indexes := domainIndexes(d)
+
+ domain := Domain{
+ Name: dName,
+ Type: dType,
+ Flags: flags,
+ Indexes: indexes,
+ }
+ domains = append(domains, domain)
+ }
+ return domains, nil
+}
+
+func domainFlags(path string) ([]string, error) {
+ flagPath := fmt.Sprintf("%s/flags", path)
+
+ content, err := ioutil.ReadFile(flagPath)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read %s: %v", flagPath, err)
+ }
+
+ flags, err := strconv.ParseUint(strings.TrimSpace(string(content)), 0, 64)
+ if err != nil {
+ return nil, fmt.Errorf("failed to convert flags %s: %v", flagPath, err)
+ }
+
+ supportedFlags := []string{}
+ for k, v := range SDFlags {
+ if flags&v > 0 {
+ supportedFlags = append(supportedFlags, k)
+ }
+ }
+ return supportedFlags, nil
+}
+
+func domainIndexes(path string) map[string]string {
+ indexes := map[string]string{
+ "busy": getContent(path, "busy_idx"),
+ "idle": getContent(path, "idle_idx"),
+ "new_idle": getContent(path, "newidle_idx"),
+ "wake": getContent(path, "wake_idx"),
+ "fork_exec": getContent(path, "forkexec_idx"),
+ }
+ return indexes
+}
+
+func getContent(path, fileName string) string {
+ domainName := fmt.Sprintf("%s/%s", path, fileName)
+ name, err := ioutil.ReadFile(domainName)
+ if err != nil {
+ return ""
+ }
+ return strings.TrimSpace(string(name))
+}
+
+func CPUs() ([]string, error) {
+ cpus, err := filepath.Glob("/proc/sys/kernel/sched_domain/cpu*")
+ if err != nil {
+ return nil, fmt.Errorf("failed to get a list of cpus: %v", err)
+ }
+ return cpus, nil
+}