Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions src/hotspot/share/prims/jvmti.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10522,6 +10522,12 @@ myInit() {
thread CPU time
</description>
</capabilityfield>
<capabilityfield id="can_get_gc_cpu_time" since="26">
<description>
Can <functionlink id="GetTotalGCCpuTime">get</functionlink>
GC CPU time
</description>
</capabilityfield>
<capabilityfield id="can_generate_method_entry_events"
disp1="can_generate" disp2="_method_entry_events"
>
Expand Down Expand Up @@ -11225,6 +11231,68 @@ myInit() {
</errors>
</function>

<function id="GetGCCpuTimerInfo" phase="any" callbacksafe="safe" num="157" since="26">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be called GetTotalGCCpuTimerInfo?

<synopsis>Get Total GC Cpu Timer Information</synopsis>
<description>
Get information about the
<functionlink id="GetTotalGCCpuTime"/> timer.
The fields of the <datalink id="jvmtiTimerInfo"/> structure
are filled in with details about the timer.
This information will not change during a particular invocation of the VM.
</description>
<origin>new</origin>
<capabilities>
<required id="can_get_gc_cpu_time">
Can get GC cpu time.
</required>
</capabilities>
<parameters>
<param id="info_ptr">
<outptr><struct>jvmtiTimerInfo</struct></outptr>
<description>
On return, filled with information describing the time
returned by <functionlink id="GetTotalGCCpuTime"/>.
</description>
</param>
</parameters>
<errors>
</errors>
</function>

<function id="GetTotalGCCpuTime" phase="any" callbacksafe="safe" num="158" since="26">
<synopsis>Get Total GC CPU Time</synopsis>
<description>
Return the current value of the total GC CPU timer, in nanoseconds.
<p/>
The value returned represents nanoseconds since some fixed but
arbitrary time (perhaps in the future, so values may be
negative). This function provides nanosecond precision, but not
necessarily nanosecond accuracy. No guarantees are made about
how frequently values change.
<p/>
Get information about this timer with
<functionlink id="GetGCCpuTimerInfo"/>.
</description>
<origin>new</origin>
<capabilities>
<required id="can_get_gc_cpu_time">
Can get GC cpu time.
</required>
</capabilities>
<parameters>
<param id="nanos_ptr">
<outptr><jlong/></outptr>
<description>
On return, points to the time in nanoseconds.
This is an unsigned value. If tested or printed as a jlong (signed value)
it may appear to be a negative number.
Comment on lines +11281 to +11282
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to contradict the description that it could be relative to a value in the future and hence negative - thus it can't be "unsigned". But then I don't see how it can be as described - for this timer to be useful it must be tracking nanoseconds of CPU time consumed by GC since CPU time tracking commenced, sometime after VM startup. This has to be similar to how thread CPU time is defined.

</description>
</param>
</parameters>
<errors>
</errors>
</function>

<function id="GetAvailableProcessors" phase="any" num="144">
<synopsis>Get Available Processors</synopsis>
<description>
Expand Down Expand Up @@ -15470,6 +15538,11 @@ typedef void (JNICALL *jvmtiEventVMInit)
<change date="10 January 2025" version="25.0.0">
Add new function ClearAllFramePops. Needed to speedup debugger single stepping.
</change>
<change date="30 October 2025" version="26.0.0">
Provide GC CPU time details.
Add new functions: GetGCCpuTimerInfo and GetTotalGCCpuTime.
Add new capability: can_get_gc_cpu_time.
</change>
</changehistory>

</specification>
Expand Down
25 changes: 25 additions & 0 deletions src/hotspot/share/prims/jvmtiEnv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
#include "runtime/timerTrace.hpp"
#include "runtime/vframe.inline.hpp"
#include "runtime/vmThread.hpp"
#include "services/cpuTimeUsage.hpp"
#include "services/threadService.hpp"
#include "utilities/exceptions.hpp"
#include "utilities/preserveException.hpp"
Expand Down Expand Up @@ -3785,6 +3786,30 @@ JvmtiEnv::GetTime(jlong* nanos_ptr) {
} /* end GetTime */


// info_ptr - pre-checked for null
jvmtiError
JvmtiEnv::GetGCCpuTimerInfo(jvmtiTimerInfo* info_ptr) {
os::thread_cpu_time_info(info_ptr);
return JVMTI_ERROR_NONE;
} /* end GetGCCpuTimerInfo */


// nanos_ptr - pre-checked for null
jvmtiError
JvmtiEnv::GetTotalGCCpuTime(jlong* nanos_ptr) {
{
MutexLocker hl(Heap_lock);
if (!os::is_thread_cpu_time_supported() ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it isn't supported then you can't have the capability and so won't reach here.

Universe::heap()->is_shutting_down()) {
*nanos_ptr = -1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems wrong to me and violates the timer-info spec of this timer not jumping backwards. I think you have to cache the last returned value for this function and if you cannot calculate an updated value because of VM shutdown, then that previous value should be returned.

}
*nanos_ptr = CPUTimeUsage::GC::total();
}
return JVMTI_ERROR_NONE;
} /* end GetTotalGCCpuTime */



// processor_count_ptr - pre-checked for null
jvmtiError
JvmtiEnv::GetAvailableProcessors(jint* processor_count_ptr) {
Expand Down
3 changes: 2 additions & 1 deletion src/hotspot/share/prims/jvmtiH.xsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.

This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -117,6 +117,7 @@ enum {
JVMTI_VERSION_11 = 0x300B0000,
JVMTI_VERSION_19 = 0x30130000,
JVMTI_VERSION_21 = 0x30150000,
JVMTI_VERSION_25 = 0x30170000,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be JVMTI_VERSION_26 if this is targeted for version 26?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the hex value represents JDK 23 not 25.


JVMTI_VERSION = 0x30000000 + (</xsl:text>
<xsl:value-of select="$majorversion"/>
Expand Down
3 changes: 3 additions & 0 deletions src/hotspot/share/prims/jvmtiManageCapabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ jvmtiCapabilities JvmtiManageCapabilities::init_always_capabilities() {
if (os::is_thread_cpu_time_supported()) {
jc.can_get_current_thread_cpu_time = 1;
jc.can_get_thread_cpu_time = 1;
jc.can_get_gc_cpu_time = 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering, would a user trying to call GetTotalGCCpuTime if can_get_gc_cpu_time is not successfully set to 1 be undefined behavior? The specs say "To possess a capability, the agent must add the capability (https://docs.oracle.com/en/java/javase/25/docs/specs/jvmti.html#capability). If yes maybe we can discard the extra call to os::is_thread_cpu_time_supported in JvmtiEnv::GetTotalGCCpuTime? That seems to align with the pattern to not have that check in the other methods as you pointed out.

}
jc.can_redefine_classes = 1;
jc.can_redefine_any_class = 1;
Expand Down Expand Up @@ -461,6 +462,8 @@ void JvmtiManageCapabilities:: print(const jvmtiCapabilities* cap) {
log_trace(jvmti)("can_get_current_thread_cpu_time");
if (cap->can_get_thread_cpu_time)
log_trace(jvmti)("can_get_thread_cpu_time");
if (cap->can_get_gc_cpu_time)
log_trace(jvmti)("can_get_gc_cpu_time");
if (cap->can_generate_method_entry_events)
log_trace(jvmti)("can_generate_method_entry_events");
if (cap->can_generate_method_exit_events)
Expand Down