| /* | |
| * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * Copyright (c) 2023 Google LLC. 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 | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| * | |
| */ | |
| #include "runtime/cpuTimeCounters.hpp" | |
| #include "runtime/atomic.hpp" | |
| const char* CPUTimeGroups::to_string(CPUTimeType val) { | |
| switch (val) { | |
| case CPUTimeType::gc_total: | |
| return "total_gc_cpu_time"; | |
| case CPUTimeType::gc_parallel_workers: | |
| return "gc_parallel_workers"; | |
| case CPUTimeType::gc_conc_mark: | |
| return "gc_conc_mark"; | |
| case CPUTimeType::gc_conc_refine: | |
| return "gc_conc_refine"; | |
| case CPUTimeType::gc_service: | |
| return "gc_service"; | |
| case CPUTimeType::vm: | |
| return "vm"; | |
| case CPUTimeType::conc_dedup: | |
| return "conc_dedup"; | |
| default: | |
| ShouldNotReachHere(); | |
| return ""; | |
| }; | |
| } | |
| bool CPUTimeGroups::is_gc_counter(CPUTimeType val) { | |
| switch (val) { | |
| case CPUTimeType::gc_parallel_workers: | |
| case CPUTimeType::gc_conc_mark: | |
| case CPUTimeType::gc_conc_refine: | |
| case CPUTimeType::gc_service: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| ShouldNotReachHere(); | |
| } | |
| CPUTimeCounters* CPUTimeCounters::_instance = nullptr; | |
| CPUTimeCounters::CPUTimeCounters() : | |
| _cpu_time_counters(), | |
| _gc_total_cpu_time_diff(0) { | |
| } | |
| void CPUTimeCounters::inc_gc_total_cpu_time(jlong diff) { | |
| CPUTimeCounters* instance = CPUTimeCounters::get_instance(); | |
| Atomic::add(&(instance->_gc_total_cpu_time_diff), diff); | |
| } | |
| void CPUTimeCounters::publish_gc_total_cpu_time() { | |
| CPUTimeCounters* instance = CPUTimeCounters::get_instance(); | |
| // Ensure that we are only incrementing atomically by using Atomic::cmpxchg | |
| // to set the value to zero after we obtain the new CPU time difference. | |
| jlong old_value; | |
| jlong fetched_value = Atomic::load(&(instance->_gc_total_cpu_time_diff)); | |
| jlong new_value = 0; | |
| do { | |
| old_value = fetched_value; | |
| fetched_value = Atomic::cmpxchg(&(instance->_gc_total_cpu_time_diff), old_value, new_value); | |
| } while (old_value != fetched_value); | |
| get_counter(CPUTimeGroups::CPUTimeType::gc_total)->inc(fetched_value); | |
| } | |
| void CPUTimeCounters::create_counter(CounterNS ns, CPUTimeGroups::CPUTimeType name) { | |
| if (UsePerfData && os::is_thread_cpu_time_supported()) { | |
| EXCEPTION_MARK; | |
| CPUTimeCounters* instance = CPUTimeCounters::get_instance(); | |
| instance->_cpu_time_counters[static_cast<int>(name)] = | |
| PerfDataManager::create_counter(ns, CPUTimeGroups::to_string(name), | |
| PerfData::U_Ticks, CHECK); | |
| } | |
| } | |
| void CPUTimeCounters::create_counter(CPUTimeGroups::CPUTimeType group) { | |
| CPUTimeCounters::create_counter(SUN_THREADS_CPUTIME, group); | |
| } | |
| PerfCounter* CPUTimeCounters::get_counter(CPUTimeGroups::CPUTimeType name) { | |
| return CPUTimeCounters::get_instance()->_cpu_time_counters[static_cast<int>(name)]; | |
| } | |
| void CPUTimeCounters::update_counter(CPUTimeGroups::CPUTimeType name, jlong total) { | |
| CPUTimeCounters* instance = CPUTimeCounters::get_instance(); | |
| PerfCounter* counter = instance->get_counter(name); | |
| jlong prev_value = counter->get_value(); | |
| jlong net_cpu_time = total - prev_value; | |
| counter->inc(net_cpu_time); | |
| if (CPUTimeGroups::is_gc_counter(name)) { | |
| instance->inc_gc_total_cpu_time(net_cpu_time); | |
| } | |
| } | |
| ThreadTotalCPUTimeClosure::~ThreadTotalCPUTimeClosure() { | |
| CPUTimeCounters::update_counter(_name, _total); | |
| } | |
| void ThreadTotalCPUTimeClosure::do_thread(Thread* thread) { | |
| // The default code path (fast_thread_cpu_time()) asserts that | |
| // pthread_getcpuclockid() and clock_gettime() must return 0. Thus caller | |
| // must ensure the thread exists and has not terminated. | |
| _total += os::thread_cpu_time(thread); | |
| } |