File: | jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp |
Warning: | line 602, column 3 Returning null reference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * Copyright (c) 2012, 2021, 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 "jfr/jfrEvents.hpp" | |||
27 | #include "jfr/recorder/jfrRecorder.hpp" | |||
28 | #include "jfr/periodic/sampling/jfrCallTrace.hpp" | |||
29 | #include "jfr/periodic/sampling/jfrThreadSampler.hpp" | |||
30 | #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp" | |||
31 | #include "jfr/recorder/service/jfrOptionSet.hpp" | |||
32 | #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" | |||
33 | #include "jfr/recorder/storage/jfrBuffer.hpp" | |||
34 | #include "jfr/support/jfrThreadId.hpp" | |||
35 | #include "jfr/support/jfrThreadLocal.hpp" | |||
36 | #include "jfr/utilities/jfrTime.hpp" | |||
37 | #include "jfrfiles/jfrEventClasses.hpp" | |||
38 | #include "logging/log.hpp" | |||
39 | #include "runtime/frame.inline.hpp" | |||
40 | #include "runtime/os.hpp" | |||
41 | #include "runtime/semaphore.hpp" | |||
42 | #include "runtime/thread.inline.hpp" | |||
43 | #include "runtime/threadSMR.hpp" | |||
44 | ||||
45 | enum JfrSampleType { | |||
46 | NO_SAMPLE = 0, | |||
47 | JAVA_SAMPLE = 1, | |||
48 | NATIVE_SAMPLE = 2 | |||
49 | }; | |||
50 | ||||
51 | static bool thread_state_in_java(JavaThread* thread) { | |||
52 | assert(thread != NULL, "invariant")do { if (!(thread != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 52, "assert(" "thread != __null" ") failed", "invariant"); :: breakpoint(); } } while (0); | |||
53 | switch(thread->thread_state()) { | |||
54 | case _thread_new: | |||
55 | case _thread_uninitialized: | |||
56 | case _thread_new_trans: | |||
57 | case _thread_in_vm_trans: | |||
58 | case _thread_blocked_trans: | |||
59 | case _thread_in_native_trans: | |||
60 | case _thread_blocked: | |||
61 | case _thread_in_vm: | |||
62 | case _thread_in_native: | |||
63 | case _thread_in_Java_trans: | |||
64 | break; | |||
65 | case _thread_in_Java: | |||
66 | return true; | |||
67 | default: | |||
68 | ShouldNotReachHere()do { (*g_assert_poison) = 'X';; report_should_not_reach_here( "/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 68); ::breakpoint(); } while (0); | |||
69 | break; | |||
70 | } | |||
71 | return false; | |||
72 | } | |||
73 | ||||
74 | static bool thread_state_in_native(JavaThread* thread) { | |||
75 | assert(thread != NULL, "invariant")do { if (!(thread != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 75, "assert(" "thread != __null" ") failed", "invariant"); :: breakpoint(); } } while (0); | |||
76 | switch(thread->thread_state()) { | |||
77 | case _thread_new: | |||
78 | case _thread_uninitialized: | |||
79 | case _thread_new_trans: | |||
80 | case _thread_blocked_trans: | |||
81 | case _thread_blocked: | |||
82 | case _thread_in_vm: | |||
83 | case _thread_in_vm_trans: | |||
84 | case _thread_in_Java_trans: | |||
85 | case _thread_in_Java: | |||
86 | case _thread_in_native_trans: | |||
87 | break; | |||
88 | case _thread_in_native: | |||
89 | return true; | |||
90 | default: | |||
91 | ShouldNotReachHere()do { (*g_assert_poison) = 'X';; report_should_not_reach_here( "/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 91); ::breakpoint(); } while (0); | |||
92 | break; | |||
93 | } | |||
94 | return false; | |||
95 | } | |||
96 | ||||
97 | class JfrThreadSampleClosure { | |||
98 | public: | |||
99 | JfrThreadSampleClosure(EventExecutionSample* events, EventNativeMethodSample* events_native); | |||
100 | ~JfrThreadSampleClosure() {} | |||
101 | EventExecutionSample* next_event() { return &_events[_added_java++]; } | |||
102 | EventNativeMethodSample* next_event_native() { return &_events_native[_added_native++]; } | |||
103 | void commit_events(JfrSampleType type); | |||
104 | bool do_sample_thread(JavaThread* thread, JfrStackFrame* frames, u4 max_frames, JfrSampleType type); | |||
105 | uint java_entries() { return _added_java; } | |||
106 | uint native_entries() { return _added_native; } | |||
107 | ||||
108 | private: | |||
109 | bool sample_thread_in_java(JavaThread* thread, JfrStackFrame* frames, u4 max_frames); | |||
110 | bool sample_thread_in_native(JavaThread* thread, JfrStackFrame* frames, u4 max_frames); | |||
111 | EventExecutionSample* _events; | |||
112 | EventNativeMethodSample* _events_native; | |||
113 | Thread* _self; | |||
114 | uint _added_java; | |||
115 | uint _added_native; | |||
116 | }; | |||
117 | ||||
118 | class OSThreadSampler : public os::SuspendedThreadTask { | |||
119 | public: | |||
120 | OSThreadSampler(JavaThread* thread, | |||
121 | JfrThreadSampleClosure& closure, | |||
122 | JfrStackFrame *frames, | |||
123 | u4 max_frames) : os::SuspendedThreadTask((Thread*)thread), | |||
124 | _success(false), | |||
125 | _thread_oop(thread->threadObj()), | |||
126 | _stacktrace(frames, max_frames), | |||
127 | _closure(closure), | |||
128 | _suspend_time() {} | |||
129 | ||||
130 | void take_sample(); | |||
131 | void do_task(const os::SuspendedThreadTaskContext& context); | |||
132 | void protected_task(const os::SuspendedThreadTaskContext& context); | |||
133 | bool success() const { return _success; } | |||
134 | const JfrStackTrace& stacktrace() const { return _stacktrace; } | |||
135 | ||||
136 | private: | |||
137 | bool _success; | |||
138 | oop _thread_oop; | |||
139 | JfrStackTrace _stacktrace; | |||
140 | JfrThreadSampleClosure& _closure; | |||
141 | JfrTicks _suspend_time; | |||
142 | }; | |||
143 | ||||
144 | class OSThreadSamplerCallback : public os::CrashProtectionCallback { | |||
145 | public: | |||
146 | OSThreadSamplerCallback(OSThreadSampler& sampler, const os::SuspendedThreadTaskContext &context) : | |||
147 | _sampler(sampler), _context(context) { | |||
148 | } | |||
149 | virtual void call() { | |||
150 | _sampler.protected_task(_context); | |||
151 | } | |||
152 | private: | |||
153 | OSThreadSampler& _sampler; | |||
154 | const os::SuspendedThreadTaskContext& _context; | |||
155 | }; | |||
156 | ||||
157 | void OSThreadSampler::do_task(const os::SuspendedThreadTaskContext& context) { | |||
158 | #ifndef ASSERT1 | |||
159 | guarantee(JfrOptionSet::sample_protection(), "Sample Protection should be on in product builds")do { if (!(JfrOptionSet::sample_protection())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 159, "guarantee(" "JfrOptionSet::sample_protection()" ") failed" , "Sample Protection should be on in product builds"); ::breakpoint (); } } while (0); | |||
160 | #endif | |||
161 | assert(_suspend_time.value() == 0, "already timestamped!")do { if (!(_suspend_time.value() == 0)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 161, "assert(" "_suspend_time.value() == 0" ") failed", "already timestamped!" ); ::breakpoint(); } } while (0); | |||
162 | _suspend_time = JfrTicks::now(); | |||
163 | ||||
164 | if (JfrOptionSet::sample_protection()) { | |||
165 | OSThreadSamplerCallback cb(*this, context); | |||
166 | os::ThreadCrashProtection crash_protection; | |||
167 | if (!crash_protection.call(cb)) { | |||
168 | log_error(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("Thread method sampler crashed"); | |||
169 | } | |||
170 | } else { | |||
171 | protected_task(context); | |||
172 | } | |||
173 | } | |||
174 | ||||
175 | /* | |||
176 | * From this method and down the call tree we attempt to protect against crashes | |||
177 | * using a signal handler / __try block. Don't take locks, rely on destructors or | |||
178 | * leave memory (in case of signal / exception) in an inconsistent state. */ | |||
179 | void OSThreadSampler::protected_task(const os::SuspendedThreadTaskContext& context) { | |||
180 | JavaThread* jth = JavaThread::cast(context.thread()); | |||
181 | // Skip sample if we signaled a thread that moved to other state | |||
182 | if (!thread_state_in_java(jth)) { | |||
183 | return; | |||
184 | } | |||
185 | JfrGetCallTrace trace(true, jth); | |||
186 | frame topframe; | |||
187 | if (trace.get_topframe(context.ucontext(), topframe)) { | |||
188 | if (_stacktrace.record_thread(*jth, topframe)) { | |||
189 | /* If we managed to get a topframe and a stacktrace, create an event | |||
190 | * and put it into our array. We can't call Jfr::_stacktraces.add() | |||
191 | * here since it would allocate memory using malloc. Doing so while | |||
192 | * the stopped thread is inside malloc would deadlock. */ | |||
193 | _success = true; | |||
194 | EventExecutionSample *ev = _closure.next_event(); | |||
195 | ev->set_starttime(_suspend_time); | |||
196 | ev->set_endtime(_suspend_time); // fake to not take an end time | |||
197 | ev->set_sampledThread(JFR_THREAD_ID(jth)((jth)->jfr_thread_local()->thread_id())); | |||
198 | ev->set_state(static_cast<u8>(java_lang_Thread::get_thread_status(_thread_oop))); | |||
199 | } | |||
200 | } | |||
201 | } | |||
202 | ||||
203 | void OSThreadSampler::take_sample() { | |||
204 | run(); | |||
205 | } | |||
206 | ||||
207 | class JfrNativeSamplerCallback : public os::CrashProtectionCallback { | |||
208 | public: | |||
209 | JfrNativeSamplerCallback(JfrThreadSampleClosure& closure, JavaThread* jt, JfrStackFrame* frames, u4 max_frames) : | |||
210 | _closure(closure), _jt(jt), _thread_oop(jt->threadObj()), _stacktrace(frames, max_frames), _success(false) { | |||
211 | } | |||
212 | virtual void call(); | |||
213 | bool success() { return _success; } | |||
214 | JfrStackTrace& stacktrace() { return _stacktrace; } | |||
215 | ||||
216 | private: | |||
217 | JfrThreadSampleClosure& _closure; | |||
218 | JavaThread* _jt; | |||
219 | oop _thread_oop; | |||
220 | JfrStackTrace _stacktrace; | |||
221 | bool _success; | |||
222 | }; | |||
223 | ||||
224 | static void write_native_event(JfrThreadSampleClosure& closure, JavaThread* jt, oop thread_oop) { | |||
225 | EventNativeMethodSample *ev = closure.next_event_native(); | |||
226 | ev->set_starttime(JfrTicks::now()); | |||
227 | ev->set_sampledThread(JFR_THREAD_ID(jt)((jt)->jfr_thread_local()->thread_id())); | |||
228 | ev->set_state(static_cast<u8>(java_lang_Thread::get_thread_status(thread_oop))); | |||
229 | } | |||
230 | ||||
231 | void JfrNativeSamplerCallback::call() { | |||
232 | // When a thread is only attach it will be native without a last java frame | |||
233 | if (!_jt->has_last_Java_frame()) { | |||
234 | return; | |||
235 | } | |||
236 | ||||
237 | frame topframe = _jt->last_frame(); | |||
238 | frame first_java_frame; | |||
239 | Method* method = NULL__null; | |||
240 | JfrGetCallTrace gct(false, _jt); | |||
241 | if (!gct.find_top_frame(topframe, &method, first_java_frame)) { | |||
242 | return; | |||
243 | } | |||
244 | if (method == NULL__null) { | |||
245 | return; | |||
246 | } | |||
247 | topframe = first_java_frame; | |||
248 | _success = _stacktrace.record_thread(*_jt, topframe); | |||
249 | if (_success) { | |||
250 | write_native_event(_closure, _jt, _thread_oop); | |||
251 | } | |||
252 | } | |||
253 | ||||
254 | bool JfrThreadSampleClosure::sample_thread_in_java(JavaThread* thread, JfrStackFrame* frames, u4 max_frames) { | |||
255 | OSThreadSampler sampler(thread, *this, frames, max_frames); | |||
256 | sampler.take_sample(); | |||
257 | /* We don't want to allocate any memory using malloc/etc while the thread | |||
258 | * is stopped, so everything is stored in stack allocated memory until this | |||
259 | * point where the thread has been resumed again, if the sampling was a success | |||
260 | * we need to store the stacktrace in the stacktrace repository and update | |||
261 | * the event with the id that was returned. */ | |||
262 | if (!sampler.success()) { | |||
263 | return false; | |||
264 | } | |||
265 | EventExecutionSample *event = &_events[_added_java - 1]; | |||
266 | traceid id = JfrStackTraceRepository::add(sampler.stacktrace()); | |||
267 | assert(id != 0, "Stacktrace id should not be 0")do { if (!(id != 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 267, "assert(" "id != 0" ") failed", "Stacktrace id should not be 0" ); ::breakpoint(); } } while (0); | |||
268 | event->set_stackTrace(id); | |||
269 | return true; | |||
270 | } | |||
271 | ||||
272 | bool JfrThreadSampleClosure::sample_thread_in_native(JavaThread* thread, JfrStackFrame* frames, u4 max_frames) { | |||
273 | JfrNativeSamplerCallback cb(*this, thread, frames, max_frames); | |||
274 | if (JfrOptionSet::sample_protection()) { | |||
275 | os::ThreadCrashProtection crash_protection; | |||
276 | if (!crash_protection.call(cb)) { | |||
277 | log_error(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("Thread method sampler crashed for native"); | |||
278 | } | |||
279 | } else { | |||
280 | cb.call(); | |||
281 | } | |||
282 | if (!cb.success()) { | |||
283 | return false; | |||
284 | } | |||
285 | EventNativeMethodSample *event = &_events_native[_added_native - 1]; | |||
286 | traceid id = JfrStackTraceRepository::add(cb.stacktrace()); | |||
287 | assert(id != 0, "Stacktrace id should not be 0")do { if (!(id != 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 287, "assert(" "id != 0" ") failed", "Stacktrace id should not be 0" ); ::breakpoint(); } } while (0); | |||
288 | event->set_stackTrace(id); | |||
289 | return true; | |||
290 | } | |||
291 | ||||
292 | static const uint MAX_NR_OF_JAVA_SAMPLES = 5; | |||
293 | static const uint MAX_NR_OF_NATIVE_SAMPLES = 1; | |||
294 | ||||
295 | void JfrThreadSampleClosure::commit_events(JfrSampleType type) { | |||
296 | if (JAVA_SAMPLE == type) { | |||
297 | assert(_added_java > 0 && _added_java <= MAX_NR_OF_JAVA_SAMPLES, "invariant")do { if (!(_added_java > 0 && _added_java <= MAX_NR_OF_JAVA_SAMPLES )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 297, "assert(" "_added_java > 0 && _added_java <= MAX_NR_OF_JAVA_SAMPLES" ") failed", "invariant"); ::breakpoint(); } } while (0); | |||
298 | for (uint i = 0; i < _added_java; ++i) { | |||
299 | _events[i].commit(); | |||
300 | } | |||
301 | } else { | |||
302 | assert(NATIVE_SAMPLE == type, "invariant")do { if (!(NATIVE_SAMPLE == type)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 302, "assert(" "NATIVE_SAMPLE == type" ") failed", "invariant" ); ::breakpoint(); } } while (0); | |||
303 | assert(_added_native > 0 && _added_native <= MAX_NR_OF_NATIVE_SAMPLES, "invariant")do { if (!(_added_native > 0 && _added_native <= MAX_NR_OF_NATIVE_SAMPLES)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 303, "assert(" "_added_native > 0 && _added_native <= MAX_NR_OF_NATIVE_SAMPLES" ") failed", "invariant"); ::breakpoint(); } } while (0); | |||
304 | for (uint i = 0; i < _added_native; ++i) { | |||
305 | _events_native[i].commit(); | |||
306 | } | |||
307 | } | |||
308 | } | |||
309 | ||||
310 | JfrThreadSampleClosure::JfrThreadSampleClosure(EventExecutionSample* events, EventNativeMethodSample* events_native) : | |||
311 | _events(events), | |||
312 | _events_native(events_native), | |||
313 | _self(Thread::current()), | |||
314 | _added_java(0), | |||
315 | _added_native(0) { | |||
316 | } | |||
317 | ||||
318 | class JfrThreadSampler : public NonJavaThread { | |||
319 | friend class JfrThreadSampling; | |||
320 | private: | |||
321 | Semaphore _sample; | |||
322 | Thread* _sampler_thread; | |||
323 | JfrStackFrame* const _frames; | |||
324 | JavaThread* _last_thread_java; | |||
325 | JavaThread* _last_thread_native; | |||
326 | size_t _interval_java; | |||
327 | size_t _interval_native; | |||
328 | const size_t _min_size; // for enqueue buffer monitoring | |||
329 | const size_t _renew_size; | |||
330 | int _cur_index; | |||
331 | const u4 _max_frames; | |||
332 | volatile bool _disenrolled; | |||
333 | ||||
334 | const JfrBuffer* get_enqueue_buffer(); | |||
335 | const JfrBuffer* renew_if_full(const JfrBuffer* enqueue_buffer); | |||
336 | ||||
337 | JavaThread* next_thread(ThreadsList* t_list, JavaThread* first_sampled, JavaThread* current); | |||
338 | void task_stacktrace(JfrSampleType type, JavaThread** last_thread); | |||
339 | JfrThreadSampler(size_t interval_java, size_t interval_native, u4 max_frames); | |||
340 | ~JfrThreadSampler(); | |||
341 | ||||
342 | void start_thread(); | |||
343 | ||||
344 | void enroll(); | |||
345 | void disenroll(); | |||
346 | void set_java_interval(size_t interval) { _interval_java = interval; }; | |||
347 | void set_native_interval(size_t interval) { _interval_native = interval; }; | |||
348 | size_t get_java_interval() { return _interval_java; }; | |||
349 | size_t get_native_interval() { return _interval_native; }; | |||
350 | protected: | |||
351 | virtual void post_run(); | |||
352 | public: | |||
353 | virtual const char* name() const { return "JFR Thread Sampler"; } | |||
354 | virtual const char* type_name() const { return "JfrThreadSampler"; } | |||
355 | bool is_JfrSampler_thread() const { return true; } | |||
356 | void run(); | |||
357 | static Monitor* transition_block() { return JfrThreadSampler_lock; } | |||
358 | static void on_javathread_suspend(JavaThread* thread); | |||
359 | }; | |||
360 | ||||
361 | static void clear_transition_block(JavaThread* jt) { | |||
362 | assert(Threads_lock->owned_by_self(), "Holding the thread table lock.")do { if (!(Threads_lock->owned_by_self())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 362, "assert(" "Threads_lock->owned_by_self()" ") failed" , "Holding the thread table lock."); ::breakpoint(); } } while (0); | |||
363 | jt->clear_trace_flag(); | |||
364 | JfrThreadLocal* const tl = jt->jfr_thread_local(); | |||
365 | MutexLocker ml(JfrThreadSampler::transition_block(), Mutex::_no_safepoint_check_flag); | |||
366 | if (tl->is_trace_block()) { | |||
367 | JfrThreadSampler::transition_block()->notify(); | |||
368 | } | |||
369 | } | |||
370 | ||||
371 | static bool is_excluded(JavaThread* thread) { | |||
372 | assert(thread != NULL, "invariant")do { if (!(thread != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 372, "assert(" "thread != __null" ") failed", "invariant"); ::breakpoint(); } } while (0); | |||
373 | return thread->is_hidden_from_external_view() || thread->in_deopt_handler() || thread->jfr_thread_local()->is_excluded(); | |||
374 | } | |||
375 | ||||
376 | bool JfrThreadSampleClosure::do_sample_thread(JavaThread* thread, JfrStackFrame* frames, u4 max_frames, JfrSampleType type) { | |||
377 | assert(Threads_lock->owned_by_self(), "Holding the thread table lock.")do { if (!(Threads_lock->owned_by_self())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 377, "assert(" "Threads_lock->owned_by_self()" ") failed" , "Holding the thread table lock."); ::breakpoint(); } } while (0); | |||
378 | if (is_excluded(thread)) { | |||
379 | return false; | |||
380 | } | |||
381 | ||||
382 | bool ret = false; | |||
383 | thread->set_trace_flag(); // Provides StoreLoad, needed to keep read of thread state from floating up. | |||
384 | if (JAVA_SAMPLE == type) { | |||
385 | if (thread_state_in_java(thread)) { | |||
386 | ret = sample_thread_in_java(thread, frames, max_frames); | |||
387 | } | |||
388 | } else { | |||
389 | assert(NATIVE_SAMPLE == type, "invariant")do { if (!(NATIVE_SAMPLE == type)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 389, "assert(" "NATIVE_SAMPLE == type" ") failed", "invariant" ); ::breakpoint(); } } while (0); | |||
390 | if (thread_state_in_native(thread)) { | |||
391 | ret = sample_thread_in_native(thread, frames, max_frames); | |||
392 | } | |||
393 | } | |||
394 | clear_transition_block(thread); | |||
395 | return ret; | |||
396 | } | |||
397 | ||||
398 | JfrThreadSampler::JfrThreadSampler(size_t interval_java, size_t interval_native, u4 max_frames) : | |||
399 | _sample(), | |||
400 | _sampler_thread(NULL__null), | |||
401 | _frames(JfrCHeapObj::new_array<JfrStackFrame>(max_frames)), | |||
402 | _last_thread_java(NULL__null), | |||
403 | _last_thread_native(NULL__null), | |||
404 | _interval_java(interval_java), | |||
405 | _interval_native(interval_native), | |||
406 | _min_size(JfrOptionSet::stackdepth() * sizeof(intptr_t)), | |||
407 | _renew_size(_min_size * 2), | |||
408 | _cur_index(-1), | |||
409 | _max_frames(max_frames), | |||
410 | _disenrolled(true) { | |||
411 | } | |||
412 | ||||
413 | JfrThreadSampler::~JfrThreadSampler() { | |||
414 | JfrCHeapObj::free(_frames, sizeof(JfrStackFrame) * _max_frames); | |||
415 | } | |||
416 | ||||
417 | static inline bool is_released(JavaThread* jt) { | |||
418 | return !jt->is_trace_suspend(); | |||
419 | } | |||
420 | ||||
421 | void JfrThreadSampler::on_javathread_suspend(JavaThread* thread) { | |||
422 | if (is_released(thread)) { | |||
423 | return; | |||
424 | } | |||
425 | JfrThreadLocal* const tl = thread->jfr_thread_local(); | |||
426 | MonitorLocker ml(transition_block(), Mutex::_no_safepoint_check_flag); | |||
427 | tl->set_trace_block(); | |||
428 | while (!is_released(thread)) { | |||
429 | ml.wait(); | |||
430 | } | |||
431 | tl->clear_trace_block(); | |||
432 | } | |||
433 | ||||
434 | JavaThread* JfrThreadSampler::next_thread(ThreadsList* t_list, JavaThread* first_sampled, JavaThread* current) { | |||
435 | assert(t_list != NULL, "invariant")do { if (!(t_list != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 435, "assert(" "t_list != __null" ") failed", "invariant"); ::breakpoint(); } } while (0); | |||
436 | assert(Threads_lock->owned_by_self(), "Holding the thread table lock.")do { if (!(Threads_lock->owned_by_self())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 436, "assert(" "Threads_lock->owned_by_self()" ") failed" , "Holding the thread table lock."); ::breakpoint(); } } while (0); | |||
437 | assert(_cur_index >= -1 && (uint)_cur_index + 1 <= t_list->length(), "invariant")do { if (!(_cur_index >= -1 && (uint)_cur_index + 1 <= t_list->length())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 437, "assert(" "_cur_index >= -1 && (uint)_cur_index + 1 <= t_list->length()" ") failed", "invariant"); ::breakpoint(); } } while (0); | |||
438 | assert((current == NULL && -1 == _cur_index) || (t_list->find_index_of_JavaThread(current) == _cur_index), "invariant")do { if (!((current == __null && -1 == _cur_index) || (t_list->find_index_of_JavaThread(current) == _cur_index) )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 438, "assert(" "(current == __null && -1 == _cur_index) || (t_list->find_index_of_JavaThread(current) == _cur_index)" ") failed", "invariant"); ::breakpoint(); } } while (0); | |||
439 | if ((uint)_cur_index + 1 == t_list->length()) { | |||
440 | // wrap | |||
441 | _cur_index = 0; | |||
442 | } else { | |||
443 | _cur_index++; | |||
444 | } | |||
445 | assert(_cur_index >= 0 && (uint)_cur_index < t_list->length(), "invariant")do { if (!(_cur_index >= 0 && (uint)_cur_index < t_list->length())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 445, "assert(" "_cur_index >= 0 && (uint)_cur_index < t_list->length()" ") failed", "invariant"); ::breakpoint(); } } while (0); | |||
446 | JavaThread* const next = t_list->thread_at(_cur_index); | |||
447 | return next != first_sampled ? next : NULL__null; | |||
448 | } | |||
449 | ||||
450 | void JfrThreadSampler::start_thread() { | |||
451 | if (os::create_thread(this, os::os_thread)) { | |||
452 | os::start_thread(this); | |||
453 | } else { | |||
454 | log_error(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("Failed to create thread for thread sampling"); | |||
455 | } | |||
456 | } | |||
457 | ||||
458 | void JfrThreadSampler::enroll() { | |||
459 | if (_disenrolled) { | |||
460 | log_trace(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Trace))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Trace>("Enrolling thread sampler"); | |||
461 | _sample.signal(); | |||
462 | _disenrolled = false; | |||
463 | } | |||
464 | } | |||
465 | ||||
466 | void JfrThreadSampler::disenroll() { | |||
467 | if (!_disenrolled) { | |||
468 | _sample.wait(); | |||
469 | _disenrolled = true; | |||
470 | log_trace(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Trace))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Trace>("Disenrolling thread sampler"); | |||
471 | } | |||
472 | } | |||
473 | ||||
474 | static jlong get_monotonic_ms() { | |||
475 | return os::javaTimeNanos() / 1000000; | |||
476 | } | |||
477 | ||||
478 | void JfrThreadSampler::run() { | |||
479 | assert(_sampler_thread == NULL, "invariant")do { if (!(_sampler_thread == __null)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 479, "assert(" "_sampler_thread == __null" ") failed", "invariant" ); ::breakpoint(); } } while (0); | |||
480 | ||||
481 | _sampler_thread = this; | |||
482 | ||||
483 | jlong last_java_ms = get_monotonic_ms(); | |||
484 | jlong last_native_ms = last_java_ms; | |||
485 | while (true) { | |||
486 | if (!_sample.trywait()) { | |||
487 | // disenrolled | |||
488 | _sample.wait(); | |||
489 | last_java_ms = get_monotonic_ms(); | |||
490 | last_native_ms = last_java_ms; | |||
491 | } | |||
492 | _sample.signal(); | |||
493 | jlong java_interval = _interval_java == 0 ? max_jlong : MAX2<jlong>(_interval_java, 1); | |||
494 | jlong native_interval = _interval_native == 0 ? max_jlong : MAX2<jlong>(_interval_native, 1); | |||
495 | ||||
496 | jlong now_ms = get_monotonic_ms(); | |||
497 | ||||
498 | /* | |||
499 | * Let I be java_interval or native_interval. | |||
500 | * Let L be last_java_ms or last_native_ms. | |||
501 | * Let N be now_ms. | |||
502 | * | |||
503 | * Interval, I, might be max_jlong so the addition | |||
504 | * could potentially overflow without parenthesis (UB). Also note that | |||
505 | * L - N < 0. Avoid UB, by adding parenthesis. | |||
506 | */ | |||
507 | jlong next_j = java_interval + (last_java_ms - now_ms); | |||
508 | jlong next_n = native_interval + (last_native_ms - now_ms); | |||
509 | ||||
510 | jlong sleep_to_next = MIN2<jlong>(next_j, next_n); | |||
511 | ||||
512 | if (sleep_to_next > 0) { | |||
513 | os::naked_short_sleep(sleep_to_next); | |||
514 | } | |||
515 | ||||
516 | if ((next_j - sleep_to_next) <= 0) { | |||
517 | task_stacktrace(JAVA_SAMPLE, &_last_thread_java); | |||
518 | last_java_ms = get_monotonic_ms(); | |||
519 | } | |||
520 | if ((next_n - sleep_to_next) <= 0) { | |||
521 | task_stacktrace(NATIVE_SAMPLE, &_last_thread_native); | |||
522 | last_native_ms = get_monotonic_ms(); | |||
523 | } | |||
524 | } | |||
525 | } | |||
526 | ||||
527 | void JfrThreadSampler::post_run() { | |||
528 | this->NonJavaThread::post_run(); | |||
529 | delete this; | |||
530 | } | |||
531 | ||||
532 | const JfrBuffer* JfrThreadSampler::get_enqueue_buffer() { | |||
533 | const JfrBuffer* buffer = JfrTraceIdLoadBarrier::get_enqueue_buffer(this); | |||
534 | return buffer != nullptr ? renew_if_full(buffer) : JfrTraceIdLoadBarrier::renew_enqueue_buffer(_renew_size, this); | |||
535 | } | |||
536 | ||||
537 | const JfrBuffer* JfrThreadSampler::renew_if_full(const JfrBuffer* enqueue_buffer) { | |||
538 | assert(enqueue_buffer != nullptr, "invariant")do { if (!(enqueue_buffer != nullptr)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 538, "assert(" "enqueue_buffer != nullptr" ") failed", "invariant" ); ::breakpoint(); } } while (0); | |||
539 | return enqueue_buffer->free_size() < _min_size ? JfrTraceIdLoadBarrier::renew_enqueue_buffer(_renew_size, this) : enqueue_buffer; | |||
540 | } | |||
541 | ||||
542 | void JfrThreadSampler::task_stacktrace(JfrSampleType type, JavaThread** last_thread) { | |||
543 | ResourceMark rm; | |||
544 | EventExecutionSample samples[MAX_NR_OF_JAVA_SAMPLES]; | |||
545 | EventNativeMethodSample samples_native[MAX_NR_OF_NATIVE_SAMPLES]; | |||
546 | JfrThreadSampleClosure sample_task(samples, samples_native); | |||
547 | ||||
548 | const uint sample_limit = JAVA_SAMPLE == type ? MAX_NR_OF_JAVA_SAMPLES : MAX_NR_OF_NATIVE_SAMPLES; | |||
549 | uint num_samples = 0; | |||
550 | JavaThread* start = NULL__null; | |||
551 | { | |||
552 | elapsedTimer sample_time; | |||
553 | sample_time.start(); | |||
554 | { | |||
555 | MutexLocker tlock(Threads_lock); | |||
556 | ThreadsListHandle tlh; | |||
557 | // Resolve a sample session relative start position index into the thread list array. | |||
558 | // In cases where the last sampled thread is NULL or not-NULL but stale, find_index() returns -1. | |||
559 | _cur_index = tlh.list()->find_index_of_JavaThread(*last_thread); | |||
560 | JavaThread* current = _cur_index != -1 ? *last_thread : NULL__null; | |||
561 | ||||
562 | // Explicitly monitor the available space of the thread-local buffer used by the load barrier | |||
563 | // for enqueuing klasses as part of tagging methods. We do this because if space becomes sparse, | |||
564 | // we cannot rely on the implicit allocation of a new buffer as part of the regular tag mechanism. | |||
565 | // If the free list is empty, a malloc could result, and the problem with that is that the thread | |||
566 | // we have suspended could be the holder of the malloc lock. Instead, the buffer is pre-emptively | |||
567 | // renewed before thread suspension. | |||
568 | const JfrBuffer* enqueue_buffer = get_enqueue_buffer(); | |||
569 | assert(enqueue_buffer != nullptr, "invariant")do { if (!(enqueue_buffer != nullptr)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 569, "assert(" "enqueue_buffer != nullptr" ") failed", "invariant" ); ::breakpoint(); } } while (0); | |||
570 | ||||
571 | while (num_samples < sample_limit) { | |||
572 | current = next_thread(tlh.list(), start, current); | |||
573 | if (current == NULL__null) { | |||
574 | break; | |||
575 | } | |||
576 | if (start == NULL__null) { | |||
577 | start = current; // remember the thread where we started to attempt sampling | |||
578 | } | |||
579 | if (current->is_Compiler_thread()) { | |||
580 | continue; | |||
581 | } | |||
582 | assert(enqueue_buffer->free_size() >= _min_size, "invariant")do { if (!(enqueue_buffer->free_size() >= _min_size)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 582, "assert(" "enqueue_buffer->free_size() >= _min_size" ") failed", "invariant"); ::breakpoint(); } } while (0); | |||
583 | if (sample_task.do_sample_thread(current, _frames, _max_frames, type)) { | |||
584 | num_samples++; | |||
585 | } | |||
586 | enqueue_buffer = renew_if_full(enqueue_buffer); | |||
587 | } | |||
588 | *last_thread = current; // remember the thread we last attempted to sample | |||
589 | } | |||
590 | sample_time.stop(); | |||
591 | log_trace(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Trace))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Trace>("JFR thread sampling done in %3.7f secs with %d java %d native samples", | |||
592 | sample_time.seconds(), sample_task.java_entries(), sample_task.native_entries()); | |||
593 | } | |||
594 | if (num_samples > 0) { | |||
595 | sample_task.commit_events(type); | |||
596 | } | |||
597 | } | |||
598 | ||||
599 | static JfrThreadSampling* _instance = NULL__null; | |||
600 | ||||
601 | JfrThreadSampling& JfrThreadSampling::instance() { | |||
602 | return *_instance; | |||
| ||||
603 | } | |||
604 | ||||
605 | JfrThreadSampling* JfrThreadSampling::create() { | |||
606 | assert(_instance == NULL, "invariant")do { if (!(_instance == __null)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 606, "assert(" "_instance == __null" ") failed", "invariant" ); ::breakpoint(); } } while (0); | |||
607 | _instance = new JfrThreadSampling(); | |||
608 | return _instance; | |||
609 | } | |||
610 | ||||
611 | void JfrThreadSampling::destroy() { | |||
612 | if (_instance != NULL__null) { | |||
613 | delete _instance; | |||
614 | _instance = NULL__null; | |||
615 | } | |||
616 | } | |||
617 | ||||
618 | JfrThreadSampling::JfrThreadSampling() : _sampler(NULL__null) {} | |||
619 | ||||
620 | JfrThreadSampling::~JfrThreadSampling() { | |||
621 | if (_sampler != NULL__null) { | |||
622 | _sampler->disenroll(); | |||
623 | } | |||
624 | } | |||
625 | ||||
626 | static void log(size_t interval_java, size_t interval_native) { | |||
627 | log_trace(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Trace))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Trace>("Updated thread sampler for java: " SIZE_FORMAT"%" "l" "u" " ms, native " SIZE_FORMAT"%" "l" "u" " ms", interval_java, interval_native); | |||
628 | } | |||
629 | ||||
630 | void JfrThreadSampling::start_sampler(size_t interval_java, size_t interval_native) { | |||
631 | assert(_sampler == NULL, "invariant")do { if (!(_sampler == __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 631, "assert(" "_sampler == __null" ") failed", "invariant" ); ::breakpoint(); } } while (0); | |||
632 | log_trace(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Trace))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Trace>("Enrolling thread sampler"); | |||
633 | _sampler = new JfrThreadSampler(interval_java, interval_native, JfrOptionSet::stackdepth()); | |||
634 | _sampler->start_thread(); | |||
635 | _sampler->enroll(); | |||
636 | } | |||
637 | ||||
638 | void JfrThreadSampling::set_sampling_interval(bool java_interval, size_t period) { | |||
639 | size_t interval_java = 0; | |||
640 | size_t interval_native = 0; | |||
641 | if (_sampler != NULL__null) { | |||
642 | interval_java = _sampler->get_java_interval(); | |||
643 | interval_native = _sampler->get_native_interval(); | |||
644 | } | |||
645 | if (java_interval) { | |||
646 | interval_java = period; | |||
647 | } else { | |||
648 | interval_native = period; | |||
649 | } | |||
650 | if (interval_java > 0 || interval_native > 0) { | |||
651 | if (_sampler == NULL__null) { | |||
652 | log_trace(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Trace))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Trace>("Creating thread sampler for java:%zu ms, native %zu ms", interval_java, interval_native); | |||
653 | start_sampler(interval_java, interval_native); | |||
654 | } else { | |||
655 | _sampler->set_java_interval(interval_java); | |||
656 | _sampler->set_native_interval(interval_native); | |||
657 | _sampler->enroll(); | |||
658 | } | |||
659 | assert(_sampler != NULL, "invariant")do { if (!(_sampler != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp" , 659, "assert(" "_sampler != __null" ") failed", "invariant" ); ::breakpoint(); } } while (0); | |||
660 | log(interval_java, interval_native); | |||
661 | } else if (_sampler != NULL__null) { | |||
662 | _sampler->disenroll(); | |||
663 | } | |||
664 | } | |||
665 | ||||
666 | void JfrThreadSampling::set_java_sample_interval(size_t period) { | |||
667 | if (_instance == NULL__null && 0 == period) { | |||
668 | return; | |||
669 | } | |||
670 | instance().set_sampling_interval(true, period); | |||
671 | } | |||
672 | ||||
673 | void JfrThreadSampling::set_native_sample_interval(size_t period) { | |||
674 | if (_instance == NULL__null && 0 == period) { | |||
| ||||
675 | return; | |||
676 | } | |||
677 | instance().set_sampling_interval(false, period); | |||
678 | } | |||
679 | ||||
680 | void JfrThreadSampling::on_javathread_suspend(JavaThread* thread) { | |||
681 | JfrThreadSampler::on_javathread_suspend(thread); | |||
682 | } |