diff --git a/internal/ccTopology/ccTopology.go b/internal/ccTopology/ccTopology.go new file mode 100644 index 0000000..8d53b05 --- /dev/null +++ b/internal/ccTopology/ccTopology.go @@ -0,0 +1,277 @@ +package ccTopology + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strconv" + "strings" + + cclogger "github.com/ClusterCockpit/cc-metric-collector/internal/ccLogger" +) + +// intArrayContains scans an array of ints if the value str is present in the array +// If the specified value is found, the corresponding array index is returned. +// The bool value is used to signal success or failure +func intArrayContains(array []int, str int) (int, bool) { + for i, a := range array { + if a == str { + return i, true + } + } + return -1, false +} + +// stringArrayContains scans an array of strings if the value str is present in the array +// If the specified value is found, the corresponding array index is returned. +// The bool value is used to signal success or failure +// func stringArrayContains(array []string, str string) (int, bool) { +// for i, a := range array { +// if a == str { +// return i, true +// } +// } +// return -1, false +// } + +func SocketList() []int { + buffer, err := ioutil.ReadFile("/proc/cpuinfo") + if err != nil { + log.Print(err) + return nil + } + ll := strings.Split(string(buffer), "\n") + var packs []int + for _, line := range ll { + if strings.HasPrefix(line, "physical id") { + lv := strings.Fields(line) + id, err := strconv.ParseInt(lv[3], 10, 32) + if err != nil { + log.Print(err) + return packs + } + _, found := intArrayContains(packs, int(id)) + if !found { + packs = append(packs, int(id)) + } + } + } + return packs +} + +func CpuList() []int { + buffer, err := ioutil.ReadFile("/proc/cpuinfo") + if err != nil { + log.Print(err) + return nil + } + ll := strings.Split(string(buffer), "\n") + var cpulist []int + for _, line := range ll { + if strings.HasPrefix(line, "processor") { + lv := strings.Fields(line) + id, err := strconv.ParseInt(lv[2], 10, 32) + if err != nil { + log.Print(err) + return cpulist + } + _, found := intArrayContains(cpulist, int(id)) + if !found { + cpulist = append(cpulist, int(id)) + } + } + } + return cpulist +} + +type CpuEntry struct { + Cpuid int + SMT int + Core int + Socket int + Numadomain int + Die int +} + +func CpuData() []CpuEntry { + + fileToInt := func(path string) int { + buffer, err := ioutil.ReadFile(path) + if err != nil { + log.Print(err) + cclogger.ComponentError("ccTopology", "Reading", path, ":", err.Error()) + return -1 + } + sbuffer := strings.Replace(string(buffer), "\n", "", -1) + var id int64 + //_, err = fmt.Scanf("%d", sbuffer, &id) + id, err = strconv.ParseInt(sbuffer, 10, 32) + if err != nil { + cclogger.ComponentError("ccTopology", "Parsing", path, ":", sbuffer, err.Error()) + return -1 + } + return int(id) + } + getCore := func(basepath string) int { + return fileToInt(fmt.Sprintf("%s/core_id", basepath)) + } + + getSocket := func(basepath string) int { + return fileToInt(fmt.Sprintf("%s/physical_package_id", basepath)) + } + + getDie := func(basepath string) int { + return fileToInt(fmt.Sprintf("%s/die_id", basepath)) + } + + getSMT := func(cpuid int, basepath string) int { + buffer, err := ioutil.ReadFile(fmt.Sprintf("%s/thread_siblings_list", basepath)) + if err != nil { + log.Print(err) + } + threadlist := make([]int, 0) + sbuffer := strings.Replace(string(buffer), "\n", "", -1) + for _, x := range strings.Split(sbuffer, ",") { + id, err := strconv.ParseInt(x, 10, 32) + if err != nil { + log.Print(err) + } + threadlist = append(threadlist, int(id)) + } + for i, x := range threadlist { + if x == cpuid { + return i + } + } + return 1 + } + + getNumaDomain := func(basepath string) int { + files, err := filepath.Glob(fmt.Sprintf("%s/node*", basepath)) + if err != nil { + log.Print(err) + } + for _, f := range files { + finfo, err := os.Lstat(f) + if err == nil && (finfo.IsDir() || finfo.Mode()&os.ModeSymlink != 0) { + var id int + parts := strings.Split(f, "/") + _, err = fmt.Scanf("node%d", parts[len(parts)-1], &id) + if err == nil { + return id + } + } + } + return 0 + } + + clist := make([]CpuEntry, 0) + for _, c := range CpuList() { + clist = append(clist, CpuEntry{Cpuid: c}) + } + for _, centry := range clist { + centry.Socket = -1 + centry.Numadomain = -1 + centry.Die = -1 + centry.Core = -1 + // Set base directory for topology lookup + base := fmt.Sprintf("/sys/devices/system/cpu/cpu%d/topology", centry.Cpuid) + + // Lookup CPU core id + centry.Core = getCore(base) + + // Lookup CPU socket id + centry.Socket = getSocket(base) + + // Lookup CPU die id + centry.Die = getDie(base) + + // Lookup SMT thread id + centry.SMT = getSMT(centry.Cpuid, base) + + // Lookup NUMA domain id + centry.Numadomain = getNumaDomain(base) + + } + return clist +} + +type CpuInformation struct { + NumHWthreads int + SMTWidth int + NumSockets int + NumDies int + NumNumaDomains int +} + +func CpuInfo() CpuInformation { + var c CpuInformation + + smt := 0 + numa := 0 + die := 0 + socket := 0 + cdata := CpuData() + for _, d := range cdata { + if d.SMT > smt { + smt = d.SMT + } + if d.Numadomain > numa { + numa = d.Numadomain + } + if d.Die > die { + die = d.Die + } + if d.Socket > socket { + socket = d.Socket + } + } + c.NumNumaDomains = numa + 1 + c.SMTWidth = smt + 1 + c.NumDies = die + 1 + c.NumSockets = socket + 1 + c.NumHWthreads = len(cdata) + return c +} + +func GetCpuSocket(cpuid int) int { + cdata := CpuData() + for _, d := range cdata { + if d.Cpuid == cpuid { + return d.Socket + } + } + return -1 +} + +func GetCpuNumaDomain(cpuid int) int { + cdata := CpuData() + for _, d := range cdata { + if d.Cpuid == cpuid { + return d.Numadomain + } + } + return -1 +} + +func GetCpuDie(cpuid int) int { + cdata := CpuData() + for _, d := range cdata { + if d.Cpuid == cpuid { + return d.Die + } + } + return -1 +} + +func GetCpuCore(cpuid int) int { + cdata := CpuData() + for _, d := range cdata { + if d.Cpuid == cpuid { + return d.Core + } + } + return -1 +}