結論
C/C++ 側で Java のjdk.jfr.internal.EventWriter
が作成されます。このインスタンスにはThreadLocalに作られたJava用のバッファへのポインタを保持しています。イベントが書込まれるとEventWriter
はそのイベントをこのバッファへ書込みます。この書き込み処理は各イベントに commit
メソッドを使って行われます。このメソッドは親クラスであるEvent
クラスでは空実装ですが、jdk.jfr.FlightRecorder
クラスがイベントを読み込む際にバイトコードを操作されて、commit
メソッドはイベントが持っているフィールドを先ほどのバッファに書くように変更されます。
今回はこの様なイベントクラスを使って、その変化を確認していきます。
@Label("JfrEvent") public class JfrEvent extends Event { @Label("id") private int id; @Label("name") private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
JFR では、先ほどのイベントクラスを C/C++ レベルで書き換えます。
- @
src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp
またイベントに合わせたイベントハンドラはASM と言うツールを使ってバイトコードを動的に作成します。このイベントハンドラはjdk.jfr.internal.handlers.EventHandler
を継承しています。
- @
src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java
- @
src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriterMethod.java
イベントのクラスは以下の様に修正されます。
public class JfrEvent extends Event { private int id; private String name; private static EventHandler eventHandler; private transient long startTime; private transient long duration; public String getName() { return this.name; } public JfrEvent() { } static { FlightRecorder.register(JfrEvent.class); } public void begin() { this.startTime = EventHandler.timestamp(); } public void end() { this.duration = EventHandler.duration(this.startTime); } public void setName(String name) { this.name = name; } public int getId() { return this.id; } public void setId(int id) { this.id = id; } public void commit() { if (this.isEnabled()) { if (this.startTime == 0L) { this.startTime = EventHandler.timestamp(); } else if (this.duration == 0L) { this.duration = EventHandler.timestamp() - this.startTime; } if (this.shouldCommit()) { ((EventHandler1754_1595079330631_153352)eventHandler).write(this.startTime, this.duration, this.id, this.name); } } } public boolean isEnabled() { return eventHandler.isEnabled(); } public boolean shouldCommit() { return eventHandler.shouldCommit(this.duration); } }
また、このイベントクラス用のイベントハンドラは以下の様に作られます。
public final class EventHandler1754_1595079330631-153352 extends EventHandler { private final StringPool stringPool3 = this.createStringFieldWriter(); private EventHandler1754_1595079330631_153352(boolean registered, EventType eventType, EventControl eventControl) { super(registered, eventType, eventControl); } public void write(long startTime, long duration, int id, String name) { EventWriter eventWriter; try { do { eventWriter= EventWriter.getEventWriter(); if (!eventWriter.beginEvent(super.platformEventType)) { break; } eventWriter.putLong(startTime); eventWriter.putLong(duration); eventWriter.putEventThread(); eventWriter.putStackTrace(); eventWriter.putInt(id); eventWriter.putString(name, this.stringPool3); } while(!eventWriter.endEvent()); } catch (Throwable t) { EventWriter eventWriter2 = EventWriter.getEventWriter(); if (eventWriter2 != null) { eventWriter2.reset(); } throw t; } } }