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/cds_globals.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(AOTRecordOptCompilationOrderInterval) {}
41
42 void task() {
43 MethodProfiler::tick();
44 }
45 };
46
47 static MethodProfilerTask* _task;
48
49 void MethodProfiler::initialize() {
50 if (AOTRecordOptCompilationOrder) {
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 AtomicAccess::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 }