Conversation
SuperQ
left a comment
There was a problem hiding this comment.
Interesting, thanks, it seems mostly reasonable.
Please sign off your commits with git commit -s --amend. Also, it would be good to add an [ENHANCEMENT] entry to the changelog.
50e67a7 to
c6a436b
Compare
`runtime.NumCPU()` returns the number of CPUs that the process can run on. This number does not necessarily correlate to CPU ids if the affinity mask of the process is set. This change maintains the current behavior as default, but also allows the user to specify a range of CPUids to use instead. The CPU id is stored as the value of a map keyed on the profiler object's address. Signed-off-by: Joe Damato <jdamato@fastly.com>
c6a436b to
b17a872
Compare
|
Thanks for the review @SuperQ. Made some changes as you've suggested. Let me know how this looks. |
|
This is a nice config option! |
Signed-off-by: Joe Damato <jdamato@fastly.com>
f0bb354 to
27381c4
Compare
|
I think this addresses your feedback @discordianfish and @SuperQ. Was wondering if there might be anyone who would be interested in testing this just to double check that this is working for them? cc @hodgesds |
Signed-off-by: Joe Damato <jdamato@fastly.com>
c772e25 to
341322e
Compare
collector/perf_linux.go
Outdated
| for cpu, profiler := range c.perfSwProfilers { | ||
| cpuStr := fmt.Sprintf("%d", cpu) | ||
| for _, profiler := range c.perfSwProfilers { | ||
| cpuid := c.swProfilerCpuMap[&profiler] |
There was a problem hiding this comment.
So, I'm new to go and this appears to be a bug.
&profiler here refers to the address of the profiler object itself and not the address of the perf.SoftwareProfiler that was inserted into c.swProfilerCpuMap.
This leads to the incorrect CPU id being retrieved. I'm not really sure how to deal with this as I know basically 0 golang (sorry). Would love any suggestions you folks have on the best way to fix this.
Signed-off-by: Joe Damato <jdamato@fastly.com>
57bc58c to
573cf02
Compare
|
I don't really know anything about golang but this commit I just pushed: 573cf02 I think fixes the address of the object issue I mentioned here: #1518 (comment) |
| perfSwProfilers map[int]perf.SoftwareProfiler | ||
| perfCacheProfilers map[int]perf.CacheProfiler | ||
| desc map[string]*prometheus.Desc | ||
| hwProfilerCpuMap map[*perf.HardwareProfiler]int |
There was a problem hiding this comment.
I wouldn't expect to keep a map of pointers to interfaces. For example NewSoftwareProfiler returns an interface that can be used directly.
There was a problem hiding this comment.
yea but I think you need them otherwise you can't generate a map of pointers to CPU ids.
There was a problem hiding this comment.
confirmed that this change (pointers to interfaces) works correctly, as the address pointed to can be used as a key for the CPU ID map. the code without this change does not work as mentioned here: #1518 (comment)
I think keeping this is correct -- I am using this in a lab setting and am getting the expected results now.
|
It might be nice to make the flag match the |
That sounds like a lot of work. Happy to turn that over to anyone else who'd like to jump in on this, though. |
Signed-off-by: Joe Damato <jdamato@fastly.com>
cf393a4 to
b813bf1
Compare
I think this roughly does what you want, let me know your thoughts. |
I left a few comments on the code over there. As mentioned above, I'm not a golang programmer so I'm probably not a good person to ask for a code review 😅 FWIW I am using the code I wrote in this branch in a lab setting and it is working as expected (as mentioned here: #1518 (comment)). In my use case: I run It's worth noting that any use of |
|
I see the bug now, here's a small example that shows it's because a interface isn't assignable, the index expression isn't valid by taking the address of the interface it works. I think this is fine then, would you mind porting over the flag parsing code so that strides work properly as well? |
Thanks for the detailed explanation. Sure, I can copy over your flag parsing code shortly. |
|
FWIW, worth noting that using the code I wrote in this branch the HardwareProfiler which gathers |
What are you observing?
Note that configuration for those events also includes some defaults that are pretty opinionated. For more configuration and moved much of that functionality into |
On kernel 4.19 with a sandybridge CPU issuing For example, on my system However, running This leads me to believe that either I'm not sure if this bug is related to my code or something else. EDIT : I should note that some CPUs seem to have correct values, but others have values which never seem to change from |
|
I've tested your branch on two kernels (4.14.78 and 5.2.5) on a E3-1505M v5 and Ryzen 2600 processors and both seem to be working as expected. Do you have access to any other architectures for testing? |
|
It appears to work on kernel 4.9 on this machine, but not on kernel 4.19. Could be some weird kernel regression. Any chance you could test on 4.19? |
|
I run custom kernels for everything so my config will likely be different. Can you post the output of: |
|
|
I've tested this on a few machines with a similar config and it seems to be working as expected. I think this is good to go, however you may want to do some more digging ( |
| kingpin "gopkg.in/alecthomas/kingpin.v2" | ||
| "runtime" | ||
| "strconv" | ||
| "strings" |
There was a problem hiding this comment.
Normally we group all the built-in imports at the top, can you re-format these using goimports?
go get golang.org/x/tools/cmd/goimports
goimports -w ./collector/perf_linux.go
|
|
||
| ncpus, err = strconv.Atoi(cpuRange[1]) | ||
| if err != nil { | ||
| ncpus = runtime.NumCPU() - 1 |
There was a problem hiding this comment.
I don't think we should fallback to the default when a user provided a non-integer. This should fail instead.
| cacheProfilerCpuMap: map[*perf.CacheProfiler]int{}, | ||
| } | ||
|
|
||
| start := 0 |
There was a problem hiding this comment.
Use a var block here:
var (
start = 0
ncpus = 0
..
)
| // man perf_event_open | ||
| collector.perfHwProfilers[i] = perf.NewHardwareProfiler(-1, i) | ||
| if err := collector.perfHwProfilers[i].Start(); err != nil { | ||
| p := perf.NewHardwareProfiler(-1, i) |
There was a problem hiding this comment.
Let's rename the p's here:
- ph = hardware profiler
- ps = software profiler
- pc = cache profiler
| collector.perfHwProfilers[idx] = &p | ||
| if err := p.Start(); err != nil { | ||
| return collector, err | ||
| } else { |
There was a problem hiding this comment.
No need for the else here, if err != nil it returns anyway.
|
Should we close this in favor of #1561? |
|
Superseded by #1561. |
runtime.NumCPU()returns the number of CPUs that the process can runon. This number does not necessarily correlate to CPU ids if the
affinity mask of the process is set.
This change maintains the current behavior as default, but also allows
the user to specify a range of CPUids to use instead.
The CPU id is stored as the value of a map keyed on the profiler
object's address.