1 | /* |
2 | * Copyright (c) 2012, 2019, 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/dcmd/jfrDcmds.hpp" |
27 | #include "jfr/instrumentation/jfrJvmtiAgent.hpp" |
28 | #include "jfr/jni/jfrJavaSupport.hpp" |
29 | #include "jfr/periodic/jfrOSInterface.hpp" |
30 | #include "jfr/periodic/sampling/jfrThreadSampler.hpp" |
31 | #include "jfr/recorder/jfrRecorder.hpp" |
32 | #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" |
33 | #include "jfr/recorder/repository/jfrRepository.hpp" |
34 | #include "jfr/recorder/service/jfrOptionSet.hpp" |
35 | #include "jfr/recorder/service/jfrPostBox.hpp" |
36 | #include "jfr/recorder/service/jfrRecorderService.hpp" |
37 | #include "jfr/recorder/service/jfrRecorderThread.hpp" |
38 | #include "jfr/recorder/storage/jfrStorage.hpp" |
39 | #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" |
40 | #include "jfr/recorder/stringpool/jfrStringPool.hpp" |
41 | #include "jfr/utilities/jfrTime.hpp" |
42 | #include "jfr/writers/jfrJavaEventWriter.hpp" |
43 | #include "logging/log.hpp" |
44 | #include "logging/logStream.hpp" |
45 | #include "memory/resourceArea.inline.hpp" |
46 | #include "runtime/handles.inline.hpp" |
47 | #include "runtime/globals_extension.hpp" |
48 | #include "utilities/growableArray.hpp" |
49 | |
50 | bool JfrRecorder::is_disabled() { |
51 | // True if -XX:-FlightRecorder has been explicitly set on the |
52 | // command line |
53 | return FLAG_IS_CMDLINE(FlightRecorder) ? !FlightRecorder : false; |
54 | } |
55 | |
56 | static bool _enabled = false; |
57 | |
58 | static bool enable() { |
59 | assert(!_enabled, "invariant" ); |
60 | FLAG_SET_MGMT(FlightRecorder, true); |
61 | _enabled = FlightRecorder; |
62 | assert(_enabled, "invariant" ); |
63 | return _enabled; |
64 | } |
65 | |
66 | bool JfrRecorder::is_enabled() { |
67 | return _enabled; |
68 | } |
69 | |
70 | bool JfrRecorder::on_vm_init() { |
71 | if (!is_disabled()) { |
72 | if (FlightRecorder || StartFlightRecording != NULL) { |
73 | enable(); |
74 | } |
75 | } |
76 | // fast time initialization |
77 | return JfrTime::initialize(); |
78 | } |
79 | |
80 | static GrowableArray<JfrStartFlightRecordingDCmd*>* dcmd_recordings_array = NULL; |
81 | |
82 | static void release_recordings() { |
83 | if (dcmd_recordings_array != NULL) { |
84 | const int length = dcmd_recordings_array->length(); |
85 | for (int i = 0; i < length; ++i) { |
86 | delete dcmd_recordings_array->at(i); |
87 | } |
88 | delete dcmd_recordings_array; |
89 | dcmd_recordings_array = NULL; |
90 | } |
91 | } |
92 | |
93 | static void teardown_startup_support() { |
94 | release_recordings(); |
95 | JfrOptionSet::release_startup_recording_options(); |
96 | } |
97 | |
98 | // Parsing options here to detect errors as soon as possible |
99 | static bool parse_recording_options(const char* options, JfrStartFlightRecordingDCmd* dcmd_recording, TRAPS) { |
100 | assert(options != NULL, "invariant" ); |
101 | assert(dcmd_recording != NULL, "invariant" ); |
102 | CmdLine cmdline(options, strlen(options), true); |
103 | dcmd_recording->parse(&cmdline, ',', THREAD); |
104 | if (HAS_PENDING_EXCEPTION) { |
105 | java_lang_Throwable::print(PENDING_EXCEPTION, tty); |
106 | CLEAR_PENDING_EXCEPTION; |
107 | return false; |
108 | } |
109 | return true; |
110 | } |
111 | |
112 | static bool validate_recording_options(TRAPS) { |
113 | const GrowableArray<const char*>* options = JfrOptionSet::startup_recording_options(); |
114 | if (options == NULL) { |
115 | return true; |
116 | } |
117 | const int length = options->length(); |
118 | assert(length >= 1, "invariant" ); |
119 | assert(dcmd_recordings_array == NULL, "invariant" ); |
120 | dcmd_recordings_array = new (ResourceObj::C_HEAP, mtTracing)GrowableArray<JfrStartFlightRecordingDCmd*>(length, true, mtTracing); |
121 | assert(dcmd_recordings_array != NULL, "invariant" ); |
122 | for (int i = 0; i < length; ++i) { |
123 | JfrStartFlightRecordingDCmd* const dcmd_recording = new(ResourceObj::C_HEAP, mtTracing) JfrStartFlightRecordingDCmd(tty, true); |
124 | assert(dcmd_recording != NULL, "invariant" ); |
125 | dcmd_recordings_array->append(dcmd_recording); |
126 | if (!parse_recording_options(options->at(i), dcmd_recording, THREAD)) { |
127 | return false; |
128 | } |
129 | } |
130 | return true; |
131 | } |
132 | |
133 | static bool launch_recording(JfrStartFlightRecordingDCmd* dcmd_recording, TRAPS) { |
134 | assert(dcmd_recording != NULL, "invariant" ); |
135 | log_trace(jfr, system)("Starting a recording" ); |
136 | dcmd_recording->execute(DCmd_Source_Internal, THREAD); |
137 | if (HAS_PENDING_EXCEPTION) { |
138 | log_debug(jfr, system)("Exception while starting a recording" ); |
139 | CLEAR_PENDING_EXCEPTION; |
140 | return false; |
141 | } |
142 | log_trace(jfr, system)("Finished starting a recording" ); |
143 | return true; |
144 | } |
145 | |
146 | static bool launch_recordings(TRAPS) { |
147 | bool result = true; |
148 | if (dcmd_recordings_array != NULL) { |
149 | const int length = dcmd_recordings_array->length(); |
150 | assert(length >= 1, "invariant" ); |
151 | for (int i = 0; i < length; ++i) { |
152 | if (!launch_recording(dcmd_recordings_array->at(i), THREAD)) { |
153 | result = false; |
154 | break; |
155 | } |
156 | } |
157 | } |
158 | teardown_startup_support(); |
159 | return result; |
160 | } |
161 | |
162 | static void log_jdk_jfr_module_resolution_error(TRAPS) { |
163 | LogTarget(Error, jfr, system) lt_error; |
164 | LogTargetHandle handle(lt_error); |
165 | LogStream stream(handle); |
166 | JfrJavaSupport::is_jdk_jfr_module_available(&stream, THREAD); |
167 | } |
168 | |
169 | static bool is_cds_dump_requested() { |
170 | // we will not be able to launch recordings if a cds dump is being requested |
171 | if ((DumpSharedSpaces || DynamicDumpSharedSpaces) && (JfrOptionSet::startup_recording_options() != NULL)) { |
172 | warning("JFR will be disabled during CDS dumping" ); |
173 | teardown_startup_support(); |
174 | return true; |
175 | } |
176 | return false; |
177 | } |
178 | |
179 | bool JfrRecorder::on_vm_start() { |
180 | if (is_cds_dump_requested()) { |
181 | return true; |
182 | } |
183 | Thread* const thread = Thread::current(); |
184 | if (!JfrOptionSet::initialize(thread)) { |
185 | return false; |
186 | } |
187 | if (!register_jfr_dcmds()) { |
188 | return false; |
189 | } |
190 | |
191 | const bool in_graph = JfrJavaSupport::is_jdk_jfr_module_available(); |
192 | |
193 | if (in_graph) { |
194 | if (!validate_recording_options(thread)) { |
195 | return false; |
196 | } |
197 | if (!JfrOptionSet::configure(thread)) { |
198 | return false; |
199 | } |
200 | } |
201 | |
202 | if (!is_enabled()) { |
203 | return true; |
204 | } |
205 | |
206 | if (!in_graph) { |
207 | log_jdk_jfr_module_resolution_error(thread); |
208 | return false; |
209 | } |
210 | |
211 | return launch_recordings(thread); |
212 | } |
213 | |
214 | static bool _created = false; |
215 | |
216 | // |
217 | // Main entry point for starting Jfr functionality. |
218 | // Non-protected initializations assume single-threaded setup. |
219 | // |
220 | bool JfrRecorder::create(bool simulate_failure) { |
221 | assert(!is_disabled(), "invariant" ); |
222 | assert(!is_created(), "invariant" ); |
223 | if (!is_enabled()) { |
224 | enable(); |
225 | } |
226 | if (!create_components() || simulate_failure) { |
227 | destroy_components(); |
228 | return false; |
229 | } |
230 | if (!create_recorder_thread()) { |
231 | destroy_components(); |
232 | return false; |
233 | } |
234 | _created = true; |
235 | return true; |
236 | } |
237 | |
238 | bool JfrRecorder::is_created() { |
239 | return _created; |
240 | } |
241 | |
242 | bool JfrRecorder::create_components() { |
243 | ResourceMark rm; |
244 | HandleMark hm; |
245 | |
246 | if (!create_java_event_writer()) { |
247 | return false; |
248 | } |
249 | if (!create_jvmti_agent()) { |
250 | return false; |
251 | } |
252 | if (!create_post_box()) { |
253 | return false; |
254 | } |
255 | if (!create_chunk_repository()) { |
256 | return false; |
257 | } |
258 | if (!create_storage()) { |
259 | return false; |
260 | } |
261 | if (!create_checkpoint_manager()) { |
262 | return false; |
263 | } |
264 | if (!create_stacktrace_repository()) { |
265 | return false; |
266 | } |
267 | if (!create_os_interface()) { |
268 | return false; |
269 | } |
270 | if (!create_stringpool()) { |
271 | return false; |
272 | } |
273 | if (!create_thread_sampling()) { |
274 | return false; |
275 | } |
276 | return true; |
277 | } |
278 | |
279 | // subsystems |
280 | static JfrJvmtiAgent* _jvmti_agent = NULL; |
281 | static JfrPostBox* _post_box = NULL; |
282 | static JfrStorage* _storage = NULL; |
283 | static JfrCheckpointManager* _checkpoint_manager = NULL; |
284 | static JfrRepository* _repository = NULL; |
285 | static JfrStackTraceRepository* _stack_trace_repository; |
286 | static JfrStringPool* _stringpool = NULL; |
287 | static JfrOSInterface* _os_interface = NULL; |
288 | static JfrThreadSampling* _thread_sampling = NULL; |
289 | |
290 | bool JfrRecorder::create_java_event_writer() { |
291 | return JfrJavaEventWriter::initialize(); |
292 | } |
293 | |
294 | bool JfrRecorder::create_jvmti_agent() { |
295 | return JfrOptionSet::allow_retransforms() ? JfrJvmtiAgent::create() : true; |
296 | } |
297 | |
298 | bool JfrRecorder::create_post_box() { |
299 | assert(_post_box == NULL, "invariant" ); |
300 | _post_box = JfrPostBox::create(); |
301 | return _post_box != NULL; |
302 | } |
303 | |
304 | bool JfrRecorder::create_chunk_repository() { |
305 | assert(_repository == NULL, "invariant" ); |
306 | assert(_post_box != NULL, "invariant" ); |
307 | _repository = JfrRepository::create(*_post_box); |
308 | return _repository != NULL && _repository->initialize(); |
309 | } |
310 | |
311 | bool JfrRecorder::create_os_interface() { |
312 | assert(_os_interface == NULL, "invariant" ); |
313 | _os_interface = JfrOSInterface::create(); |
314 | return _os_interface != NULL && _os_interface->initialize(); |
315 | } |
316 | |
317 | bool JfrRecorder::create_storage() { |
318 | assert(_repository != NULL, "invariant" ); |
319 | assert(_post_box != NULL, "invariant" ); |
320 | _storage = JfrStorage::create(_repository->chunkwriter(), *_post_box); |
321 | return _storage != NULL && _storage->initialize(); |
322 | } |
323 | |
324 | bool JfrRecorder::create_checkpoint_manager() { |
325 | assert(_checkpoint_manager == NULL, "invariant" ); |
326 | assert(_repository != NULL, "invariant" ); |
327 | _checkpoint_manager = JfrCheckpointManager::create(_repository->chunkwriter()); |
328 | return _checkpoint_manager != NULL && _checkpoint_manager->initialize(); |
329 | } |
330 | |
331 | bool JfrRecorder::create_stacktrace_repository() { |
332 | assert(_stack_trace_repository == NULL, "invariant" ); |
333 | _stack_trace_repository = JfrStackTraceRepository::create(); |
334 | return _stack_trace_repository != NULL && _stack_trace_repository->initialize(); |
335 | } |
336 | |
337 | bool JfrRecorder::create_stringpool() { |
338 | assert(_stringpool == NULL, "invariant" ); |
339 | assert(_repository != NULL, "invariant" ); |
340 | _stringpool = JfrStringPool::create(_repository->chunkwriter()); |
341 | return _stringpool != NULL && _stringpool->initialize(); |
342 | } |
343 | |
344 | bool JfrRecorder::create_thread_sampling() { |
345 | assert(_thread_sampling == NULL, "invariant" ); |
346 | _thread_sampling = JfrThreadSampling::create(); |
347 | return _thread_sampling != NULL; |
348 | } |
349 | |
350 | void JfrRecorder::destroy_components() { |
351 | JfrJvmtiAgent::destroy(); |
352 | if (_post_box != NULL) { |
353 | JfrPostBox::destroy(); |
354 | _post_box = NULL; |
355 | } |
356 | if (_repository != NULL) { |
357 | JfrRepository::destroy(); |
358 | _repository = NULL; |
359 | } |
360 | if (_storage != NULL) { |
361 | JfrStorage::destroy(); |
362 | _storage = NULL; |
363 | } |
364 | if (_checkpoint_manager != NULL) { |
365 | JfrCheckpointManager::destroy(); |
366 | _checkpoint_manager = NULL; |
367 | } |
368 | if (_stack_trace_repository != NULL) { |
369 | JfrStackTraceRepository::destroy(); |
370 | _stack_trace_repository = NULL; |
371 | } |
372 | if (_stringpool != NULL) { |
373 | JfrStringPool::destroy(); |
374 | _stringpool = NULL; |
375 | } |
376 | if (_os_interface != NULL) { |
377 | JfrOSInterface::destroy(); |
378 | _os_interface = NULL; |
379 | } |
380 | if (_thread_sampling != NULL) { |
381 | JfrThreadSampling::destroy(); |
382 | _thread_sampling = NULL; |
383 | } |
384 | } |
385 | |
386 | bool JfrRecorder::create_recorder_thread() { |
387 | return JfrRecorderThread::start(_checkpoint_manager, _post_box, Thread::current()); |
388 | } |
389 | |
390 | void JfrRecorder::destroy() { |
391 | assert(is_created(), "invariant" ); |
392 | _post_box->post(MSG_SHUTDOWN); |
393 | JfrJvmtiAgent::destroy(); |
394 | } |
395 | |
396 | void JfrRecorder::on_recorder_thread_exit() { |
397 | assert(!is_recording(), "invariant" ); |
398 | // intent is to destroy the recorder instance and components, |
399 | // but need sensitive coordination not yet in place |
400 | // |
401 | // destroy_components(); |
402 | // |
403 | log_debug(jfr, system)("Recorder thread STOPPED" ); |
404 | } |
405 | |
406 | void JfrRecorder::start_recording() { |
407 | _post_box->post(MSG_START); |
408 | } |
409 | |
410 | bool JfrRecorder::is_recording() { |
411 | return JfrRecorderService::is_recording(); |
412 | } |
413 | |
414 | void JfrRecorder::stop_recording() { |
415 | _post_box->post(MSG_STOP); |
416 | } |
417 | |