1 /* |
1 /* |
2 * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. |
2 * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
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 |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. Oracle designates this |
7 * published by the Free Software Foundation. Oracle designates this |
29 import static jdk.jfr.internal.LogLevel.TRACE; |
29 import static jdk.jfr.internal.LogLevel.TRACE; |
30 import static jdk.jfr.internal.LogLevel.WARN; |
30 import static jdk.jfr.internal.LogLevel.WARN; |
31 import static jdk.jfr.internal.LogTag.JFR; |
31 import static jdk.jfr.internal.LogTag.JFR; |
32 import static jdk.jfr.internal.LogTag.JFR_SYSTEM; |
32 import static jdk.jfr.internal.LogTag.JFR_SYSTEM; |
33 |
33 |
34 import java.io.IOException; |
|
35 import java.security.AccessControlContext; |
34 import java.security.AccessControlContext; |
36 import java.security.AccessController; |
35 import java.security.AccessController; |
37 import java.time.Duration; |
36 import java.time.Duration; |
38 import java.time.Instant; |
37 import java.time.Instant; |
39 import java.util.ArrayList; |
38 import java.util.ArrayList; |
56 import jdk.jfr.events.ActiveSettingEvent; |
55 import jdk.jfr.events.ActiveSettingEvent; |
57 import jdk.jfr.internal.SecuritySupport.SecureRecorderListener; |
56 import jdk.jfr.internal.SecuritySupport.SecureRecorderListener; |
58 import jdk.jfr.internal.instrument.JDKEvents; |
57 import jdk.jfr.internal.instrument.JDKEvents; |
59 |
58 |
60 public final class PlatformRecorder { |
59 public final class PlatformRecorder { |
|
60 |
61 |
61 |
62 private final List<PlatformRecording> recordings = new ArrayList<>(); |
62 private final List<PlatformRecording> recordings = new ArrayList<>(); |
63 private final static List<SecureRecorderListener> changeListeners = new ArrayList<>(); |
63 private final static List<SecureRecorderListener> changeListeners = new ArrayList<>(); |
64 private final Repository repository; |
64 private final Repository repository; |
65 private final Timer timer; |
65 private final Timer timer; |
94 try { |
94 try { |
95 List<Timer> result = new CopyOnWriteArrayList<>(); |
95 List<Timer> result = new CopyOnWriteArrayList<>(); |
96 Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> { |
96 Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> { |
97 result.add(new Timer("JFR Recording Scheduler", true)); |
97 result.add(new Timer("JFR Recording Scheduler", true)); |
98 }); |
98 }); |
|
99 jvm.exclude(t); |
99 t.start(); |
100 t.start(); |
100 t.join(); |
101 t.join(); |
101 return result.get(0); |
102 return result.get(0); |
102 } catch (InterruptedException e) { |
103 } catch (InterruptedException e) { |
103 throw new IllegalStateException("Not able to create timer task. " + e.getMessage(), e); |
104 throw new IllegalStateException("Not able to create timer task. " + e.getMessage(), e); |
202 jvm.destroyNativeJFR(); |
203 jvm.destroyNativeJFR(); |
203 } |
204 } |
204 repository.clear(); |
205 repository.clear(); |
205 } |
206 } |
206 |
207 |
207 synchronized void start(PlatformRecording recording) { |
208 synchronized long start(PlatformRecording recording) { |
208 // State can only be NEW or DELAYED because of previous checks |
209 // State can only be NEW or DELAYED because of previous checks |
209 Instant now = Instant.now(); |
210 Instant now = Instant.now(); |
210 recording.setStartTime(now); |
211 recording.setStartTime(now); |
211 recording.updateTimer(); |
212 recording.updateTimer(); |
212 Duration duration = recording.getDuration(); |
213 Duration duration = recording.getDuration(); |
213 if (duration != null) { |
214 if (duration != null) { |
214 recording.setStopTime(now.plus(duration)); |
215 recording.setStopTime(now.plus(duration)); |
215 } |
216 } |
216 boolean toDisk = recording.isToDisk(); |
217 boolean toDisk = recording.isToDisk(); |
217 boolean beginPhysical = true; |
218 boolean beginPhysical = true; |
|
219 long streamInterval = recording.getStreamIntervalMillis(); |
218 for (PlatformRecording s : getRecordings()) { |
220 for (PlatformRecording s : getRecordings()) { |
219 if (s.getState() == RecordingState.RUNNING) { |
221 if (s.getState() == RecordingState.RUNNING) { |
220 beginPhysical = false; |
222 beginPhysical = false; |
221 if (s.isToDisk()) { |
223 if (s.isToDisk()) { |
222 toDisk = true; |
224 toDisk = true; |
223 } |
225 } |
224 } |
226 streamInterval = Math.min(streamInterval, s.getStreamIntervalMillis()); |
225 } |
227 } |
|
228 } |
|
229 long startNanos = -1; |
226 if (beginPhysical) { |
230 if (beginPhysical) { |
227 RepositoryChunk newChunk = null; |
231 RepositoryChunk newChunk = null; |
228 if (toDisk) { |
232 if (toDisk) { |
229 newChunk = repository.newChunk(now); |
233 newChunk = repository.newChunk(now); |
230 MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString()); |
234 MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString()); |
231 } else { |
235 } else { |
232 MetadataRepository.getInstance().setOutput(null); |
236 MetadataRepository.getInstance().setOutput(null); |
233 } |
237 } |
234 currentChunk = newChunk; |
238 currentChunk = newChunk; |
235 jvm.beginRecording_(); |
239 jvm.beginRecording_(); |
|
240 startNanos = jvm.getChunkStartNanos(); |
236 recording.setState(RecordingState.RUNNING); |
241 recording.setState(RecordingState.RUNNING); |
237 updateSettings(); |
242 updateSettings(); |
238 writeMetaEvents(); |
243 writeMetaEvents(); |
239 } else { |
244 } else { |
240 RepositoryChunk newChunk = null; |
245 RepositoryChunk newChunk = null; |
241 if (toDisk) { |
246 if (toDisk) { |
242 newChunk = repository.newChunk(now); |
247 newChunk = repository.newChunk(now); |
243 RequestEngine.doChunkEnd(); |
248 RequestEngine.doChunkEnd(); |
244 MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString()); |
249 MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString()); |
|
250 startNanos = jvm.getChunkStartNanos(); |
245 } |
251 } |
246 recording.setState(RecordingState.RUNNING); |
252 recording.setState(RecordingState.RUNNING); |
247 updateSettings(); |
253 updateSettings(); |
248 writeMetaEvents(); |
254 writeMetaEvents(); |
249 if (currentChunk != null) { |
255 if (currentChunk != null) { |
250 finishChunk(currentChunk, now, recording); |
256 finishChunk(currentChunk, now, recording); |
251 } |
257 } |
252 currentChunk = newChunk; |
258 currentChunk = newChunk; |
253 } |
259 } |
254 |
260 if (toDisk) { |
|
261 RequestEngine.setFlushInterval(streamInterval); |
|
262 } |
255 RequestEngine.doChunkBegin(); |
263 RequestEngine.doChunkBegin(); |
|
264 |
|
265 return startNanos; |
256 } |
266 } |
257 |
267 |
258 synchronized void stop(PlatformRecording recording) { |
268 synchronized void stop(PlatformRecording recording) { |
259 RecordingState state = recording.getState(); |
269 RecordingState state = recording.getState(); |
260 |
270 |
265 throw new IllegalStateException("Recording must be started before it can be stopped."); |
275 throw new IllegalStateException("Recording must be started before it can be stopped."); |
266 } |
276 } |
267 Instant now = Instant.now(); |
277 Instant now = Instant.now(); |
268 boolean toDisk = false; |
278 boolean toDisk = false; |
269 boolean endPhysical = true; |
279 boolean endPhysical = true; |
|
280 long streamInterval = Long.MAX_VALUE; |
270 for (PlatformRecording s : getRecordings()) { |
281 for (PlatformRecording s : getRecordings()) { |
271 RecordingState rs = s.getState(); |
282 RecordingState rs = s.getState(); |
272 if (s != recording && RecordingState.RUNNING == rs) { |
283 if (s != recording && RecordingState.RUNNING == rs) { |
273 endPhysical = false; |
284 endPhysical = false; |
274 if (s.isToDisk()) { |
285 if (s.isToDisk()) { |
275 toDisk = true; |
286 toDisk = true; |
276 } |
287 } |
|
288 streamInterval = Math.min(streamInterval, s.getStreamIntervalMillis()); |
277 } |
289 } |
278 } |
290 } |
279 OldObjectSample.emit(recording); |
291 OldObjectSample.emit(recording); |
280 |
292 |
281 if (endPhysical) { |
293 if (endPhysical) { |
307 finishChunk(currentChunk, now, null); |
319 finishChunk(currentChunk, now, null); |
308 } |
320 } |
309 currentChunk = newChunk; |
321 currentChunk = newChunk; |
310 RequestEngine.doChunkBegin(); |
322 RequestEngine.doChunkBegin(); |
311 } |
323 } |
|
324 |
|
325 if (toDisk) { |
|
326 RequestEngine.setFlushInterval(streamInterval); |
|
327 } else { |
|
328 RequestEngine.setFlushInterval(Long.MAX_VALUE); |
|
329 } |
|
330 |
312 recording.setState(RecordingState.STOPPED); |
331 recording.setState(RecordingState.STOPPED); |
313 } |
332 } |
314 |
333 |
315 private void dumpMemoryToDestination(PlatformRecording recording) { |
334 private void dumpMemoryToDestination(PlatformRecording recording) { |
316 WriteableUserPath dest = recording.getDestination(); |
335 WriteableUserPath dest = recording.getDestination(); |
393 for (PlatformRecording r : getRecordings()) { |
424 for (PlatformRecording r : getRecordings()) { |
394 if (r != ignoreMe && r.getState() == RecordingState.RUNNING) { |
425 if (r != ignoreMe && r.getState() == RecordingState.RUNNING) { |
395 r.appendChunk(chunk); |
426 r.appendChunk(chunk); |
396 } |
427 } |
397 } |
428 } |
|
429 FilePurger.purge(); |
398 } |
430 } |
399 |
431 |
400 private void writeMetaEvents() { |
432 private void writeMetaEvents() { |
401 |
|
402 if (activeRecordingEvent.isEnabled()) { |
433 if (activeRecordingEvent.isEnabled()) { |
|
434 ActiveRecordingEvent event = ActiveRecordingEvent.EVENT.get(); |
403 for (PlatformRecording r : getRecordings()) { |
435 for (PlatformRecording r : getRecordings()) { |
404 if (r.getState() == RecordingState.RUNNING && r.shouldWriteMetadataEvent()) { |
436 if (r.getState() == RecordingState.RUNNING && r.shouldWriteMetadataEvent()) { |
405 ActiveRecordingEvent event = new ActiveRecordingEvent(); |
|
406 event.id = r.getId(); |
437 event.id = r.getId(); |
407 event.name = r.getName(); |
438 event.name = r.getName(); |
408 WriteableUserPath p = r.getDestination(); |
439 WriteableUserPath p = r.getDestination(); |
409 event.destination = p == null ? null : p.getRealPathText(); |
440 event.destination = p == null ? null : p.getRealPathText(); |
410 Duration d = r.getDuration(); |
441 Duration d = r.getDuration(); |
413 event.maxAge = age == null ? Long.MAX_VALUE : age.toMillis(); |
444 event.maxAge = age == null ? Long.MAX_VALUE : age.toMillis(); |
414 Long size = r.getMaxSize(); |
445 Long size = r.getMaxSize(); |
415 event.maxSize = size == null ? Long.MAX_VALUE : size; |
446 event.maxSize = size == null ? Long.MAX_VALUE : size; |
416 Instant start = r.getStartTime(); |
447 Instant start = r.getStartTime(); |
417 event.recordingStart = start == null ? Long.MAX_VALUE : start.toEpochMilli(); |
448 event.recordingStart = start == null ? Long.MAX_VALUE : start.toEpochMilli(); |
|
449 Duration fi = r.getFlushInterval(); |
|
450 event.flushInterval = fi == null ? Long.MAX_VALUE : fi.toMillis(); |
418 event.commit(); |
451 event.commit(); |
419 } |
452 } |
420 } |
453 } |
421 } |
454 } |
422 if (activeSettingEvent.isEnabled()) { |
455 if (activeSettingEvent.isEnabled()) { |