Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
67 changes: 67 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,62 @@ 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/>
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 +15532,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
24 changes: 24 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,29 @@ 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 (Universe::heap()->is_shutting_down()) {
*nanos_ptr = -1;
}
*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
4 changes: 3 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,8 @@ enum {
JVMTI_VERSION_11 = 0x300B0000,
JVMTI_VERSION_19 = 0x30130000,
JVMTI_VERSION_21 = 0x30150000,
JVMTI_VERSION_25 = 0x30190000,
JVMTI_VERSION_26 = 0x301A0000,

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