1 /* 2 * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "precompiled.hpp" 26 #include "cds/methodProfiler.hpp" 27 #include "code/codeBlob.hpp" 28 #include "code/nmethod.hpp" 29 #include "logging/log.hpp" 30 #include "runtime/frame.inline.hpp" 31 #include "runtime/handshake.hpp" 32 #include "runtime/javaThread.hpp" 33 #include "runtime/registerMap.hpp" 34 #include "runtime/task.hpp" 35 36 volatile uint64_t MethodProfiler::_num_samples = 0; 37 class MethodProfilerTask : public PeriodicTask { 38 public: 39 MethodProfilerTask() 40 : PeriodicTask(RecordOptCompilationOrderInterval) {} 41 42 void task() { 43 MethodProfiler::tick(); 44 } 45 }; 46 47 static MethodProfilerTask* _task; 48 49 void MethodProfiler::initialize() { 50 if (RecordOptCompilationOrder) { 51 _task = new MethodProfilerTask(); 52 _task->enroll(); 53 } 54 } 55 56 class MethodProfilerClosure : public HandshakeClosure { 57 public: 58 MethodProfilerClosure() 59 : HandshakeClosure("MethodProfiler") {} 60 61 void do_thread(Thread* thread) { 62 ResourceMark rm; 63 64 if (thread != Thread::current()) { 65 // Run by the VM thread - implication is that the target 66 // thread was blocked or in native, i.e. not executing 67 // Java code. 68 return; 69 } 70 71 JavaThread* jt = JavaThread::cast(thread); 72 if (!jt->has_last_Java_frame()) { 73 return; 74 } 75 76 frame fr = jt->last_frame(); 77 if (fr.is_safepoint_blob_frame()) { 78 RegisterMap rm(jt, 79 RegisterMap::UpdateMap::skip, 80 RegisterMap::ProcessFrames::skip, 81 RegisterMap::WalkContinuation::skip); 82 fr = fr.sender(&rm); 83 } 84 85 if (!fr.is_compiled_frame()) { 86 return; 87 } 88 89 nmethod* nm = fr.cb()->as_nmethod(); 90 if (!nm->is_compiled_by_c2()) { 91 return; 92 } 93 94 log_trace(cds, profiling)("%s sampled %s::%s: " UINT64_FORMAT, 95 thread->name(), 96 nm->method()->method_holder()->name()->as_C_string(), 97 nm->method()->name()->as_C_string(), 98 nm->method_profiling_count()); 99 100 // Found a C2 top frame that was just executing - sample it 101 nm->inc_method_profiling_count(); 102 Atomic::inc(&MethodProfiler::_num_samples); 103 } 104 }; 105 106 void MethodProfiler::tick() { 107 MethodProfilerClosure cl; 108 Handshake::execute(&cl); 109 } 110 111 static int method_hotness_compare(nmethod** a, nmethod** b) { 112 return (*b)->method_profiling_count() - (*a)->method_profiling_count(); 113 } 114 115 116 GrowableArrayCHeap<nmethod*, mtClassShared>* MethodProfiler::sampled_nmethods() { 117 GrowableArrayCHeap<nmethod*, mtClassShared>* nmethods = new GrowableArrayCHeap<nmethod*, mtClassShared>(); 118 119 { 120 MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); 121 NMethodIterator iter(NMethodIterator::not_unloading); 122 while(iter.next()) { 123 nmethod* nm = iter.method(); 124 if (nm->is_compiled_by_c2() || nm->is_compiled_by_jvmci()) { 125 nmethods->append(nm); 126 } 127 } 128 } 129 130 nmethods->sort(&method_hotness_compare); 131 132 return nmethods; 133 } 134 135 double MethodProfiler::hotness(nmethod* nm) { 136 return double(nm->method_profiling_count()) / double(_num_samples) * 100.0; 137 } 138 139 uint64_t MethodProfiler::num_samples() { 140 return _num_samples; 141 } 142 143 void MethodProfiler::process_method_hotness() { 144 if (_num_samples == 0) { 145 return; 146 } 147 148 _task->disenroll(); 149 150 LogTarget(Debug, cds, profiling) lt; 151 if (lt.is_enabled()) { 152 ResourceMark rm; 153 double accumulated_sample_percent = 0.0; 154 int i = 0; 155 GrowableArrayCHeap<nmethod*, mtClassShared>* nmethods = sampled_nmethods(); 156 157 for (auto it = nmethods->begin(); it != nmethods->end(); ++it) { 158 ++i; 159 nmethod* nm = *it; 160 if (nm->method_profiling_count() == 0) { 161 break; 162 } 163 double sample_percent = hotness(nm); 164 accumulated_sample_percent += sample_percent; 165 log_info(cds, profiling)("%d (%.2f). %s::%s: " UINT64_FORMAT " (%.2f%%, %.2f%% accumulated)", 166 i, 167 double(i) / double(nmethods->length()) * 100.0, 168 nm->method()->method_holder()->name()->as_C_string(), 169 nm->method()->name()->as_C_string(), 170 nm->method_profiling_count(), 171 sample_percent, 172 accumulated_sample_percent); 173 } 174 175 delete nmethods; 176 } 177 }