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 "cds/methodProfiler.hpp" 26 #include "code/codeBlob.hpp" 27 #include "code/nmethod.hpp" 28 #include "logging/log.hpp" 29 #include "runtime/frame.inline.hpp" 30 #include "runtime/handshake.hpp" 31 #include "runtime/javaThread.hpp" 32 #include "runtime/registerMap.hpp" 33 #include "runtime/task.hpp" 34 35 volatile uint64_t MethodProfiler::_num_samples = 0; 36 class MethodProfilerTask : public PeriodicTask { 37 public: 38 MethodProfilerTask() 39 : PeriodicTask(RecordOptCompilationOrderInterval) {} 40 41 void task() { 42 MethodProfiler::tick(); 43 } 44 }; 45 46 static MethodProfilerTask* _task; 47 48 void MethodProfiler::initialize() { 49 if (RecordOptCompilationOrder) { 50 _task = new MethodProfilerTask(); 51 _task->enroll(); 52 } 53 } 54 55 class MethodProfilerClosure : public HandshakeClosure { 56 public: 57 MethodProfilerClosure() 58 : HandshakeClosure("MethodProfiler") {} 59 60 void do_thread(Thread* thread) { 61 ResourceMark rm; 62 63 if (thread != Thread::current()) { 64 // Run by the VM thread - implication is that the target 65 // thread was blocked or in native, i.e. not executing 66 // Java code. 67 return; 68 } 69 70 JavaThread* jt = JavaThread::cast(thread); 71 if (!jt->has_last_Java_frame()) { 72 return; 73 } 74 75 frame fr = jt->last_frame(); 76 if (fr.is_safepoint_blob_frame()) { 77 RegisterMap rm(jt, 78 RegisterMap::UpdateMap::skip, 79 RegisterMap::ProcessFrames::skip, 80 RegisterMap::WalkContinuation::skip); 81 fr = fr.sender(&rm); 82 } 83 84 if (!fr.is_compiled_frame()) { 85 return; 86 } 87 88 nmethod* nm = fr.cb()->as_nmethod(); 89 if (!nm->is_compiled_by_c2()) { 90 return; 91 } 92 93 log_trace(cds, profiling)("%s sampled %s::%s: " UINT64_FORMAT, 94 thread->name(), 95 nm->method()->method_holder()->name()->as_C_string(), 96 nm->method()->name()->as_C_string(), 97 nm->method_profiling_count()); 98 99 // Found a C2 top frame that was just executing - sample it 100 nm->inc_method_profiling_count(); 101 Atomic::inc(&MethodProfiler::_num_samples); 102 } 103 }; 104 105 void MethodProfiler::tick() { 106 MethodProfilerClosure cl; 107 Handshake::execute(&cl); 108 } 109 110 static int method_hotness_compare(nmethod** a, nmethod** b) { 111 return (*b)->method_profiling_count() - (*a)->method_profiling_count(); 112 } 113 114 115 GrowableArrayCHeap<nmethod*, mtClassShared>* MethodProfiler::sampled_nmethods() { 116 GrowableArrayCHeap<nmethod*, mtClassShared>* nmethods = new GrowableArrayCHeap<nmethod*, mtClassShared>(); 117 118 { 119 MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); 120 NMethodIterator iter(NMethodIterator::not_unloading); 121 while(iter.next()) { 122 nmethod* nm = iter.method(); 123 if (nm->is_compiled_by_c2() || nm->is_compiled_by_jvmci()) { 124 nmethods->append(nm); 125 } 126 } 127 } 128 129 nmethods->sort(&method_hotness_compare); 130 131 return nmethods; 132 } 133 134 double MethodProfiler::hotness(nmethod* nm) { 135 return double(nm->method_profiling_count()) / double(_num_samples) * 100.0; 136 } 137 138 uint64_t MethodProfiler::num_samples() { 139 return _num_samples; 140 } 141 142 void MethodProfiler::process_method_hotness() { 143 if (_num_samples == 0) { 144 return; 145 } 146 147 _task->disenroll(); 148 149 LogTarget(Debug, cds, profiling) lt; 150 if (lt.is_enabled()) { 151 ResourceMark rm; 152 double accumulated_sample_percent = 0.0; 153 int i = 0; 154 GrowableArrayCHeap<nmethod*, mtClassShared>* nmethods = sampled_nmethods(); 155 156 for (auto it = nmethods->begin(); it != nmethods->end(); ++it) { 157 ++i; 158 nmethod* nm = *it; 159 if (nm->method_profiling_count() == 0) { 160 break; 161 } 162 double sample_percent = hotness(nm); 163 accumulated_sample_percent += sample_percent; 164 log_info(cds, profiling)("%d (%.2f). %s::%s: " UINT64_FORMAT " (%.2f%%, %.2f%% accumulated)", 165 i, 166 double(i) / double(nmethods->length()) * 100.0, 167 nm->method()->method_holder()->name()->as_C_string(), 168 nm->method()->name()->as_C_string(), 169 nm->method_profiling_count(), 170 sample_percent, 171 accumulated_sample_percent); 172 } 173 174 delete nmethods; 175 } 176 }