-
Notifications
You must be signed in to change notification settings - Fork 937
Add CPUTimeSample event support to jfrconv #1475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
0d7636f
to
673745e
Compare
673745e
to
39fa108
Compare
} catch (IllegalArgumentException e) { | ||
f.set(this, CpuSampleType.ExecutionSample); | ||
|
||
// Next arg was not a valid value for the enum | ||
if (hasNextArg) { | ||
i--; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a hack. Let's not change behavior of an existing option.
I think --cpu
can be a "smart" option that automatically detects what event to use. To force using a specific event, there can be a separate option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We won't need this with --cpu-time
. As for making --cpu
a smart option, leaving that out of the current PR.
|
||
public enum ThreadStateFilter { | ||
Wall, Cpu, CpuTime, Default | ||
} No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whitespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, didn't we have a linter for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's clang-format which is different from clang-tidy
threadStates = getThreadStates(false); | ||
threadStates = getThreadStates(ThreadStateFilter.Wall); | ||
} else { | ||
threadStates = getThreadStates(ThreadStateFilter.Default); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's remove this branch & keep threadStates = null
, I don't see why it would be needed
You can do
!(event instanceof ExecutionSample) || threadStates == null || threadStates.get(((ExecutionSample) event).threadState)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, threadStates == null
was an intentional micro-optimization for the common case when no filtering is needed.
BTW, threadStates == null
can also substitute instanceof
check: just don't set threadStates
for event classes other than ExecutionSample
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently if you do bad usages such as --alloc --cpu
it would exit with a casting exception
The correct solution IMO would adding a validation to prevent such cases
The simplest would be throwing a clear exception when the threadStates aren't null & the sample class is anything other than ExecutionSample
for (Event event; (event = jfr.readEvent(eventClass)) != null; ) { | ||
if (event.time >= startTicks && event.time <= endTicks) { | ||
if (threadStates == null || threadStates.get(((ExecutionSample) event).threadState)) { | ||
if (!(event instanceof ExecutionSample) || threadStates.get(((ExecutionSample) event).threadState)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah this was a risky code :)
If you do --alloc --cpu
it would have failed previously
threadStates = getThreadStates(false); | ||
threadStates = getThreadStates(ThreadStateFilter.Wall); | ||
} else { | ||
threadStates = getThreadStates(ThreadStateFilter.Default); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, threadStates == null
was an intentional micro-optimization for the common case when no filtering is needed.
BTW, threadStates == null
can also substitute instanceof
check: just don't set threadStates
for event classes other than ExecutionSample
.
} | ||
|
||
protected BitSet getThreadStates(boolean cpu) { | ||
protected BitSet getThreadStates(ThreadStateFilter filter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused about this extra enum and all the related complications.
Will it work if you just add if (args.cpuTime)
branch above and leave this method intact?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to make the distinction between cpu
and cpuTime
and lack of both thereof, boolean
wasn't cutting it.
Since now we are parsing CPUTimeSample
s as ExecutionSample
s, we need to exclude CPUTimeSample
s with getThreadState
if mode is cpuTime
. Both sample types can be in the jfr at the same time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, you don't need to call this function for cpuTime
- that's the point.
Your ThreadStateFilter
serves as unnecessary bridge to connect two pieces of logic that should not be separate in the first place.
E.g., instead of
if (a) {
filter = F1;
} else if (b) {
filter = F2;
} else if (c) {
filter = F3;
}
if (filter == F1) {
// do_A
} else if (filter == F2) {
// do_B
} else if (filter == F3) {
// do_C
}
you could just write
if (a) {
// do_A
} else if (b) {
// do_B
} else if (c) {
// do_C
}
package one.jfr.event; | ||
|
||
public class ExecutionSample extends Event { | ||
public static final int CpuTimeSample = 254; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add some context behind why this value was selected,
It does look out of place when first checking the code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added comment
This PR adds support for the
jdk.CPUTimeSample
event, an experimental feature introduced in OpenJDK 25.This new mode provides a more accurate CPU profiling alternative to the default
ExecutionSample
in the JDK. It uses thread CPU time, which is particularly useful for analyzing CPU-bound applications.Motivation and context
CPUTimeSample
event support was added to OpenJDK in openjdk/jdk#25654, we can extend the JFR converter to support this new event type.How has this been tested?
By manually generating flamegraphs and collapsed output.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.