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