Skip to content

Commit 3c598f2

Browse files
[exporter/signalfx] Fix for physical cores and cpus counting in linux (#47550)
1 parent f608be1 commit 3c598f2

4 files changed

Lines changed: 70 additions & 23 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: bug_fix
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. receiver/filelog)
7+
component: exporter/signalfx
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Fix incorrect `host_physical_cpus` and `host_cpu_cores` values reported on Linux
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [47550]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [user]

exporter/signalfxexporter/internal/hostmetadata/host.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package hostmetadata // import "github.com/open-telemetry/opentelemetry-collecto
88

99
import (
1010
"context"
11+
"fmt"
1112
"strconv"
1213
"time"
1314

@@ -52,29 +53,38 @@ func (c *hostCPU) toStringMap() map[string]string {
5253
func getCPU(ctx context.Context) (info *hostCPU, err error) {
5354
info = &hostCPU{}
5455

55-
// get physical cpu stats
5656
var cpus []cpu.InfoStat
5757

5858
// On Windows this can sometimes take longer than the default timeout (10 seconds).
5959
ctx, cancel := context.WithTimeout(ctx, cpuStatsTimeout)
6060
defer cancel()
6161

62+
// get cpu infoStats
6263
cpus, err = cpuInfo(ctx)
6364
if err != nil {
6465
return info, err
6566
}
6667

67-
info.HostPhysicalCPUs = len(cpus)
68+
// get physical cpu stats
69+
info.HostPhysicalCPUs, err = cpuCounts(ctx, false)
70+
if err != nil {
71+
return info, err
72+
}
6873

6974
// get logical cpu stats
7075
info.HostLogicalCPUs, err = cpuCounts(ctx, true)
7176
if err != nil {
7277
return info, err
7378
}
7479

75-
// total number of cpu cores
80+
// Count physical CPU cores by tracking unique {PhysicalID, CoreID} pairs.
81+
physicalCores := make(map[string]bool)
7682
for i := range cpus {
77-
info.HostCPUCores += int64(cpus[i].Cores)
83+
k := fmt.Sprintf("%s,%s", cpus[i].PhysicalID, cpus[i].CoreID)
84+
if !physicalCores[k] {
85+
physicalCores[k] = true
86+
info.HostCPUCores++
87+
}
7888
// TODO: This is not ideal... if there are different processors
7989
// we will only report one of the models... This is unlikely to happen,
8090
// but it could

exporter/signalfxexporter/internal/hostmetadata/host_test.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,33 @@ func TestGetCPU(t *testing.T) {
3232
{
3333
name: "successful host cpu info",
3434
fixtures: testfixture{
35+
// Simulate a Linux host: 2 sockets, 2 cores each, 2 threads per core = 8 logical CPUs.
36+
// On Linux, cpu.InfoWithContext returns one InfoStat per logical CPU with Cores=1,
37+
// so PhysicalID and CoreID must be set to identify unique physical cores.
3538
cpuInfo: func(context.Context) ([]cpu.InfoStat, error) {
3639
return []cpu.InfoStat{
37-
{
38-
ModelName: "testmodelname",
39-
Cores: 4,
40-
},
41-
{
42-
ModelName: "testmodelname2",
43-
Cores: 4,
44-
},
40+
{ModelName: "testmodelname", Cores: 1, PhysicalID: "0", CoreID: "0"},
41+
{ModelName: "testmodelname", Cores: 1, PhysicalID: "0", CoreID: "0"}, // hyperthreading sibling
42+
{ModelName: "testmodelname", Cores: 1, PhysicalID: "0", CoreID: "1"},
43+
{ModelName: "testmodelname", Cores: 1, PhysicalID: "0", CoreID: "1"}, // hyperthreading sibling
44+
{ModelName: "testmodelname2", Cores: 1, PhysicalID: "1", CoreID: "0"},
45+
{ModelName: "testmodelname2", Cores: 1, PhysicalID: "1", CoreID: "0"}, // hyperthreading sibling
46+
{ModelName: "testmodelname2", Cores: 1, PhysicalID: "1", CoreID: "1"},
47+
{ModelName: "testmodelname2", Cores: 1, PhysicalID: "1", CoreID: "1"}, // hyperthreading sibling
4548
}, nil
4649
},
47-
cpuCounts: func(context.Context, bool) (int, error) {
48-
return 2, nil
50+
cpuCounts: func(_ context.Context, logical bool) (int, error) {
51+
if logical {
52+
return 8, nil // 2 sockets * 2 cores * 2 threads
53+
}
54+
return 2, nil // 2 physical sockets
4955
},
5056
},
5157
wantInfo: map[string]string{
5258
"host_physical_cpus": "2",
53-
"host_cpu_cores": "8",
59+
"host_cpu_cores": "4",
5460
"host_cpu_model": "testmodelname2",
55-
"host_logical_cpus": "2",
61+
"host_logical_cpus": "8",
5662
},
5763
},
5864
{
@@ -90,7 +96,7 @@ func TestGetCPU(t *testing.T) {
9096
},
9197
},
9298
wantInfo: map[string]string{
93-
"host_physical_cpus": "2",
99+
"host_physical_cpus": "0", // cpuCounts(false) fails, HostPhysicalCPUs stays at zero
94100
"host_cpu_cores": "0",
95101
"host_cpu_model": "",
96102
"host_logical_cpus": "0",

exporter/signalfxexporter/internal/hostmetadata/metadata_test.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ func TestSyncMetadata(t *testing.T) {
4141
{
4242
name: "all_stats_available",
4343
cpuStat: cpu.InfoStat{
44-
Cores: 4,
45-
ModelName: "testprocessor",
44+
Cores: 1,
45+
ModelName: "testprocessor",
46+
PhysicalID: "0",
47+
CoreID: "0",
4648
},
4749
cpuStatErr: nil,
4850
memStat: mem.VirtualMemoryStat{
@@ -63,7 +65,7 @@ func TestSyncMetadata(t *testing.T) {
6365
ResourceID: "host1",
6466
MetadataDelta: metadata.MetadataDelta{
6567
MetadataToUpdate: map[string]string{
66-
"host_cpu_cores": "4",
68+
"host_cpu_cores": "1",
6769
"host_cpu_model": "testprocessor",
6870
"host_logical_cpus": "1",
6971
"host_physical_cpus": "1",
@@ -84,8 +86,10 @@ func TestSyncMetadata(t *testing.T) {
8486
{
8587
name: "no_host_stats",
8688
cpuStat: cpu.InfoStat{
87-
Cores: 4,
88-
ModelName: "testprocessor",
89+
Cores: 1,
90+
ModelName: "testprocessor",
91+
PhysicalID: "0",
92+
CoreID: "0",
8993
},
9094
cpuStatErr: nil,
9195
memStat: mem.VirtualMemoryStat{
@@ -102,7 +106,7 @@ func TestSyncMetadata(t *testing.T) {
102106
ResourceID: "host1",
103107
MetadataDelta: metadata.MetadataDelta{
104108
MetadataToUpdate: map[string]string{
105-
"host_cpu_cores": "4",
109+
"host_cpu_cores": "1",
106110
"host_cpu_model": "testprocessor",
107111
"host_logical_cpus": "1",
108112
"host_physical_cpus": "1",

0 commit comments

Comments
 (0)