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 } func fileToInt(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) } 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") cpulist := make([]int, 0) 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 } func CoreList() []int { buffer, err := ioutil.ReadFile("/proc/cpuinfo") if err != nil { log.Print(err) return nil } ll := strings.Split(string(buffer), "\n") corelist := make([]int, 0) for _, line := range ll { if strings.HasPrefix(line, "core id") { lv := strings.Fields(line) id, err := strconv.ParseInt(lv[3], 10, 32) if err != nil { log.Print(err) return corelist } _, found := intArrayContains(corelist, int(id)) if !found { corelist = append(corelist, int(id)) } } } return corelist } func NumaNodeList() []int { numalist := make([]int, 0) files, err := filepath.Glob("/sys/devices/system/node/node*") 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 { _, found := intArrayContains(numalist, int(id)) if !found { numalist = append(numalist, int(id)) } } } } return numalist } func DieList() []int { cpulist := CpuList() dielist := make([]int, 0) for _, c := range cpulist { dieid := fileToInt(fmt.Sprintf("/sys/devices/system/cpu/cpu%d/topology/die_id", c)) _, found := intArrayContains(dielist, int(dieid)) if !found { dielist = append(dielist, int(dieid)) } } return dielist } 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 NumCores int NumNumaDomains int } func CpuInfo() CpuInformation { var c CpuInformation smt := 0 numa := 0 die := 0 socket := 0 core := 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 } if d.Core > core { core = d.Core } } c.NumNumaDomains = numa + 1 c.SMTWidth = smt + 1 c.NumDies = die + 1 c.NumCores = core + 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 } func GetSocketCpus(socket int) []int { all := CpuData() cpulist := make([]int, 0) for _, d := range all { if d.Socket == socket { cpulist = append(cpulist, d.Cpuid) } } return cpulist } func GetNumaDomainCpus(domain int) []int { all := CpuData() cpulist := make([]int, 0) for _, d := range all { if d.Numadomain == domain { cpulist = append(cpulist, d.Cpuid) } } return cpulist } func GetDieCpus(die int) []int { all := CpuData() cpulist := make([]int, 0) for _, d := range all { if d.Die == die { cpulist = append(cpulist, d.Cpuid) } } return cpulist } func GetCoreCpus(core int) []int { all := CpuData() cpulist := make([]int, 0) for _, d := range all { if d.Core == core { cpulist = append(cpulist, d.Cpuid) } } return cpulist }