From 4062b53405763921e3c3a2384b89c9f00b57c129 Mon Sep 17 00:00:00 2001 From: "Gubrud, Aaron D" Date: Mon, 18 Aug 2025 10:36:04 -0700 Subject: [PATCH 1/2] change how CPUSocketMap is created, adapt coalesceEvents to leverage this mapping --- cmd/metrics/event_frame.go | 14 +++++++++++--- cmd/metrics/metadata.go | 29 +++++++++-------------------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/cmd/metrics/event_frame.go b/cmd/metrics/event_frame.go index b8dca3f0..9be08dd2 100644 --- a/cmd/metrics/event_frame.go +++ b/cmd/metrics/event_frame.go @@ -233,10 +233,18 @@ func coalesceEvents(allEvents []Event, scope string, granularity string, metadat cpuMap[cpu] = i } } else { - numCPUs = metadata.SocketCount * metadata.CoresPerSocket * metadata.ThreadsPerCore + numCPUs = len(metadata.CPUSocketMap) cpuMap = make(map[int]int, numCPUs) - for i := 0; i < numCPUs; i++ { - cpuMap[i] = i + var cpuIDs []int + for cpuID := range metadata.CPUSocketMap { + cpuIDs = append(cpuIDs, cpuID) + } + slices.Sort(cpuIDs) + + i := 0 + for _, cpuID := range cpuIDs { + cpuMap[cpuID] = i + i++ } } // note: if some cores have been off-lined, this may cause an issue because 'perf' seems diff --git a/cmd/metrics/metadata.go b/cmd/metrics/metadata.go index 5190475f..1fe3c466 100644 --- a/cmd/metrics/metadata.go +++ b/cmd/metrics/metadata.go @@ -85,7 +85,7 @@ func LoadMetadata(myTarget target.Target, noRoot bool, noSystemSummary bool, per metadata.ThreadsPerCore = 1 } // CPUSocketMap (from cpuInfo) - metadata.CPUSocketMap = createCPUSocketMap(metadata.CoresPerSocket, metadata.SocketCount, metadata.ThreadsPerCore == 2) + metadata.CPUSocketMap = createCPUSocketMap(cpuInfo) // Model Name (from cpuInfo) metadata.ModelName = cpuInfo[0]["model name"] // Vendor (from cpuInfo) @@ -709,29 +709,18 @@ func getKernelVersion(scriptOutputs map[string]script.ScriptOutput) (version str } // createCPUSocketMap creates a mapping of logical CPUs to their corresponding sockets. -// The function takes the number of cores per socket, the number of sockets, and a boolean indicating whether hyperthreading is enabled. +// The function traverses the output of /proc/cpuinfo and examines the "processor" and "physical id" fields. // It returns a map where the key is the logical CPU index and the value is the socket index. -func createCPUSocketMap(coresPerSocket int, sockets int, hyperthreading bool) (cpuSocketMap map[int]int) { +func createCPUSocketMap(cpuInfo []map[string]string) (cpuSocketMap map[int]int) { // Create an empty map cpuSocketMap = make(map[int]int) - // Calculate the total number of logical CPUs - totalCPUs := coresPerSocket * sockets - if hyperthreading { - totalCPUs *= 2 // hyperthreading doubles the number of logical CPUs - } - // Assign each CPU to a socket - for i := range totalCPUs { - // Assume that the CPUs are evenly distributed between the sockets - socket := i / coresPerSocket - if hyperthreading { - // With non-adjacent hyperthreading, the second logical CPU of each core is in the second half - if i >= totalCPUs/2 { - socket = (i - totalCPUs/2) / coresPerSocket - } - } - // Store the mapping - cpuSocketMap[i] = socket + // Iterate over the CPU info to create the mapping + for idx := range cpuInfo { + procID, _ := strconv.Atoi(cpuInfo[idx]["processor"]) + physID, _ := strconv.Atoi(cpuInfo[idx]["physical id"]) + cpuSocketMap[procID] = physID } + return cpuSocketMap } From 2cd62e29cbea6da26a7df568f36d42fb304ca3c6 Mon Sep 17 00:00:00 2001 From: "Gubrud, Aaron D" Date: Mon, 18 Aug 2025 12:24:03 -0700 Subject: [PATCH 2/2] cleaning up code flow and improving implementation --- cmd/metrics/event_frame.go | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/cmd/metrics/event_frame.go b/cmd/metrics/event_frame.go index 9be08dd2..5becff11 100644 --- a/cmd/metrics/event_frame.go +++ b/cmd/metrics/event_frame.go @@ -219,33 +219,36 @@ func coalesceEvents(allEvents []Event, scope string, granularity string, metadat // create a mapping of cpu numbers to event indices var cpuMap map[int]int - // if cpu range is specified, use it to determine the number of cpus - // otherwise, use the number of sockets, cores per socket, and threads per core - // to determine the number of cpus + // create a list of CPU IDs targeted for profiling + var cpuIDs []int + + // if CPU range is specified, use it to determine the number of CPUs + // otherwise, use the CPUSocketMap structure to determine the online CPUs + // then create a list of CPU IDs for the targeted CPUs if flagCpuRange != "" { - cpuList, err := util.SelectiveIntRangeToIntList(flagCpuRange) + var err error + cpuIDs, err = util.SelectiveIntRangeToIntList(flagCpuRange) if err != nil { return nil, fmt.Errorf("failed to parse cpu range: %w", err) } - numCPUs = len(cpuList) - cpuMap = make(map[int]int, numCPUs) - for i, cpu := range cpuList { - cpuMap[cpu] = i - } + numCPUs = len(cpuIDs) } else { numCPUs = len(metadata.CPUSocketMap) - cpuMap = make(map[int]int, numCPUs) - var cpuIDs []int + for cpuID := range metadata.CPUSocketMap { cpuIDs = append(cpuIDs, cpuID) } - slices.Sort(cpuIDs) + } - i := 0 - for _, cpuID := range cpuIDs { - cpuMap[cpuID] = i - i++ - } + // create a mapping of the target CPU IDs to their event indices + cpuMap = make(map[int]int, numCPUs) + + // sort the CPU IDs + slices.Sort(cpuIDs) + + // place the sorted CPU IDs into the mapping + for idx, cpuID := range cpuIDs { + cpuMap[cpuID] = idx } // note: if some cores have been off-lined, this may cause an issue because 'perf' seems // to still report events for those cores