static const char klass[] = "jdk/jfr/internal/dcmd/DCmdCheck";
static const char method[] = "execute";
static const char signature[] = "(Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/String;";
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
execute_args.set_receiver(h_dcmd_instance);
execute_args.push_jobject(name);
execute_args.push_jobject(verbose);
JfrJavaSupport::call_virtual(&execute_args, THREAD);
handle_dcmd_result(output(), (oop)result.get_jobject(), source, THREAD);
}
JfrStartFlightRecordingDCmd::JfrStartFlightRecordingDCmd(outputStream* output,
bool heap) : DCmdWithParser(output, heap),
_name("name", "Name that can be used to identify recording, e.g. \\\"My Recording\\\"", "STRING", false, NULL),
_settings("settings", "Settings file(s), e.g. profile or default. See JRE_HOME/lib/jfr", "STRING SET", false),
_delay("delay", "Delay recording start with (s)econds, (m)inutes), (h)ours), or (d)ays, e.g. 5h.", "NANOTIME", false, "0"),
_duration("duration", "Duration of recording in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 300s.", "NANOTIME", false, "0"),
_filename("filename", "Resulting recording filename, e.g. \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", false),
_disk("disk", "Recording should be persisted to disk", "BOOLEAN", false),
_maxage("maxage", "Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, "0"),
_maxsize("maxsize", "Maximum amount of bytes to keep (on disk) in (k)B, (M)B or (G)B, e.g. 500M, or 0 for no limit", "MEMORY SIZE", false, "0"),
_dump_on_exit("dumponexit", "Dump running recording when JVM shuts down", "BOOLEAN", false),
_path_to_gc_roots("path-to-gc-roots", "Collect path to GC roots", "BOOLEAN", false, "false") {
_dcmdparser.add_dcmd_option(&_name);
_dcmdparser.add_dcmd_option(&_settings);
_dcmdparser.add_dcmd_option(&_delay);
_dcmdparser.add_dcmd_option(&_duration);
_dcmdparser.add_dcmd_option(&_disk);
_dcmdparser.add_dcmd_option(&_filename);
_dcmdparser.add_dcmd_option(&_maxage);
_dcmdparser.add_dcmd_option(&_maxsize);
_dcmdparser.add_dcmd_option(&_dump_on_exit);
_dcmdparser.add_dcmd_option(&_path_to_gc_roots);
};
int JfrStartFlightRecordingDCmd::num_arguments() {
ResourceMark rm;
JfrStartFlightRecordingDCmd* dcmd = new JfrStartFlightRecordingDCmd(NULL, false);
if (dcmd != NULL) {
DCmdMark mark(dcmd);
return dcmd->_dcmdparser.num_arguments();
}
return 0;
}
void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
if (invalid_state(output(), THREAD)) {
return;
}
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
JNIHandleBlockManager jni_handle_management(THREAD);
JavaValue result(T_OBJECT);
JfrJavaArguments constructor_args(&result);
constructor_args.set_klass("jdk/jfr/internal/dcmd/DCmdStart", THREAD);
const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK);
Handle h_dcmd_instance(THREAD, dcmd);
assert(h_dcmd_instance.not_null(), "invariant");
jstring name = NULL;
if (_name.is_set() && _name.value() != NULL) {
name = JfrJavaSupport::new_string(_name.value(), CHECK);
}
jstring filename = NULL;
if (_filename.is_set() && _filename.value() != NULL) {
filename = JfrJavaSupport::new_string(_filename.value(), CHECK);
}
jobject maxage = NULL;
if (_maxage.is_set()) {
maxage = JfrJavaSupport::new_java_lang_Long(_maxage.value()._nanotime, CHECK);
}
jobject maxsize = NULL;
if (_maxsize.is_set()) {
maxsize = JfrJavaSupport::new_java_lang_Long(_maxsize.value()._size, CHECK);
}
jobject duration = NULL;
if (_duration.is_set()) {
duration = JfrJavaSupport::new_java_lang_Long(_duration.value()._nanotime, CHECK);
}
jobject delay = NULL;
if (_delay.is_set()) {
delay = JfrJavaSupport::new_java_lang_Long(_delay.value()._nanotime, CHECK);
}
jobject disk = NULL;
if (_disk.is_set()) {
disk = JfrJavaSupport::new_java_lang_Boolean(_disk.value(), CHECK);
}
jobject dump_on_exit = NULL;
if (_dump_on_exit.is_set()) {
dump_on_exit = JfrJavaSupport::new_java_lang_Boolean(_dump_on_exit.value(), CHECK);
}
jobject path_to_gc_roots = NULL;
if (_path_to_gc_roots.is_set()) {
path_to_gc_roots = JfrJavaSupport::new_java_lang_Boolean(_path_to_gc_roots.value(), CHECK);
}
jobjectArray settings = NULL;
if (_settings.is_set()) {
int length = _settings.value()->array()->length();
if (length == 1) {
const char* c_str = _settings.value()->array()->at(0);
if (strcmp(c_str, "none") == 0) {
length = 0;
}
}
settings = JfrJavaSupport::new_string_array(length, CHECK);
assert(settings != NULL, "invariant");
for (int i = 0; i < length; ++i) {
jobject element = JfrJavaSupport::new_string(_settings.value()->array()->at(i), CHECK);
assert(element != NULL, "invariant");
JfrJavaSupport::set_array_element(settings, element, i, CHECK);
}
} else {
settings = JfrJavaSupport::new_string_array(1, CHECK);
assert(settings != NULL, "invariant");
jobject element = JfrJavaSupport::new_string("default", CHECK);
assert(element != NULL, "invariant");
JfrJavaSupport::set_array_element(settings, element, 0, CHECK);
}
static const char klass[] = "jdk/jfr/internal/dcmd/DCmdStart";
static const char method[] = "execute";
static const char signature[] = "(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Long;"
"Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;"
"Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)Ljava/lang/String;";
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
execute_args.set_receiver(h_dcmd_instance);
execute_args.push_jobject(name);
execute_args.push_jobject(settings);
execute_args.push_jobject(delay);
execute_args.push_jobject(duration);
execute_args.push_jobject(disk);
execute_args.push_jobject(filename);
execute_args.push_jobject(maxage);
execute_args.push_jobject(maxsize);
execute_args.push_jobject(dump_on_exit);
execute_args.push_jobject(path_to_gc_roots);
JfrJavaSupport::call_virtual(&execute_args, THREAD);
handle_dcmd_result(output(), (oop)result.get_jobject(), source, THREAD);
}
JfrStopFlightRecordingDCmd::JfrStopFlightRecordingDCmd(outputStream* output,
bool heap) : DCmdWithParser(output, heap),
_name("name", "Recording text,.e.g \\\"My Recording\\\"", "STRING", true, NULL),
_filename("filename", "Copy recording data to file, e.g. \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", false, NULL) {
_dcmdparser.add_dcmd_option(&_name);
_dcmdparser.add_dcmd_option(&_filename);
};
int JfrStopFlightRecordingDCmd::num_arguments() {
ResourceMark rm;
JfrStopFlightRecordingDCmd* dcmd = new JfrStopFlightRecordingDCmd(NULL, false);
if (dcmd != NULL) {
DCmdMark mark(dcmd);
return dcmd->_dcmdparser.num_arguments();
}
return 0;
}
void JfrStopFlightRecordingDCmd::execute(DCmdSource source, TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
if (invalid_state(output(), THREAD) || !is_recorder_instance_created(output())) {
return;
}
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
JNIHandleBlockManager jni_handle_management(THREAD);
JavaValue result(T_OBJECT);
JfrJavaArguments constructor_args(&result);
constructor_args.set_klass("jdk/jfr/internal/dcmd/DCmdStop", CHECK);
const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK);
Handle h_dcmd_instance(THREAD, dcmd);
assert(h_dcmd_instance.not_null(), "invariant");
jstring name = NULL;
if (_name.is_set() && _name.value() != NULL) {
name = JfrJavaSupport::new_string(_name.value(), CHECK);
}
jstring filepath = NULL;
if (_filename.is_set() && _filename.value() != NULL) {
filepath = JfrJavaSupport::new_string(_filename.value(), CHECK);
}
static const char klass[] = "jdk/jfr/internal/dcmd/DCmdStop";
static const char method[] = "execute";
static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
execute_args.set_receiver(h_dcmd_instance);
execute_args.push_jobject(name);
execute_args.push_jobject(filepath);
JfrJavaSupport::call_virtual(&execute_args, THREAD);
handle_dcmd_result(output(), (oop)result.get_jobject(), source, THREAD);
}
JfrConfigureFlightRecorderDCmd::JfrConfigureFlightRecorderDCmd(outputStream* output,
bool heap) : DCmdWithParser(output, heap),
_repository_path("repositorypath", "Path to repository,.e.g \\\"My Repository\\\"", "STRING", false, NULL),
_dump_path("dumppath", "Path to dump,.e.g \\\"My Dump path\\\"", "STRING", false, NULL),
_stack_depth("stackdepth", "Stack Depth", "JULONG", false, "64"),
_global_buffer_count("globalbuffercount", "Number of global buffers,", "JULONG", false, "20"),
_global_buffer_size("globalbuffersize", "Size of a global buffers,", "MEMORY SIZE", false, "512k"),
_thread_buffer_size("thread_buffer_size", "Size of a thread buffer", "MEMORY SIZE", false, "8k"),
_memory_size("memorysize", "Overall memory size, ", "MEMORY SIZE", false, "10m"),
_max_chunk_size("maxchunksize", "Size of an individual disk chunk", "MEMORY SIZE", false, "12m"),
_sample_threads("samplethreads", "Activate Thread sampling", "BOOLEAN", false, "true") {
_dcmdparser.add_dcmd_option(&_repository_path);
_dcmdparser.add_dcmd_option(&_dump_path);
_dcmdparser.add_dcmd_option(&_stack_depth);
_dcmdparser.add_dcmd_option(&_global_buffer_count);
_dcmdparser.add_dcmd_option(&_global_buffer_size);
_dcmdparser.add_dcmd_option(&_thread_buffer_size);
_dcmdparser.add_dcmd_option(&_memory_size);
_dcmdparser.add_dcmd_option(&_max_chunk_size);
_dcmdparser.add_dcmd_option(&_sample_threads);
};
int JfrConfigureFlightRecorderDCmd::num_arguments() {
ResourceMark rm;
JfrConfigureFlightRecorderDCmd* dcmd = new JfrConfigureFlightRecorderDCmd(NULL, false);
if (dcmd != NULL) {
DCmdMark mark(dcmd);
return dcmd->_dcmdparser.num_arguments();
}
return 0;
}
void JfrConfigureFlightRecorderDCmd::execute(DCmdSource source, TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
if (invalid_state(output(), THREAD)) {
return;
}
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
JNIHandleBlockManager jni_handle_management(THREAD);
JavaValue result(T_OBJECT);
JfrJavaArguments constructor_args(&result);
constructor_args.set_klass("jdk/jfr/internal/dcmd/DCmdConfigure", CHECK);
const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK);
Handle h_dcmd_instance(THREAD, dcmd);
assert(h_dcmd_instance.not_null(), "invariant");
jstring repository_path = NULL;
if (_repository_path.is_set() && _repository_path.value() != NULL) {
repository_path = JfrJavaSupport::new_string(_repository_path.value(), CHECK);
}
jstring dump_path = NULL;
if (_dump_path.is_set() && _dump_path.value() != NULL) {
dump_path = JfrJavaSupport::new_string(_dump_path.value(), CHECK);
}
jobject stack_depth = NULL;
if (_stack_depth.is_set()) {
stack_depth = JfrJavaSupport::new_java_lang_Integer((jint)_stack_depth.value(), CHECK);
}
jobject global_buffer_count = NULL;
if (_global_buffer_count.is_set()) {
global_buffer_count = JfrJavaSupport::new_java_lang_Long(_global_buffer_count.value(), CHECK);
}
jobject global_buffer_size = NULL;
if (_global_buffer_size.is_set()) {
global_buffer_size = JfrJavaSupport::new_java_lang_Long(_global_buffer_size.value()._size, CHECK);
}
jobject thread_buffer_size = NULL;
if (_thread_buffer_size.is_set()) {
thread_buffer_size = JfrJavaSupport::new_java_lang_Long(_thread_buffer_size.value()._size, CHECK);
}
jobject max_chunk_size = NULL;
if (_max_chunk_size.is_set()) {
max_chunk_size = JfrJavaSupport::new_java_lang_Long(_max_chunk_size.value()._size, CHECK);
}
jobject memory_size = NULL;
if (_memory_size.is_set()) {
memory_size = JfrJavaSupport::new_java_lang_Long(_memory_size.value()._size, CHECK);
}
jobject sample_threads = NULL;
if (_sample_threads.is_set()) {
sample_threads = JfrJavaSupport::new_java_lang_Boolean(_sample_threads.value(), CHECK);
}
static const char klass[] = "jdk/jfr/internal/dcmd/DCmdConfigure";
static const char method[] = "execute";
static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;"
"Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;"
"Ljava/lang/Long;Ljava/lang/Boolean;)Ljava/lang/String;";
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
execute_args.set_receiver(h_dcmd_instance);
execute_args.push_jobject(repository_path);
execute_args.push_jobject(dump_path);
execute_args.push_jobject(stack_depth);
execute_args.push_jobject(global_buffer_count);
execute_args.push_jobject(global_buffer_size);
execute_args.push_jobject(thread_buffer_size);
execute_args.push_jobject(memory_size);
execute_args.push_jobject(max_chunk_size);
execute_args.push_jobject(sample_threads);
JfrJavaSupport::call_virtual(&execute_args, THREAD);
handle_dcmd_result(output(), (oop)result.get_jobject(), source, THREAD);
}
bool register_jfr_dcmds() {
uint32_t full_export = DCmd_Source_Internal | DCmd_Source_AttachAPI | DCmd_Source_MBean;
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrCheckFlightRecordingDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrDumpFlightRecordingDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrStartFlightRecordingDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrStopFlightRecordingDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrConfigureFlightRecorderDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrUnlockCommercialFeaturesDCmd>(full_export, true, false));
return true;
}
C:\hotspot-69087d08d473\src\share\vm/jfr/dcmd/jfrDcmds.hpp
#ifndef SHARE_VM_JFR_JFRDCMDS_HPP
#define SHARE_VM_JFR_JFRDCMDS_HPP
#include "services/diagnosticCommand.hpp"
class JfrDumpFlightRecordingDCmd : public DCmdWithParser {
protected:
DCmdArgument<char*> _name;
DCmdArgument<char*> _filename;
DCmdArgument<NanoTimeArgument> _maxage;
DCmdArgument<MemorySizeArgument> _maxsize;
DCmdArgument<char*> _begin;
DCmdArgument<char*> _end;
DCmdArgument<bool> _path_to_gc_roots;
public:
JfrDumpFlightRecordingDCmd(outputStream* output, bool heap);
static const char* name() {
return "JFR.dump";
}
static const char* description() {
return "Copies contents of a JFR recording to file. Either the name or the recording id must be specified.";
}
static const char* impact() {
return "Low";
}
static const JavaPermission permission() {
JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL};
return p;
}
static int num_arguments();
virtual void execute(DCmdSource source, TRAPS);
};
class JfrCheckFlightRecordingDCmd : public DCmdWithParser {
protected:
DCmdArgument<char*> _name;
DCmdArgument<bool> _verbose;
public:
JfrCheckFlightRecordingDCmd(outputStream* output, bool heap);
static const char* name() {
return "JFR.check";
}
static const char* description() {
return "Checks running JFR recording(s)";
}
static const char* impact() {
return "Low";
}
static const JavaPermission permission() {
JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL};
return p;
}
static int num_arguments();
virtual void execute(DCmdSource source, TRAPS);
};
class JfrStartFlightRecordingDCmd : public DCmdWithParser {
protected:
DCmdArgument<char*> _name;
DCmdArgument<StringArrayArgument*> _settings;
DCmdArgument<NanoTimeArgument> _delay;
DCmdArgument<NanoTimeArgument> _duration;
DCmdArgument<bool> _disk;
DCmdArgument<char*> _filename;
DCmdArgument<NanoTimeArgument> _maxage;
DCmdArgument<MemorySizeArgument> _maxsize;
DCmdArgument<bool> _dump_on_exit;
DCmdArgument<bool> _path_to_gc_roots;
public:
JfrStartFlightRecordingDCmd(outputStream* output, bool heap);
static const char* name() {
return "JFR.start";
}
static const char* description() {
return "Starts a new JFR recording";
}
static const char* impact() {
return "Medium: Depending on the settings for a recording, the impact can range from low to high.";
}
static const JavaPermission permission() {
JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL};
return p;
}
static int num_arguments();
virtual void execute(DCmdSource source, TRAPS);
};
class JfrStopFlightRecordingDCmd : public DCmdWithParser {
protected:
DCmdArgument<char*> _name;
DCmdArgument<char*> _filename;
public:
JfrStopFlightRecordingDCmd(outputStream* output, bool heap);
static const char* name() {
return "JFR.stop";
}
static const char* description() {
return "Stops a JFR recording";
}
static const char* impact() {
return "Low";
}
static const JavaPermission permission() {
JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL};
return p;
}
static int num_arguments();
virtual void execute(DCmdSource source, TRAPS);
};
class JfrRuntimeOptions;
class JfrConfigureFlightRecorderDCmd : public DCmdWithParser {
friend class JfrOptionSet;
protected:
DCmdArgument<char*> _repository_path;
DCmdArgument<char*> _dump_path;
DCmdArgument<jlong> _stack_depth;
DCmdArgument<jlong> _global_buffer_count;
DCmdArgument<MemorySizeArgument> _global_buffer_size;
DCmdArgument<MemorySizeArgument> _thread_buffer_size;
DCmdArgument<MemorySizeArgument> _memory_size;
DCmdArgument<MemorySizeArgument> _max_chunk_size;
DCmdArgument<bool> _sample_threads;
public:
JfrConfigureFlightRecorderDCmd(outputStream* output, bool heap);
static const char* name() {
return "JFR.configure";
}
static const char* description() {
return "Configure JFR";
}
static const char* impact() {
return "Low";
}
static const JavaPermission permission() {
JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL};
return p;
}
static int num_arguments();
virtual void execute(DCmdSource source, TRAPS);
};
class JfrUnlockCommercialFeaturesDCmd : public DCmd {
public:
JfrUnlockCommercialFeaturesDCmd(outputStream* output, bool heap) : DCmd(output, heap) { }
static const char* name() { return "VM.unlock_commercial_features"; }
static const char* description() {
return "Simulate commercial features unlocking for Zulu.";
}
static const char* impact() { return "Low"; }
static const JavaPermission permission() {
JavaPermission p = {"java.lang.management.ManagementPermission",
"monitor", NULL};
return p;
}
static int num_arguments() { return 0; }
virtual void execute(DCmdSource source, TRAPS) {
UnlockCommercialFeatures = true;
}
};
bool register_jfr_dcmds();
#endif // SHARE_VM_JFR_JFRDCMDS_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/GenerateJfrFiles.java
package build.tools.jfr;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.validation.SchemaFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
public class GenerateJfrFiles {
public static void main(String... args) throws Exception {
if (args.length != 3) {
System.err.println("Incorrect number of command line arguments.");
System.err.println("Usage:");
System.err.println("java GenerateJfrFiles[.java] <path-to-metadata.xml> <path-to-metadata.xsd> <output-directory>");
System.exit(1);
}
try {
File metadataXml = new File(args[0]);
File metadataSchema = new File(args[1]);
File outputDirectory = new File(args[2]);
Metadata metadata = new Metadata(metadataXml, metadataSchema);
metadata.verify();
metadata.wireUpTypes();
printJfrPeriodicHpp(metadata, outputDirectory);
printJfrEventIdsHpp(metadata, outputDirectory);
printJfrEventControlHpp(metadata, outputDirectory);
printJfrTypesHpp(metadata, outputDirectory);
printJfrEventClassesHpp(metadata, outputDirectory);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
static class XmlType {
final String fieldType;
final String parameterType;
XmlType(String fieldType, String parameterType) {
this.fieldType = fieldType;
this.parameterType = parameterType;
}
}
static class TypeElement {
List<FieldElement> fields = new ArrayList<>();
String name;
String fieldType;
String parameterType;
boolean supportStruct;
}
interface TypePredicate {
boolean isType(TypeElement type);
}
static class StringJoiner {
private final CharSequence delimiter;
private final List<CharSequence> elements;
public StringJoiner(CharSequence delimiter) {
this.delimiter = delimiter;
elements = new LinkedList<CharSequence>();
}
public StringJoiner add(CharSequence newElement) {
elements.add(newElement);
return this;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
Iterator<CharSequence> i = elements.iterator();
while (i.hasNext()) {
builder.append(i.next());
if (i.hasNext()) {
builder.append(delimiter);
}
}
return builder.toString();
}
}
static class Metadata {
final Map<String, TypeElement> types = new LinkedHashMap<>();
final Map<String, XmlType> xmlTypes = new HashMap<>();
Metadata(File metadataXml, File metadataSchema) throws ParserConfigurationException, SAXException, FileNotFoundException, IOException {
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setSchema(schemaFactory.newSchema(metadataSchema));
SAXParser sp = factory.newSAXParser();
sp.parse(metadataXml, new MetadataHandler(this));
}
List<EventElement> getEvents() {
return getList(new TypePredicate() {
@Override
public boolean isType(TypeElement t) {
return t.getClass() == EventElement.class;
}
});
}
List<TypeElement> getEventsAndStructs() {
return getList(new TypePredicate() {
@Override
public boolean isType(TypeElement t) {
return t.getClass() == EventElement.class || t.supportStruct;
}
});
}
List<TypeElement> getTypesAndStructs() {
return getList(new TypePredicate() {
@Override
public boolean isType(TypeElement t) {
return t.getClass() == TypeElement.class || t.supportStruct;
}
});
}
@SuppressWarnings("unchecked")
<T> List<T> getList(TypePredicate pred) {
List<T> result = new ArrayList<>(types.size());
for (TypeElement t : types.values()) {
if (pred.isType(t)) {
result.add((T) t);
}
}
return result;
}
List<EventElement> getPeriodicEvents() {
return getList(new TypePredicate() {
@Override
public boolean isType(TypeElement t) {
return t.getClass() == EventElement.class && ((EventElement) t).periodic;
}
});
}
List<TypeElement> getNonEventsAndNonStructs() {
return getList(new TypePredicate() {
@Override
public boolean isType(TypeElement t) {
return t.getClass() != EventElement.class && !t.supportStruct;
}
});
}
List<TypeElement> getTypes() {
return getList(new TypePredicate() {
@Override
public boolean isType(TypeElement t) {
return t.getClass() == TypeElement.class && !t.supportStruct;
}
});
}
List<TypeElement> getStructs() {
return getList(new TypePredicate() {
@Override
public boolean isType(TypeElement t) {
return t.getClass() == TypeElement.class && t.supportStruct;
}
});
}
void verify() {
for (TypeElement t : types.values()) {
for (FieldElement f : t.fields) {
if (!xmlTypes.containsKey(f.typeName)) { // ignore primitives
if (!types.containsKey(f.typeName)) {
throw new IllegalStateException("Could not find definition of type '" + f.typeName + "' used by " + t.name + "#" + f.name);
}
}
}
}
}
void wireUpTypes() {
for (TypeElement t : types.values()) {
for (FieldElement f : t.fields) {
TypeElement type = types.get(f.typeName);
if (f.struct) {
type.supportStruct = true;
}
f.type = type;
}
}
}
}
static class EventElement extends TypeElement {
String representation;
boolean thread;
boolean stackTrace;
boolean startTime;
boolean periodic;
boolean cutoff;
}
static class FieldElement {
final Metadata metadata;
TypeElement type;
String name;
String typeName;
boolean struct;
FieldElement(Metadata metadata) {
this.metadata = metadata;
}
String getParameterType() {
if (struct) {
return "const JfrStruct" + typeName + "&";
}
XmlType xmlType = metadata.xmlTypes.get(typeName);
if (xmlType != null) {
return xmlType.parameterType;
}
return type != null ? "u8" : typeName;
}
String getParameterName() {
return struct ? "value" : "new_value";
}
String getFieldType() {
if (struct) {
return "JfrStruct" + typeName;
}
XmlType xmlType = metadata.xmlTypes.get(typeName);
if (xmlType != null) {
return xmlType.fieldType;
}
return type != null ? "u8" : typeName;
}
}
static class MetadataHandler extends DefaultHandler {
final Metadata metadata;
FieldElement currentField;
TypeElement currentType;
MetadataHandler(Metadata metadata) {
this.metadata = metadata;
}
@Override
public void error(SAXParseException e) throws SAXException {
throw e;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
switch (qName) {
case "XmlType":
String name = attributes.getValue("name");
String parameterType = attributes.getValue("parameterType");
String fieldType = attributes.getValue("fieldType");
metadata.xmlTypes.put(name, new XmlType(fieldType, parameterType));
break;
case "Type":
currentType = new TypeElement();
currentType.name = attributes.getValue("name");
break;
case "Event":
EventElement eventtType = new EventElement();
eventtType.name = attributes.getValue("name");
eventtType.thread = getBoolean(attributes, "thread", false);
eventtType.stackTrace = getBoolean(attributes, "stackTrace", false);
eventtType.startTime = getBoolean(attributes, "startTime", true);
eventtType.periodic = attributes.getValue("period") != null;
eventtType.cutoff = getBoolean(attributes, "cutoff", false);
currentType = eventtType;
break;
case "Field":
currentField = new FieldElement(metadata);
currentField.struct = getBoolean(attributes, "struct", false);
currentField.name = attributes.getValue("name");
currentField.typeName = attributes.getValue("type");
break;
}
}
private boolean getBoolean(Attributes attributes, String name, boolean defaultValue) {
String value = attributes.getValue(name);
return value == null ? defaultValue : Boolean.valueOf(value);
}
@Override
public void endElement(String uri, String localName, String qName) {
switch (qName) {
case "Type":
case "Event":
metadata.types.put(currentType.name, currentType);
currentType = null;
break;
case "Field":
currentType.fields.add(currentField);
currentField = null;
break;
}
}
}
static class Printer implements AutoCloseable {
final PrintStream out;
Printer(File outputDirectory, String filename) throws FileNotFoundException {
out = new PrintStream(new BufferedOutputStream(new FileOutputStream(new File(outputDirectory, filename))));
write("/* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */");
write("");
}
void write(String text) {
out.print(text);
out.print("\n"); // Don't use Windows line endings
}
@Override
public void close() throws Exception {
out.close();
}
}
private static void printJfrPeriodicHpp(Metadata metadata, File outputDirectory) throws Exception {
try (Printer out = new Printer(outputDirectory, "jfrPeriodic.hpp")) {
out.write("#ifndef JFRFILES_JFRPERIODICEVENTSET_HPP");
out.write("#define JFRFILES_JFRPERIODICEVENTSET_HPP");
out.write("");
out.write("#include \"utilities/macros.hpp\"");
out.write("#if INCLUDE_JFR");
out.write("#include \"jfrfiles/jfrEventIds.hpp\"");
out.write("#include \"memory/allocation.hpp\"");
out.write("");
out.write("class JfrPeriodicEventSet : public AllStatic {");
out.write(" public:");
out.write(" static void requestEvent(JfrEventId id) {");
out.write(" switch(id) {");
out.write(" ");
for (EventElement e : metadata.getPeriodicEvents()) {
out.write(" case Jfr" + e.name + "Event:");
out.write(" request" + e.name + "();");
out.write(" break;");
out.write(" ");
}
out.write(" default:");
out.write(" break;");
out.write(" }");
out.write(" }");
out.write("");
out.write(" private:");
out.write("");
for (EventElement e : metadata.getPeriodicEvents()) {
out.write(" static void request" + e.name + "(void);");
out.write("");
}
out.write("};");
out.write("");
out.write("#endif // INCLUDE_JFR");
out.write("#endif // JFRFILES_JFRPERIODICEVENTSET_HPP");
}
}
private static void printJfrEventControlHpp(Metadata metadata, File outputDirectory) throws Exception {
try (Printer out = new Printer(outputDirectory, "jfrEventControl.hpp")) {
out.write("#ifndef JFRFILES_JFR_NATIVE_EVENTSETTING_HPP");
out.write("#define JFRFILES_JFR_NATIVE_EVENTSETTING_HPP");
out.write("");
out.write("#include \"utilities/macros.hpp\"");
out.write("#if INCLUDE_JFR");
out.write("#include \"jfrfiles/jfrEventIds.hpp\"");
out.write("");
out.write("/**");
out.write(" * Event setting. We add some padding so we can use our");
out.write(" * event IDs as indexes into this.");
out.write(" */");
out.write("");
out.write("struct jfrNativeEventSetting {");
out.write(" jlong threshold_ticks;");
out.write(" jlong cutoff_ticks;");
out.write(" u1 stacktrace;");
out.write(" u1 enabled;");
out.write(" u1 pad[6]; // Because GCC on linux ia32 at least tries to pack this.");
out.write("};");
out.write("");
out.write("union JfrNativeSettings {");
out.write(" // Array version.");
out.write(" jfrNativeEventSetting bits[MaxJfrEventId];");
out.write(" // Then, to make it easy to debug,");
out.write(" // add named struct members also.");
out.write(" struct {");
out.write(" jfrNativeEventSetting pad[NUM_RESERVED_EVENTS];");
for (TypeElement t : metadata.getEventsAndStructs()) {
out.write(" jfrNativeEventSetting " + t.name + ";");
}
out.write(" } ev;");
out.write("};");
out.write("");
out.write("#endif // INCLUDE_JFR");
out.write("#endif // JFRFILES_JFR_NATIVE_EVENTSETTING_HPP");
}
}
private static void printJfrEventIdsHpp(Metadata metadata, File outputDirectory) throws Exception {
try (Printer out = new Printer(outputDirectory, "jfrEventIds.hpp")) {
out.write("#ifndef JFRFILES_JFREVENTIDS_HPP");
out.write("#define JFRFILES_JFREVENTIDS_HPP");
out.write("");
out.write("#include \"utilities/macros.hpp\"");
out.write("#if INCLUDE_JFR");
out.write("#include \"jfrfiles/jfrTypes.hpp\"");
out.write("");
out.write("/**");
out.write(" * Enum of the event types in the JVM");
out.write(" */");
out.write("enum JfrEventId {");
out.write(" _jfreventbase = (NUM_RESERVED_EVENTS-1), // Make sure we start at right index.");
out.write(" ");
out.write(" // Events -> enum entry");
for (TypeElement t : metadata.getEventsAndStructs()) {
out.write(" Jfr" + t.name + "Event,");
}
out.write("");
out.write(" MaxJfrEventId");
out.write("};");
out.write("");
out.write("/**");
out.write(" * Struct types in the JVM");
out.write(" */");
out.write("enum JfrStructId {");
for (TypeElement t : metadata.getNonEventsAndNonStructs()) {
out.write(" Jfr" + t.name + "Struct,");
}
for (TypeElement t : metadata.getEventsAndStructs()) {
out.write(" Jfr" + t.name + "Struct,");
}
out.write("");
out.write(" MaxJfrStructId");
out.write("};");
out.write("");
out.write("typedef enum JfrEventId JfrEventId;");
out.write("typedef enum JfrStructId JfrStructId;");
out.write("");
out.write("#endif // INCLUDE_JFR");
out.write("#endif // JFRFILES_JFREVENTIDS_HPP");
}
}
private static void printJfrTypesHpp(Metadata metadata, File outputDirectory) throws Exception {
List<String> knownTypes = Arrays.asList(new String[] {"Thread", "StackTrace", "Class", "StackFrame"});
try (Printer out = new Printer(outputDirectory, "jfrTypes.hpp")) {
out.write("#ifndef JFRFILES_JFRTYPES_HPP");
out.write("#define JFRFILES_JFRTYPES_HPP");
out.write("");
out.write("#include \"utilities/macros.hpp\"");
out.write("#if INCLUDE_JFR");
out.write("");
out.write("enum JfrTypeId {");
out.write(" TYPE_NONE = 0,");
out.write(" TYPE_CLASS = 20,");
out.write(" TYPE_STRING = 21,");
out.write(" TYPE_THREAD = 22,");
out.write(" TYPE_STACKTRACE = 23,");
out.write(" TYPE_BYTES = 24,");
out.write(" TYPE_EPOCHMILLIS = 25,");
out.write(" TYPE_MILLIS = 26,");
out.write(" TYPE_NANOS = 27,");
out.write(" TYPE_TICKS = 28,");
out.write(" TYPE_ADDRESS = 29,");
out.write(" TYPE_PERCENTAGE = 30,");
out.write(" TYPE_DUMMY,");
out.write(" TYPE_DUMMY_1,");
for (TypeElement type : metadata.getTypes()) {
if (!knownTypes.contains(type.name)) {
out.write(" TYPE_" + type.name.toUpperCase() + ",");
}
}
out.write("");
out.write(" NUM_JFR_TYPES,");
out.write(" TYPES_END = 255");
out.write("};");
out.write("");
out.write("enum ReservedEvent {");
out.write(" EVENT_METADATA,");
out.write(" EVENT_CHECKPOINT,");
out.write(" EVENT_BUFFERLOST,");
out.write(" NUM_RESERVED_EVENTS = TYPES_END");
out.write("};");
out.write("");
out.write("#endif // INCLUDE_JFR");
out.write("#endif // JFRFILES_JFRTYPES_HPP");
};
}
private static void printJfrEventClassesHpp(Metadata metadata, File outputDirectory) throws Exception {
try (Printer out = new Printer(outputDirectory, "jfrEventClasses.hpp")) {
out.write("#ifndef JFRFILES_JFREVENTCLASSES_HPP");
out.write("#define JFRFILES_JFREVENTCLASSES_HPP");
out.write("");
out.write("#include \"oops/klass.hpp\"");
out.write("#include \"jfrfiles/jfrTypes.hpp\"");
out.write("#include \"jfr/utilities/jfrTypes.hpp\"");
out.write("#include \"utilities/macros.hpp\"");
out.write("#include \"utilities/ticks.hpp\"");
out.write("#if INCLUDE_JFR");
out.write("#include \"jfr/recorder/service/jfrEvent.hpp\"");
out.write("/*");
out.write(" * Each event class has an assert member function verify() which is invoked");
out.write(" * just before the engine writes the event and its fields to the data stream.");
out.write(" * The purpose of verify() is to ensure that all fields in the event are initialized");
out.write(" * and set before attempting to commit.");
out.write(" *");
out.write(" * We enforce this requirement because events are generally stack allocated and therefore");
out.write(" * *not* initialized to default values. This prevents us from inadvertently committing");
out.write(" * uninitialized values to the data stream.");
out.write(" *");
out.write(" * The assert message contains both the index (zero based) as well as the name of the field.");
out.write(" */");
out.write("");
printTypes(out, metadata, false);
out.write("");
out.write("");
out.write("#else // !INCLUDE_JFR");
out.write("");
out.write("template <typename T>");
out.write("class JfrEvent {");
out.write(" public:");
out.write(" JfrEvent() {}");
out.write(" void set_starttime(const Ticks&) const {}");
out.write(" void set_endtime(const Ticks&) const {}");
out.write(" bool should_commit() const { return false; }");
out.write(" static bool is_enabled() { return false; }");
out.write(" void commit() {}");
out.write("};");
out.write("");
printTypes(out, metadata, true);
out.write("");
out.write("");
out.write("#endif // INCLUDE_JFR");
out.write("#endif // JFRFILES_JFREVENTCLASSES_HPP");
}
}
private static void printTypes(Printer out, Metadata metadata, boolean empty) {
for (TypeElement t : metadata.getStructs()) {
printType(out, t, empty);
out.write("");
}
for (EventElement e : metadata.getEvents()) {
printEvent(out, e, empty);
out.write("");
}
}
private static void printType(Printer out, TypeElement t, boolean empty) {
out.write("struct JfrStruct" + t.name);
out.write("{");
if (!empty) {
out.write(" private:");
for (FieldElement f : t.fields) {
printField(out, f);
}
out.write("");
}
out.write(" public:");
for (FieldElement f : t.fields) {
printTypeSetter(out, f, empty);
}
out.write("");
if (!empty) {
printWriteData(out, t.fields);
}
out.write("};");
out.write("");
}
private static void printEvent(Printer out, EventElement event, boolean empty) {
out.write("class Event" + event.name + " : public JfrEvent<Event" + event.name + ">");
out.write("{");
if (!empty) {
out.write(" private:");
for (FieldElement f : event.fields) {
printField(out, f);
}
out.write("");
}
out.write(" public:");
if (!empty) {
out.write(" static const bool hasThread = " + event.thread + ";");
out.write(" static const bool hasStackTrace = " + event.stackTrace + ";");
out.write(" static const bool isInstant = " + !event.startTime + ";");
out.write(" static const bool hasCutoff = " + event.cutoff + ";");
out.write(" static const bool isRequestable = " + event.periodic + ";");
out.write(" static const JfrEventId eventId = Jfr" + event.name + "Event;");
out.write("");
}
if (!empty) {
out.write(" Event" + event.name + "(EventStartTime timing=TIMED) : JfrEvent<Event" + event.name + ">(timing) {}");
} else {
out.write(" Event" + event.name + "(EventStartTime timing=TIMED) {}");
}
out.write("");
int index = 0;
for (FieldElement f : event.fields) {
out.write(" void set_" + f.name + "(" + f.getParameterType() + " " + f.getParameterName() + ") {");
if (!empty) {
out.write(" this->_" + f.name + " = " + f.getParameterName() + ";");
out.write(" DEBUG_ONLY(set_field_bit(" + index++ + "));");
}
out.write(" }");
}
out.write("");
if (!empty) {
printWriteData(out, event.fields);
out.write("");
}
out.write(" using JfrEvent<Event" + event.name + ">::commit; // else commit() is hidden by overloaded versions in this class");
printConstructor2(out, event, empty);
printCommitMethod(out, event, empty);
if (!empty) {
printVerify(out, event.fields);
}
out.write("};");
}
private static void printWriteData(Printer out, List<FieldElement> fields) {
out.write(" template <typename Writer>");
out.write(" void writeData(Writer& w) {");
for (FieldElement field : fields) {
if (field.struct) {
out.write(" _" + field.name + ".writeData(w);");
} else {
out.write(" w.write(_" + field.name + ");");
}
}
out.write(" }");
}
private static void printTypeSetter(Printer out, FieldElement field, boolean empty) {
if (!empty) {
out.write(" void set_" + field.name + "(" + field.getParameterType() + " new_value) { this->_" + field.name + " = new_value; }");
} else {
out.write(" void set_" + field.name + "(" + field.getParameterType() + " new_value) { }");
}
}
private static void printVerify(Printer out, List<FieldElement> fields) {
out.write("");
out.write("#ifdef ASSERT");
out.write(" void verify() const {");
int index = 0;
for (FieldElement f : fields) {
out.write(" assert(verify_field_bit(" + index++ + "), \"Attempting to write an uninitialized event field: " + f.name + "\");");
}
out.write(" }");
out.write("#endif");
}
private static void printCommitMethod(Printer out, EventElement event, boolean empty) {
if (event.startTime) {
StringJoiner sj = new StringJoiner(",\n ");
for (FieldElement f : event.fields) {
sj.add(f.getParameterType() + " " + f.name);
}
out.write("");
out.write(" void commit(" + sj.toString() + ") {");
if (!empty) {
out.write(" if (should_commit()) {");
for (FieldElement f : event.fields) {
out.write(" set_" + f.name + "(" + f.name + ");");
}
out.write(" commit();");
out.write(" }");
}
out.write(" }");
}
out.write("");
StringJoiner sj = new StringJoiner(",\n ");
if (event.startTime) {
sj.add("const Ticks& startTicks");
sj.add("const Ticks& endTicks");
}
for (FieldElement f : event.fields) {
sj.add(f.getParameterType() + " " + f.name);
}
out.write(" static void commit(" + sj.toString() + ") {");
if (!empty) {
out.write(" Event" + event.name + " me(UNTIMED);");
out.write("");
out.write(" if (me.should_commit()) {");
if (event.startTime) {
out.write(" me.set_starttime(startTicks);");
out.write(" me.set_endtime(endTicks);");
}
for (FieldElement f : event.fields) {
out.write(" me.set_" + f.name + "(" + f.name + ");");
}
out.write(" me.commit();");
out.write(" }");
}
out.write(" }");
}
private static void printConstructor2(Printer out, EventElement event, boolean empty) {
if (!event.startTime) {
out.write("");
out.write("");
}
if (event.startTime) {
out.write("");
out.write(" Event" + event.name + "(");
StringJoiner sj = new StringJoiner(",\n ");
for (FieldElement f : event.fields) {
sj.add(f.getParameterType() + " " + f.name);
}
if (!empty) {
out.write(" " + sj.toString() + ") : JfrEvent<Event" + event.name + ">(TIMED) {");
out.write(" if (should_commit()) {");
for (FieldElement f : event.fields) {
out.write(" set_" + f.name + "(" + f.name + ");");
}
out.write(" }");
} else {
out.write(" " + sj.toString() + ") {");
}
out.write(" }");
}
}
private static void printField(Printer out, FieldElement field) {
out.write(" " + field.getFieldType() + " _" + field.name + ";");
}
}
C:\hotspot-69087d08d473\src\share\vm/jfr/instrumentation/jfrEventClassTransformer.cpp
#include "precompiled.hpp"
#include "jvm.h"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/stackMapTable.hpp"
#include "classfile/verificationType.hpp"
#include "interpreter/bytecodes.hpp"
#include "jfr/instrumentation/jfrEventClassTransformer.hpp"
#include "jfr/jfr.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/jni/jfrUpcalls.hpp"
#include "jfr/support/jfrEventClass.hpp"
#include "jfr/utilities/jfrBigEndian.hpp"
#include "jfr/writers/jfrBigEndianWriter.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/method.hpp"
#include "prims/jvmtiRedefineClasses.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/os.hpp"
#include "runtime/thread.inline.hpp"
#include "utilities/exceptions.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
static const u2 number_of_new_methods = 5;
static const u2 number_of_new_fields = 3;
static const int extra_stream_bytes = 0x280;
static const u2 invalid_cp_index = 0;
static const char* utf8_constants[] = {
"Code", // 0
"J", // 1
"commit", // 2
"eventHandler", // 3
"Ljdk/jfr/proxy/internal/EventHandlerProxy;", // 4
"duration", // 5
"begin", // 6
"()V", // 7
"isEnabled", // 8
"()Z", // 9
"end", // 10
"shouldCommit", // 11
"startTime", // 12
"<clinit>", // 13
"jdk/jfr/FlightRecorder", // 14
"register", // 15
"(Ljava/lang/Class;)V", // 16 // LAST_REQUIRED_UTF8
"StackMapTable", // 17
"Exceptions", // 18
"LineNumberTable", // 20
"LocalVariableTable", // 21
"LocalVariableTypeTable", // 22
"RuntimeVisibleAnnotation" // 23
};
enum utf8_req_symbols {
UTF8_REQ_Code,
UTF8_REQ_J_FIELD_DESC,
UTF8_REQ_commit,
UTF8_REQ_eventHandler,
UTF8_REQ_eventHandler_FIELD_DESC,
UTF8_REQ_duration,
UTF8_REQ_begin,
UTF8_REQ_EMPTY_VOID_METHOD_DESC,
UTF8_REQ_isEnabled,
UTF8_REQ_EMPTY_BOOLEAN_METHOD_DESC,
UTF8_REQ_end,
UTF8_REQ_shouldCommit,
UTF8_REQ_startTime,
UTF8_REQ_clinit,
UTF8_REQ_FlightRecorder,
UTF8_REQ_register,
UTF8_REQ_CLASS_VOID_METHOD_DESC,
NOF_UTF8_REQ_SYMBOLS
};
enum utf8_opt_symbols {
UTF8_OPT_StackMapTable = NOF_UTF8_REQ_SYMBOLS,
UTF8_OPT_Exceptions,
UTF8_OPT_LineNumberTable,
UTF8_OPT_LocalVariableTable,
UTF8_OPT_LocalVariableTypeTable,
UTF8_OPT_RuntimeVisibleAnnotation,
NOF_UTF8_SYMBOLS
};
static u1 empty_void_method_code_attribute[] = {
0x0,
0x0,
0x0,
0xd, // attribute len
0x0,
0x0, // max stack
0x0,
0x1, // max locals
0x0,
0x0,
0x0,
0x1, // code length
Bytecodes::_return,
0x0,
0x0, // ex table len
0x0,
0x0 // attributes_count
};
static u1 boolean_method_code_attribute[] = {
0x0,
0x0,
0x0,
0xe,
0x0,
0x1, // max stack
0x0,
0x1, // max locals
0x0,
0x0,
0x0,
0x2,
Bytecodes::_iconst_0,
Bytecodes::_ireturn,
0x0,
0x0, // ex table len
0x0,
0x0, // attributes_count
};
enum { // initial annotation layout
atype_off = 0, // utf8 such as 'Ljava/lang/annotation/Retention;'
count_off = 2, // u2 such as 1 (one value)
member_off = 4, // utf8 such as 'value'
tag_off = 6, // u1 such as 'c' (type) or 'e' (enum)
e_tag_val = 'e',
e_type_off = 7, // utf8 such as 'Ljava/lang/annotation/RetentionPolicy;'
e_con_off = 9, // utf8 payload, such as 'SOURCE', 'CLASS', 'RUNTIME'
e_size = 11, // end of 'e' annotation
c_tag_val = 'c', // payload is type
c_con_off = 7, // utf8 payload, such as 'I'
c_size = 9, // end of 'c' annotation
s_tag_val = 's', // payload is String
s_con_off = 7, // utf8 payload, such as 'Ljava/lang/String;'
s_size = 9,
min_size = 6 // smallest possible size (zero members)
};
static int skip_annotation_value(const address, int, int); // fwd decl
static int next_annotation_index(const address buffer, int limit, int index) {
assert(buffer != NULL, "invariant");
index += 2; // skip atype
if ((index += 2) >= limit) {
return limit;
}
int nof_members = JfrBigEndian::read<u2>(buffer + index - 2);
while (--nof_members >= 0 && index < limit) {
index += 2; // skip member
index = skip_annotation_value(buffer, limit, index);
}
return index;
}
static int skip_annotation_value(const address buffer, int limit, int index) {
assert(buffer != NULL, "invariant");
if ((index += 1) >= limit) {
return limit;
}
const u1 tag = buffer[index - 1];
switch (tag) {
case 'B':
case 'C':
case 'I':
case 'S':
case 'Z':
case 'D':
case 'F':
case 'J':
case 'c':
case 's':
index += 2; // skip con or s_con
break;
case 'e':
index += 4; // skip e_class, e_name
break;
case '[':
{
if ((index += 2) >= limit) {
return limit;
}
int nof_values = JfrBigEndian::read<u2>(buffer + index - 2);
while (--nof_values >= 0 && index < limit) {
index = skip_annotation_value(buffer, limit, index);
}
}
break;
case '@':
index = next_annotation_index(buffer, limit, index);
break;
default:
return limit; // bad tag byte
}
return index;
}
static const u2 number_of_elements_offset = (u2)2;
static const u2 element_name_offset = (u2)(number_of_elements_offset + 2);
static const u2 element_name_size = (u2)2;
static const u2 value_type_relative_offset = (u2)2;
static const u2 value_relative_offset = (u2)(value_type_relative_offset + 1);
class AnnotationElementIterator : public StackObj {
private:
const InstanceKlass* _ik;
const address _buffer;
const u2 _limit; // length of annotation
mutable u2 _current; // element
mutable u2 _next; // element
u2 value_index() const {
return JfrBigEndian::read<u2>(_buffer + _current + value_relative_offset);
}
public:
AnnotationElementIterator(const InstanceKlass* ik, address buffer, u2 limit) : _ik(ik),
_buffer(buffer),
_limit(limit),
_next(element_name_offset),
_current(element_name_offset) {
assert(_buffer != NULL, "invariant");
assert(_next == element_name_offset, "invariant");
assert(_current == element_name_offset, "invariant");
}
bool has_next() const {
return _next < _limit;
}
void move_to_next() const {
assert(has_next(), "invariant");
_current = _next;
if (_next < _limit) {
_next = skip_annotation_value(_buffer, _limit, _next + element_name_size);
}
assert(_next <= _limit, "invariant");
assert(_current <= _limit, "invariant");
}
u2 number_of_elements() const {
return JfrBigEndian::read<u2>(_buffer + number_of_elements_offset);
}
const Symbol* name() const {
assert(_current < _next, "invariant");
return _ik->constants()->symbol_at(JfrBigEndian::read<u2>(_buffer + _current));
}
char value_type() const {
return JfrBigEndian::read<u1>(_buffer + _current + value_type_relative_offset);
}
jint read_int() const {
return _ik->constants()->int_at(value_index());
}
bool read_bool() const {
return read_int() != 0;
}
};
class AnnotationIterator : public StackObj {
private:
const InstanceKlass* _ik;
u2 _limit; // length of annotations array
const address _buffer;
mutable u2 _current; // annotation
mutable u2 _next; // annotation
public:
AnnotationIterator(const InstanceKlass* ik, AnnotationArray* ar) : _ik(ik),
_current(0),
_next(0),
_limit(ar != NULL ? ar->length() : 0),
_buffer(_limit > 2 ? ar->adr_at(2) : NULL) {
if (_buffer != NULL) {
_limit -= 2; // subtract sizeof(u2) number of annotations field
}
}
bool has_next() const {
return _next < _limit;
}
void move_to_next() const {
assert(has_next(), "invariant");
_current = _next;
if (_next < _limit) {
_next = next_annotation_index(_buffer, _limit, _next);
}
assert(_next <= _limit, "invariant");
assert(_current <= _limit, "invariant");
}
const AnnotationElementIterator elements() const {
assert(_current < _next, "invariant");
return AnnotationElementIterator(_ik, _buffer + _current, _next - _current);
}
const Symbol* type() const {
assert(_buffer != NULL, "invariant");
assert(_current < _limit, "invariant");
return _ik->constants()->symbol_at(JfrBigEndian::read<u2>(_buffer + _current));
}
};
static unsigned int unused_hash = 0;
static const char value_name[] = "value";
static bool has_registered_annotation(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) {
assert(annotation_type != NULL, "invariant");
AnnotationArray* class_annotations = ik->class_annotations();
if (class_annotations == NULL) {
return false;
}
const AnnotationIterator annotation_iterator(ik, class_annotations);
while (annotation_iterator.has_next()) {
annotation_iterator.move_to_next();
if (annotation_iterator.type() == annotation_type) {
static const Symbol* value_symbol =
SymbolTable::lookup_only(value_name, sizeof value_name - 1, unused_hash);
assert(value_symbol != NULL, "invariant");
const AnnotationElementIterator element_iterator = annotation_iterator.elements();
while (element_iterator.has_next()) {
element_iterator.move_to_next();
if (value_symbol == element_iterator.name()) {
assert('Z' == element_iterator.value_type(), "invariant");
value = element_iterator.read_bool();
return true;
}
}
}
}
return false;
}
static bool registered_annotation_value(const InstanceKlass* ik, const Symbol* const registered_symbol) {
assert(registered_symbol != NULL, "invariant");
assert(ik != NULL, "invariant");
assert(JdkJfrEvent::is_a(ik), "invariant");
bool registered_value = false;
if (has_registered_annotation(ik, registered_symbol, registered_value)) {
return registered_value;
}
InstanceKlass* super = InstanceKlass::cast(ik->super());
return registered_annotation_value(super, registered_symbol);
}
static const char registered_constant[] = "Ljdk/jfr/Registered;";
static bool should_register_klass(const InstanceKlass* ik) {
static const Symbol* const registered_symbol = SymbolTable::lookup_only(registered_constant,
sizeof registered_constant - 1,
unused_hash);
assert(registered_symbol != NULL, "invariant");
return registered_annotation_value(ik, registered_symbol);
}
static u2 utf8_info_index(const InstanceKlass* ik, const Symbol* const target, TRAPS) {
assert(target != NULL, "invariant");
ConstantPool* cp = ik->constants();
const int cp_len = cp->length();
for (u2 index = 1; index < cp_len; ++index) {
const constantTag tag = cp->tag_at(index);
if (tag.is_utf8()) {
const Symbol* const utf8_sym = cp->symbol_at(index);
assert(utf8_sym != NULL, "invariant");
if (utf8_sym == target) {
return index;
}
}
}
return invalid_cp_index;
}
#ifdef ASSERT
static bool is_index_within_range(u2 index, u2 orig_cp_len, u2 new_cp_entries_len) {
return index > 0 && index < orig_cp_len + new_cp_entries_len;
}
#endif
static u2 add_utf8_info(JfrBigEndianWriter& writer, const char* utf8_constant, u2 orig_cp_len, u2& new_cp_entries_len) {
assert(utf8_constant != NULL, "invariant");
writer.write<u1>(JVM_CONSTANT_Utf8);
writer.write_utf8_u2_len(utf8_constant);
assert(writer.is_valid(), "invariant");
return orig_cp_len + new_cp_entries_len++;
}
static u2 add_method_ref_info(JfrBigEndianWriter& writer,
u2 cls_name_index,
u2 method_index,
u2 desc_index,
u2 orig_cp_len,
u2& number_of_new_constants,
TRAPS) {
assert(is_index_within_range(cls_name_index, orig_cp_len, number_of_new_constants), "invariant");
assert(is_index_within_range(method_index, orig_cp_len, number_of_new_constants), "invariant");
assert(is_index_within_range(desc_index, orig_cp_len, number_of_new_constants), "invariant");
writer.write<u1>(JVM_CONSTANT_Class);
writer.write<u2>(cls_name_index);
const u2 cls_entry_index = orig_cp_len + number_of_new_constants;
++number_of_new_constants;
writer.write<u1>(JVM_CONSTANT_NameAndType);
writer.write<u2>(method_index);
writer.write<u2>(desc_index);
const u2 nat_entry_index = orig_cp_len + number_of_new_constants;
++number_of_new_constants;
writer.write<u1>(JVM_CONSTANT_Methodref);
writer.write<u2>(cls_entry_index);
writer.write<u2>(nat_entry_index);
return orig_cp_len + number_of_new_constants++;
}
static u2 add_flr_register_method_constants(JfrBigEndianWriter& writer,
const u2* utf8_indexes,
u2 orig_cp_len,
u2& number_of_new_constants,
TRAPS) {
assert(utf8_indexes != NULL, "invariant");
return add_method_ref_info(writer,
utf8_indexes[UTF8_REQ_FlightRecorder],
utf8_indexes[UTF8_REQ_register],
utf8_indexes[UTF8_REQ_CLASS_VOID_METHOD_DESC],
orig_cp_len,
number_of_new_constants,
THREAD);
}
static jlong add_field_info(JfrBigEndianWriter& writer, u2 name_index, u2 desc_index, bool is_static = false) {
assert(name_index > 0, "invariant");
assert(desc_index > 0, "invariant");
DEBUG_ONLY(const jlong start_offset = writer.current_offset();)
writer.write<u2>(JVM_ACC_SYNTHETIC | JVM_ACC_PRIVATE | (is_static ? JVM_ACC_STATIC : JVM_ACC_TRANSIENT)); // flags
writer.write(name_index);
writer.write(desc_index);
writer.write((u2)0x0); // attributes_count
assert(writer.is_valid(), "invariant");
DEBUG_ONLY(assert(start_offset + 8 == writer.current_offset(), "invariant");)
return writer.current_offset();
}
static u2 add_field_infos(JfrBigEndianWriter& writer, const u2* utf8_indexes) {
assert(utf8_indexes != NULL, "invariant");
add_field_info(writer,
utf8_indexes[UTF8_REQ_eventHandler],
utf8_indexes[UTF8_REQ_eventHandler_FIELD_DESC],
true); // static
add_field_info(writer,
utf8_indexes[UTF8_REQ_startTime],
utf8_indexes[UTF8_REQ_J_FIELD_DESC]);
add_field_info(writer,
utf8_indexes[UTF8_REQ_duration],
utf8_indexes[UTF8_REQ_J_FIELD_DESC]);
return number_of_new_fields;
}
static jlong add_method_info(JfrBigEndianWriter& writer,
u2 name_index,
u2 desc_index,
u2 code_index,
const u1* const code,
const size_t code_len) {
assert(name_index > 0, "invariant");
assert(desc_index > 0, "invariant");
assert(code_index > 0, "invariant");
DEBUG_ONLY(const jlong start_offset = writer.current_offset();)
writer.write<u2>(JVM_ACC_SYNTHETIC | JVM_ACC_PUBLIC); // flags
writer.write(name_index);
writer.write(desc_index);
writer.write<u2>(0x1); // attributes_count ; 1 for "Code" attribute
assert(writer.is_valid(), "invariant");
DEBUG_ONLY(assert(start_offset + 8 == writer.current_offset(), "invariant");)
writer.write(code_index); // "Code"
writer.bytes(code, code_len);
DEBUG_ONLY(assert((start_offset + 8 + 2 + (jlong)code_len) == writer.current_offset(), "invariant");)
return writer.current_offset();
}
static u2 position_stream_after_cp(ClassFileStream* stream) {
assert(stream != NULL, "invariant");
assert(stream->current_offset() == 0, "invariant");
stream->skip_u4_fast(2); // 8 bytes skipped
const u2 cp_len = stream->get_u2_fast();
assert(cp_len > 0, "invariant");
for (u2 index = 1; index < cp_len; ++index) {
const u1 tag = stream->get_u1_fast(); // cp tag
switch (tag) {
case JVM_CONSTANT_Class:
case JVM_CONSTANT_String: {
stream->skip_u2_fast(1); // skip 2 bytes
continue;
}
case JVM_CONSTANT_Fieldref:
case JVM_CONSTANT_Methodref:
case JVM_CONSTANT_InterfaceMethodref:
case JVM_CONSTANT_Integer:
case JVM_CONSTANT_Float:
case JVM_CONSTANT_NameAndType:
case JVM_CONSTANT_InvokeDynamic: {
stream->skip_u4_fast(1); // skip 4 bytes
continue;
}
case JVM_CONSTANT_Long:
case JVM_CONSTANT_Double: {
stream->skip_u4_fast(2); // skip 8 bytes
++index;
continue;
}
case JVM_CONSTANT_Utf8: {
u2 utf8_length = stream->get_u2_fast();
stream->skip_u1_fast(utf8_length); // skip 2 + len bytes
continue;
}
case JVM_CONSTANT_MethodHandle:
case JVM_CONSTANT_MethodType: {
if (tag == JVM_CONSTANT_MethodHandle) {
stream->skip_u1_fast(1);
stream->skip_u2_fast(1); // skip 3 bytes
}
else if (tag == JVM_CONSTANT_MethodType) {
stream->skip_u2_fast(1); // skip 3 bytes
}
}
continue;
default:
assert(false, "error in skip logic!");
break;
} // end switch(tag)
}
return cp_len;
}
static u2 position_stream_after_fields(ClassFileStream* stream) {
assert(stream != NULL, "invariant");
assert(stream->current_offset() > 0, "invariant");
const u2 orig_fields_len = stream->get_u2_fast();
for (u2 i = 0; i < orig_fields_len; ++i) {
stream->skip_u2_fast(3);
const u2 attrib_info_len = stream->get_u2_fast();
for (u2 j = 0; j < attrib_info_len; ++j) {
stream->skip_u2_fast(1);
const u4 attrib_len = stream->get_u4_fast();
stream->skip_u1_fast(attrib_len);
}
}
return orig_fields_len;
}
static u2 position_stream_after_methods(JfrBigEndianWriter& writer,
ClassFileStream* stream,
const u2* utf8_indexes,
bool register_klass,
const Method* clinit_method,
u4& orig_method_len_offset) {
assert(stream != NULL, "invariant");
assert(stream->current_offset() > 0, "invariant");
assert(utf8_indexes != NULL, "invariant");
writer.reserve(sizeof(u2));
const u2 orig_methods_len = stream->get_u2_fast();
orig_method_len_offset += sizeof(u2);
for (u2 i = 0; i < orig_methods_len; ++i) {
const u4 method_offset = stream->current_offset();
stream->skip_u2_fast(1); // Access Flags
const u2 name_index = stream->get_u2_fast(); // Name index
stream->skip_u2_fast(1); // Descriptor index
const u2 attributes_count = stream->get_u2_fast();
for (u2 j = 0; j < attributes_count; ++j) {
stream->skip_u2_fast(1);
const u4 attrib_len = stream->get_u4_fast();
stream->skip_u1_fast(attrib_len);
}
if (clinit_method != NULL && name_index == clinit_method->name_index()) {
if (!register_klass) {
continue;
}
writer.bytes(stream->buffer() + orig_method_len_offset,
method_offset - orig_method_len_offset);
assert(writer.is_valid(), "invariant");
orig_method_len_offset = stream->current_offset();
}
}
return orig_methods_len;
}
static u2 add_method_infos(JfrBigEndianWriter& writer, const u2* utf8_indexes) {
assert(utf8_indexes != NULL, "invariant");
add_method_info(writer,
utf8_indexes[UTF8_REQ_begin],
utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC],
utf8_indexes[UTF8_REQ_Code],
empty_void_method_code_attribute,
sizeof(empty_void_method_code_attribute));
assert(writer.is_valid(), "invariant");
add_method_info(writer,
utf8_indexes[UTF8_REQ_end],
utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC],
utf8_indexes[UTF8_REQ_Code],
empty_void_method_code_attribute,
sizeof(empty_void_method_code_attribute));
assert(writer.is_valid(), "invariant");
add_method_info(writer,
utf8_indexes[UTF8_REQ_commit],
utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC],
utf8_indexes[UTF8_REQ_Code],
empty_void_method_code_attribute,
sizeof(empty_void_method_code_attribute));
assert(writer.is_valid(), "invariant");
add_method_info(writer,
utf8_indexes[UTF8_REQ_isEnabled],
utf8_indexes[UTF8_REQ_EMPTY_BOOLEAN_METHOD_DESC],
utf8_indexes[UTF8_REQ_Code],
boolean_method_code_attribute,
sizeof(boolean_method_code_attribute));
assert(writer.is_valid(), "invariant");
add_method_info(writer,
utf8_indexes[UTF8_REQ_shouldCommit],
utf8_indexes[UTF8_REQ_EMPTY_BOOLEAN_METHOD_DESC],
utf8_indexes[UTF8_REQ_Code],
boolean_method_code_attribute,
sizeof(boolean_method_code_attribute));
assert(writer.is_valid(), "invariant");
return number_of_new_methods;
}
static void adjust_exception_table(JfrBigEndianWriter& writer, u2 bci_adjustment_offset, const Method* method, TRAPS) {
const u2 ex_table_length = method != NULL ? (u2)method->exception_table_length() : 0;
writer.write<u2>(ex_table_length); // Exception table length
if (ex_table_length > 0) {
assert(method != NULL, "invariant");
const ExceptionTableElement* const ex_elements = method->exception_table_start();
for (int i = 0; i < ex_table_length; ++i) {
assert(ex_elements != NULL, "invariant");
writer.write<u2>(ex_elements[i].start_pc + bci_adjustment_offset);
writer.write<u2>(ex_elements[i].end_pc + bci_adjustment_offset);
writer.write<u2>(ex_elements[i].handler_pc + bci_adjustment_offset);
writer.write<u2>(ex_elements[i].catch_type_index); // no adjustment
}
}
}
enum StackMapFrameTypes {
SAME_FRAME_BEGIN = 0,
SAME_FRAME_END = 63,
SAME_LOCALS_1_STACK_ITEM_FRAME_BEGIN = 64,
SAME_LOCALS_1_STACK_ITEM_FRAME_END = 127,
SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247,
CHOP_FRAME_BEGIN = 248,
CHOP_FRAME_END = 250,
SAME_FRAME_EXTENDED = 251,
APPEND_FRAME_BEGIN = 252,
APPEND_FRAME_END = 254,
FULL_FRAME = 255
};
static void adjust_stack_map(JfrBigEndianWriter& writer,
Array<u1>* stack_map,
const u2* utf8_indexes,
u2 bci_adjustment_offset,
TRAPS) {
assert(stack_map != NULL, "invariant");
assert(utf8_indexes != NULL, "invariant");
writer.write<u2>(utf8_indexes[UTF8_OPT_StackMapTable]);
const jlong stack_map_attrib_len_offset = writer.current_offset();
writer.reserve(sizeof(u4));
StackMapStream stream(stack_map);
const u2 stack_map_entries = stream.get_u2(THREAD);
writer.write<u2>(stack_map_entries); // new stack map entry added
const u1 frame_type = stream.get_u1(THREAD);
if (frame_type <= SAME_FRAME_END) {
writer.write<u1>(SAME_FRAME_EXTENDED);
writer.write<u2>(frame_type + bci_adjustment_offset);
} else if (frame_type >= SAME_LOCALS_1_STACK_ITEM_FRAME_BEGIN &&
frame_type <= SAME_LOCALS_1_STACK_ITEM_FRAME_END) {
writer.write<u1>(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED);
writer.write<u2>((frame_type - SAME_LOCALS_1_STACK_ITEM_FRAME_BEGIN) + bci_adjustment_offset);
} else if (frame_type >= SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
writer.write<u1>(frame_type);
writer.write<u2>(stream.get_u2(THREAD) + bci_adjustment_offset);
} else {
assert(false, "stackMapFrame type is invalid");
}
while (!stream.at_end()) {
writer.write<u1>(stream.get_u1(THREAD));
}
u4 stack_map_attrib_len = writer.current_offset() - stack_map_attrib_len_offset;
stack_map_attrib_len -= sizeof(u4);
writer.write_at_offset(stack_map_attrib_len, stack_map_attrib_len_offset);
}
static void adjust_line_number_table(JfrBigEndianWriter& writer,
const u2* utf8_indexes,
u4 bci_adjustement_offset,
const Method* method,
TRAPS) {
assert(utf8_indexes != NULL, "invariant");
assert(method != NULL, "invariant");
assert(method->has_linenumber_table(), "invariant");
writer.write(utf8_indexes[UTF8_OPT_LineNumberTable]);
const jlong lnt_attributes_length_offset = writer.current_offset();
writer.reserve(sizeof(u4));
const jlong lnt_attributes_entries_offset = writer.current_offset();
writer.reserve(sizeof(u2));
u1* lnt = method->compressed_linenumber_table();
CompressedLineNumberReadStream lnt_stream(lnt);
u2 line_number_table_entries = 0;
while (lnt_stream.read_pair()) {
++line_number_table_entries;
const u2 bci = (u2)lnt_stream.bci();
writer.write<u2>(bci + (u2)bci_adjustement_offset);
writer.write<u2>((u2)lnt_stream.line());
}
writer.write_at_offset(line_number_table_entries, lnt_attributes_entries_offset);
u4 lnt_table_attributes_len = writer.current_offset() - lnt_attributes_length_offset;
lnt_table_attributes_len -= sizeof(u4);
writer.write_at_offset(lnt_table_attributes_len, lnt_attributes_length_offset);
}
static u2 adjust_local_variable_table(JfrBigEndianWriter& writer,
const u2* utf8_indexes,
u2 bci_adjustment_offset,
const Method* method,
TRAPS) {
assert(utf8_indexes != NULL, "invariant");
assert(method != NULL, "invariant");
assert(method->has_localvariable_table(), "invariant");
writer.write<u2>(utf8_indexes[UTF8_OPT_LocalVariableTable]);
const jlong lvt_attributes_length_offset = writer.current_offset();
writer.reserve(sizeof(u4));
const int lvt_len = method->localvariable_table_length();
writer.write<u2>((u2)lvt_len);
const LocalVariableTableElement* table = method->localvariable_table_start();
assert(table != NULL, "invariant");
u2 num_lvtt_entries = 0;
for (int i = 0; i < lvt_len; ++i) {
writer.write<u2>(table[i].start_bci + bci_adjustment_offset);
writer.write<u2>(table[i].length);
writer.write<u2>(table[i].name_cp_index);
writer.write<u2>(table[i].descriptor_cp_index);
writer.write<u2>(table[i].slot);
if (table[i].signature_cp_index > 0) {
++num_lvtt_entries;
}
}
u4 lvt_table_attributes_len = writer.current_offset() - lvt_attributes_length_offset;
lvt_table_attributes_len -= sizeof(u4);
writer.write_at_offset(lvt_table_attributes_len, lvt_attributes_length_offset);
return num_lvtt_entries;
}
static void adjust_local_variable_type_table(JfrBigEndianWriter& writer,
const u2* utf8_indexes,
u2 bci_adjustment_offset,
u2 num_lvtt_entries,
const Method* method,
TRAPS) {
assert(num_lvtt_entries > 0, "invariant");
writer.write<u2>(utf8_indexes[UTF8_OPT_LocalVariableTypeTable]);
const jlong lvtt_attributes_length_offset = writer.current_offset();
writer.reserve(sizeof(u4));
writer.write<u2>(num_lvtt_entries);
const LocalVariableTableElement* table = method->localvariable_table_start();
assert(table != NULL, "invariant");
const int lvt_len = method->localvariable_table_length();
for (int i = 0; i < lvt_len; ++i) {
if (table[i].signature_cp_index > 0) {
writer.write<u2>(table[i].start_bci + bci_adjustment_offset);
writer.write<u2>(table[i].length);
writer.write<u2>(table[i].name_cp_index);
writer.write<u2>(table[i].signature_cp_index);
writer.write<u2>(table[i].slot);
}
}
u4 lvtt_table_attributes_len = writer.current_offset() - lvtt_attributes_length_offset;
lvtt_table_attributes_len -= sizeof(u4);
writer.write_at_offset(lvtt_table_attributes_len, lvtt_attributes_length_offset);
}
static void adjust_code_attributes(JfrBigEndianWriter& writer,
const u2* utf8_indexes,
u2 bci_adjustment_offset,
const Method* clinit_method,
TRAPS) {
assert(utf8_indexes != NULL, "invariant");
const jlong code_attributes_offset = writer.current_offset();
writer.reserve(sizeof(u2));
u2 number_of_code_attributes = 0;
if (clinit_method != NULL) {
Array<u1>* stack_map = clinit_method->stackmap_data();
if (stack_map != NULL) {
++number_of_code_attributes;
adjust_stack_map(writer, stack_map, utf8_indexes, bci_adjustment_offset, THREAD);
assert(writer.is_valid(), "invariant");
}
if (clinit_method != NULL && clinit_method->has_linenumber_table()) {
++number_of_code_attributes;
adjust_line_number_table(writer, utf8_indexes, bci_adjustment_offset, clinit_method, THREAD);
assert(writer.is_valid(), "invariant");
}
if (clinit_method != NULL && clinit_method->has_localvariable_table()) {
++number_of_code_attributes;
const u2 num_of_lvtt_entries = adjust_local_variable_table(writer, utf8_indexes, bci_adjustment_offset, clinit_method, THREAD);
assert(writer.is_valid(), "invariant");
if (num_of_lvtt_entries > 0) {
++number_of_code_attributes;
adjust_local_variable_type_table(writer, utf8_indexes, bci_adjustment_offset, num_of_lvtt_entries, clinit_method, THREAD);
assert(writer.is_valid(), "invariant");
}
}
}
writer.write_at_offset(number_of_code_attributes, code_attributes_offset);
}
static jlong insert_clinit_method(const InstanceKlass* ik,
ClassFileParser& parser,
JfrBigEndianWriter& writer,
u2 orig_constant_pool_len,
const u2* utf8_indexes,
const u2 register_method_ref_index,
const Method* clinit_method,
TRAPS) {
assert(utf8_indexes != NULL, "invariant");
static const u2 injected_code_length = 8;
const u2 name_index = utf8_indexes[UTF8_REQ_clinit];
const u2 desc_index = utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC];
const u2 max_stack = MAX2(clinit_method != NULL ? clinit_method->verifier_max_stack() : 1, 1);
const u2 max_locals = MAX2(clinit_method != NULL ? clinit_method->max_locals() : 0, 0);
const u2 orig_bytecodes_length = clinit_method != NULL ? (u2)clinit_method->code_size() : 0;
const address orig_bytecodes = clinit_method != NULL ? clinit_method->code_base() : NULL;
const u2 new_code_length = injected_code_length + orig_bytecodes_length;
DEBUG_ONLY(const jlong start_offset = writer.current_offset();)
writer.write<u2>(JVM_ACC_STATIC); // flags
writer.write<u2>(name_index);
writer.write<u2>(desc_index);
writer.write<u2>((u2)0x1); // attributes_count // "Code"
assert(writer.is_valid(), "invariant");
DEBUG_ONLY(assert(start_offset + 8 == writer.current_offset(), "invariant");)
writer.write<u2>(utf8_indexes[UTF8_REQ_Code]); // "Code"
const jlong code_attribute_length_offset = writer.current_offset();
writer.reserve(sizeof(u4));
writer.write<u2>(max_stack); // max stack
writer.write<u2>(max_locals); // max locals
writer.write<u4>((u4)new_code_length); // code length
writer.write<u1>((u1)Bytecodes::_ldc_w);
writer.write<u2>((u2)parser.this_class_index()); // load constant "this class"
writer.write<u1>((u1)Bytecodes::_invokestatic);
writer.write<u2>(register_method_ref_index);
if (clinit_method == NULL) {
writer.write<u1>((u1)Bytecodes::_nop);
writer.write<u1>((u1)Bytecodes::_return);
} else {
writer.write<u1>((u1)Bytecodes::_nop);
writer.write<u1>((u1)Bytecodes::_nop);
writer.bytes(orig_bytecodes, orig_bytecodes_length);
}
assert(writer.is_valid(), "invariant");
adjust_exception_table(writer, injected_code_length, clinit_method, THREAD);
assert(writer.is_valid(), "invariant");
adjust_code_attributes(writer, utf8_indexes, injected_code_length, clinit_method, THREAD);
assert(writer.is_valid(), "invariant");
u4 code_attribute_len = writer.current_offset() - code_attribute_length_offset;
code_attribute_len -= sizeof(u4);
writer.write_at_offset(code_attribute_len, code_attribute_length_offset);
return writer.current_offset();
}
static ClassFileStream* create_new_bytes_for_event_klass(const InstanceKlass* ik, const ClassFileParser& parser, TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
static const u2 public_final_flag_mask = JVM_ACC_PUBLIC | JVM_ACC_FINAL;
ClassFileStream* const orig_stream = parser.clone_stream();
const int orig_stream_length = orig_stream->length();
u1* const new_buffer = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, u1, orig_stream_length);
if (new_buffer == NULL) {
return NULL;
}
assert(new_buffer != NULL, "invariant");
memcpy(new_buffer, orig_stream->buffer(), orig_stream_length);
const u2 orig_cp_len = position_stream_after_cp(orig_stream);
assert(orig_cp_len > 0, "invariant");
assert(orig_stream->current_offset() > 0, "invariant");
orig_stream->skip_u2_fast(3); // access_flags, this_class_index, super_class_index
const u2 iface_len = orig_stream->get_u2_fast();
orig_stream->skip_u2_fast(iface_len);
const u2 orig_fields_len = orig_stream->get_u2_fast();
for (u2 i = 0; i < orig_fields_len; ++i) {
orig_stream->skip_u2_fast(3);
const u2 attrib_info_len = orig_stream->get_u2_fast();
for (u2 j = 0; j < attrib_info_len; ++j) {
orig_stream->skip_u2_fast(1);
const u4 attrib_len = orig_stream->get_u4_fast();
orig_stream->skip_u1_fast(attrib_len);
}
}
const u2 orig_methods_len = orig_stream->get_u2_fast();
for (u2 i = 0; i < orig_methods_len; ++i) {
const u4 access_flag_offset = orig_stream->current_offset();
const u2 flags = orig_stream->get_u2_fast();
if (public_final_flag_mask == flags) {
JfrBigEndianWriter accessflagsrewriter(new_buffer + access_flag_offset, sizeof(u2));
accessflagsrewriter.write<u2>(JVM_ACC_PUBLIC);
assert(accessflagsrewriter.is_valid(), "invariant");
}
orig_stream->skip_u2_fast(2);
const u2 attributes_count = orig_stream->get_u2_fast();
for (u2 j = 0; j < attributes_count; ++j) {
orig_stream->skip_u2_fast(1);
const u4 attrib_len = orig_stream->get_u4_fast();
orig_stream->skip_u1_fast(attrib_len);
}
}
return new ClassFileStream(new_buffer, orig_stream_length, NULL);
}
static u2 find_or_add_utf8_info(JfrBigEndianWriter& writer,
const InstanceKlass* ik,
const char* const utf8_constant,
u2 orig_cp_len,
u2& added_cp_entries,
TRAPS) {
assert(utf8_constant != NULL, "invariant");
TempNewSymbol utf8_sym = SymbolTable::new_symbol(utf8_constant, THREAD);
const int utf8_orig_idx = utf8_info_index(ik, utf8_sym, THREAD);
if (utf8_orig_idx != invalid_cp_index) {
return utf8_orig_idx;
}
assert(invalid_cp_index == utf8_orig_idx, "invariant");
return add_utf8_info(writer, utf8_constant, orig_cp_len, added_cp_entries);
}
static u2 resolve_utf8_indexes(JfrBigEndianWriter& writer,
const InstanceKlass* ik,
u2* const utf8_indexes,
u2 orig_cp_len,
const Method* clinit_method,
TRAPS) {
assert(utf8_indexes != NULL, "invariant");
u2 added_cp_entries = 0;
for (u2 index = 0; index < NOF_UTF8_REQ_SYMBOLS; ++index) {
utf8_indexes[index] = find_or_add_utf8_info(writer,
ik,
utf8_constants[index],
orig_cp_len,
added_cp_entries,
THREAD);
}
if (clinit_method != NULL && clinit_method->has_stackmap_table()) {
utf8_indexes[UTF8_OPT_StackMapTable] =
find_or_add_utf8_info(writer,
ik,
utf8_constants[UTF8_OPT_StackMapTable],
orig_cp_len,
added_cp_entries,
THREAD);
} else {
utf8_indexes[UTF8_OPT_StackMapTable] = invalid_cp_index;
}
if (clinit_method != NULL && clinit_method->has_linenumber_table()) {
utf8_indexes[UTF8_OPT_LineNumberTable] =
find_or_add_utf8_info(writer,
ik,
utf8_constants[UTF8_OPT_LineNumberTable],
orig_cp_len,
added_cp_entries,
THREAD);
} else {
utf8_indexes[UTF8_OPT_LineNumberTable] = invalid_cp_index;
}
if (clinit_method != NULL && clinit_method->has_localvariable_table()) {
utf8_indexes[UTF8_OPT_LocalVariableTable] =
find_or_add_utf8_info(writer,
ik,
utf8_constants[UTF8_OPT_LocalVariableTable],
orig_cp_len,
added_cp_entries,
THREAD);
utf8_indexes[UTF8_OPT_LocalVariableTypeTable] =
find_or_add_utf8_info(writer,
ik,
utf8_constants[UTF8_OPT_LocalVariableTypeTable],
orig_cp_len,
added_cp_entries,
THREAD);
} else {
utf8_indexes[UTF8_OPT_LocalVariableTable] = invalid_cp_index;
utf8_indexes[UTF8_OPT_LocalVariableTypeTable] = invalid_cp_index;
}
return added_cp_entries;
}
static u1* new_bytes_for_lazy_instrumentation(InstanceKlass* ik,
ClassFileParser& parser,
jint& size_of_new_bytes,
TRAPS) {
assert(ik != NULL, "invariant");
const Method* clinit_method = ik->class_initializer();
const bool register_klass = should_register_klass(ik);
ClassFileStream* const orig_stream = parser.clone_stream();
const int orig_stream_size = orig_stream->length();
assert(orig_stream->current_offset() == 0, "invariant");
const u2 orig_cp_len = position_stream_after_cp(orig_stream);
assert(orig_cp_len > 0, "invariant");
assert(orig_stream->current_offset() > 0, "invariant");
const jint new_buffer_size = extra_stream_bytes + orig_stream_size;
u1* const new_buffer = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, u1, new_buffer_size);
if (new_buffer == NULL) {
if (true) tty->print_cr ("Thread local allocation (native) for " SIZE_FORMAT
" bytes failed in JfrClassAdapter::on_klass_creation", (size_t)new_buffer_size);
return NULL;
}
assert(new_buffer != NULL, "invariant");
JfrBigEndianWriter writer(new_buffer, new_buffer_size);
assert(writer.current_offset() == 0, "invariant");
const u4 orig_access_flag_offset = orig_stream->current_offset();
writer.bytes(orig_stream->buffer(), orig_access_flag_offset);
assert(writer.is_valid(), "invariant");
assert(writer.current_offset() == (intptr_t)orig_access_flag_offset, "invariant"); // same positions
u2 utf8_indexes[NOF_UTF8_SYMBOLS];
u2 number_of_new_constants = resolve_utf8_indexes(writer, ik, utf8_indexes, orig_cp_len, clinit_method, THREAD);
const u2 flr_register_method_ref_index =
register_klass ?
add_flr_register_method_constants(writer,
utf8_indexes,
orig_cp_len,
number_of_new_constants,
THREAD) : invalid_cp_index;
writer.write_at_offset<u2>(orig_cp_len + number_of_new_constants, 8);
assert(writer.is_valid(), "invariant");
orig_stream->skip_u2_fast(3); // access_flags, this_class_index, super_class_index
const u2 iface_len = orig_stream->get_u2_fast(); // interfaces
orig_stream->skip_u2_fast(iface_len);
const u4 orig_fields_len_offset = orig_stream->current_offset();
writer.bytes(orig_stream->buffer() + orig_access_flag_offset,
orig_fields_len_offset - orig_access_flag_offset);
assert(writer.is_valid(), "invariant");
const jlong new_fields_len_offset = writer.current_offset();
const u2 orig_fields_len = position_stream_after_fields(orig_stream);
u4 orig_method_len_offset = orig_stream->current_offset();
writer.bytes(orig_stream->buffer() + orig_fields_len_offset, orig_method_len_offset - orig_fields_len_offset);
assert(writer.is_valid(), "invariant");
const u2 number_of_new_fields = add_field_infos(writer, utf8_indexes);
assert(writer.is_valid(), "invariant");
const jlong new_method_len_offset = writer.current_offset();
writer.write_at_offset<u2>(orig_fields_len + number_of_new_fields, new_fields_len_offset);
assert(writer.is_valid(), "invariant");
const u2 orig_methods_len = position_stream_after_methods(writer,
orig_stream,
utf8_indexes,
register_klass,
clinit_method,
orig_method_len_offset);
const u4 orig_attributes_count_offset = orig_stream->current_offset();
writer.bytes(orig_stream->buffer() + orig_method_len_offset, orig_attributes_count_offset - orig_method_len_offset);
assert(writer.is_valid(), "invariant");
u2 number_of_new_methods = add_method_infos(writer, utf8_indexes);
if (register_klass) {
insert_clinit_method(ik, parser, writer, orig_cp_len, utf8_indexes, flr_register_method_ref_index, clinit_method, THREAD);
}
number_of_new_methods += clinit_method != NULL ? 0 : register_klass ? 1 : 0;
writer.write_at_offset<u2>(orig_methods_len + number_of_new_methods, new_method_len_offset);
assert(writer.is_valid(), "invariant");
writer.bytes(orig_stream->buffer() + orig_attributes_count_offset, orig_stream_size - orig_attributes_count_offset);
assert(writer.is_valid(), "invariant");
assert(writer.current_offset() > orig_stream->length(), "invariant");
size_of_new_bytes = (jint)writer.current_offset();
return new_buffer;
}
static void log_pending_exception(oop throwable) {
assert(throwable != NULL, "invariant");
oop msg = java_lang_Throwable::message(throwable);
if (msg != NULL) {
char* text = java_lang_String::as_utf8_string(msg);
if (text != NULL) {
if (true) tty->print_cr ("%s", text);
}
}
}
static bool should_force_instrumentation() {
return !JfrOptionSet::allow_event_retransforms() || JfrEventClassTransformer::is_force_instrumentation();
}
static ClassFileStream* create_new_bytes_for_subklass(InstanceKlass* ik, ClassFileParser& parser, Thread* t) {
assert(JdkJfrEvent::is_a(ik), "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(t));
jint size_of_new_bytes = 0;
u1* new_bytes = new_bytes_for_lazy_instrumentation(ik, parser, size_of_new_bytes, t);
if (new_bytes == NULL) {
return NULL;
}
assert(new_bytes != NULL, "invariant");
assert(size_of_new_bytes > 0, "invariant");
bool force_instrumentation = should_force_instrumentation();
if (Jfr::is_recording() || force_instrumentation) {
jint size_instrumented_data = 0;
unsigned char* instrumented_data = NULL;
const jclass super = (jclass)JNIHandles::make_local(ik->super()->java_mirror());
JfrUpcalls::new_bytes_eager_instrumentation(TRACE_ID(ik),
force_instrumentation,
super,
size_of_new_bytes,
new_bytes,
&size_instrumented_data,
&instrumented_data,
t);
if (t->has_pending_exception()) {
log_pending_exception(t->pending_exception());
t->clear_pending_exception();
return NULL;
}
assert(instrumented_data != NULL, "invariant");
assert(size_instrumented_data > 0, "invariant");
return new ClassFileStream(instrumented_data, size_instrumented_data, NULL);
}
return new ClassFileStream(new_bytes, size_of_new_bytes, NULL);
}
static bool cache_bytes(InstanceKlass* ik, ClassFileStream* new_stream, InstanceKlass* new_ik, TRAPS) {
assert(ik != NULL, "invariant");
assert(new_ik != NULL, "invariant");
assert(new_ik->name() != NULL, "invariant");
assert(new_stream != NULL, "invariant");
assert(!HAS_PENDING_EXCEPTION, "invariant");
static const bool can_retransform = JfrOptionSet::allow_retransforms();
if (!can_retransform) {
return true;
}
const jint stream_len = new_stream->length();
JvmtiCachedClassFileData* p =
(JvmtiCachedClassFileData*)NEW_C_HEAP_ARRAY_RETURN_NULL(u1, offset_of(JvmtiCachedClassFileData, data) + stream_len, mtInternal);
if (p == NULL) {
if (true) tty->print_cr("Allocation using C_HEAP_ARRAY for " SIZE_FORMAT
" bytes failed in JfrClassAdapter::on_klass_creation", (size_t)offset_of(JvmtiCachedClassFileData, data) + stream_len);
return false;
}
p->length = stream_len;
memcpy(p->data, new_stream->buffer(), stream_len);
new_ik->set_cached_class_file(p);
JvmtiCachedClassFileData* const cached_class_data = ik->get_cached_class_file();
if (cached_class_data != NULL) {
os::free(cached_class_data);
ik->set_cached_class_file(NULL);
}
return true;
}
static InstanceKlass* create_new_instance_klass(InstanceKlass* ik, ClassFileStream* stream, TRAPS) {
assert(stream != NULL, "invariant");
ResourceMark rm(THREAD);
TempNewSymbol parsed_name = NULL;
ClassLoaderData* const cld = ik->class_loader_data();
Handle pd(THREAD, ik->protection_domain());
Symbol* const class_name = ik->name();
const char* const klass_name = class_name != NULL ? class_name->as_C_string() : "";
InstanceKlass* const new_ik = ClassFileParser(stream).parseClassFile(
class_name,
cld,
pd,
NULL, // host klass
NULL, // cp_patches
parsed_name,
true, // need_verify
THREAD)();
if (HAS_PENDING_EXCEPTION) {
log_pending_exception(PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
return NULL;
}
assert(new_ik != NULL, "invariant");
assert(new_ik->name() != NULL, "invariant");
assert(strncmp(ik->name()->as_C_string(), new_ik->name()->as_C_string(), strlen(ik->name()->as_C_string())) == 0, "invariant");
return cache_bytes(ik, stream, new_ik, THREAD) ? new_ik : NULL;
}
static void rewrite_klass_pointer(InstanceKlass*& ik, InstanceKlass* new_ik, ClassFileParser& parser, TRAPS) {
assert(ik != NULL, "invariant");
assert(new_ik != NULL, "invariant");
assert(new_ik->name() != NULL, "invariant");
assert(JdkJfrEvent::is(new_ik) || JdkJfrEvent::is_subklass(new_ik), "invariant");
assert(!HAS_PENDING_EXCEPTION, "invariant");
parser.set_klass_to_deallocate(ik);
ik = new_ik;
}
static void copy_method_trace_flags(const InstanceKlass* the_original_klass, const InstanceKlass* the_scratch_klass) {
assert(the_original_klass != NULL, "invariant");
assert(the_scratch_klass != NULL, "invariant");
assert(the_original_klass->name() == the_scratch_klass->name(), "invariant");
const Array<Method*>* old_methods = the_original_klass->methods();
const Array<Method*>* new_methods = the_scratch_klass->methods();
const bool equal_array_length = old_methods->length() == new_methods->length();
for (int i = 0; i < old_methods->length(); ++i) {
const Method* const old_method = old_methods->at(i);
Method* const new_method = equal_array_length ? new_methods->at(i) :
the_scratch_klass->find_method(old_method->name(), old_method->signature());
assert(new_method != NULL, "invariant");
assert(new_method->name() == old_method->name(), "invariant");
assert(new_method->signature() == old_method->signature(), "invariant");
assert(new_method->trace_flags() == old_method->trace_flags(), "invariant");
}
}
static bool is_retransforming(const InstanceKlass* ik, TRAPS) {
assert(ik != NULL, "invariant");
assert(JdkJfrEvent::is_a(ik), "invariant");
Symbol* const name = ik->name();
assert(name != NULL, "invariant");
Handle class_loader(THREAD, ik->class_loader());
Handle protection_domain(THREAD, ik->protection_domain());
const InstanceKlass* prev_ik = (const InstanceKlass*)SystemDictionary::find(name, class_loader, protection_domain, THREAD);
if (prev_ik == NULL) {
return false;
}
assert(prev_ik != NULL, "invariant");
assert(JdkJfrEvent::is_a(prev_ik), "invariant");
copy_method_trace_flags(prev_ik, ik);
return true;
}
void JfrEventClassTransformer::on_klass_creation(InstanceKlass*& ik, ClassFileParser& parser, TRAPS) {
assert(ik != NULL, "invariant");
if (JdkJfrEvent::is(ik)) {
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
ClassFileStream* new_stream = create_new_bytes_for_event_klass(ik, parser, THREAD);
if (new_stream == NULL) {
if (true) tty->print_cr("JfrClassAdapter: unable to create ClassFileStream");
return;
}
assert(new_stream != NULL, "invariant");
InstanceKlass* new_ik = create_new_instance_klass(ik, new_stream, THREAD);
if (new_ik == NULL) {
if (true) tty->print_cr("JfrClassAdapter: unable to create InstanceKlass");
return;
}
assert(new_ik != NULL, "invariant");
assert(!JdkJfrEvent::is(new_ik), "invariant");
JdkJfrEvent::tag_as(new_ik);
assert(JdkJfrEvent::is(new_ik), "invariant");
rewrite_klass_pointer(ik, new_ik, parser, THREAD);
return;
}
assert(JdkJfrEvent::is_subklass(ik), "invariant");
if (is_retransforming(ik, THREAD)) {
return;
}
if (ik->is_abstract()) {
return;
}
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
ClassFileStream* const new_stream = create_new_bytes_for_subklass(ik, parser, THREAD);
if (NULL == new_stream) {
if (true) tty->print_cr("JfrClassAdapter: unable to create ClassFileStream");
return;
}
assert(new_stream != NULL, "invariant");
InstanceKlass* new_ik = create_new_instance_klass(ik, new_stream, THREAD);
if (new_ik == NULL) {
if (true) tty->print_cr("JfrClassAdapter: unable to create InstanceKlass");
return;
}
assert(new_ik != NULL, "invariant");
assert(JdkJfrEvent::is_subklass(new_ik), "invariant");
traceid id = ik->trace_id();
ik->set_trace_id(0);
new_ik->set_trace_id(id);
rewrite_klass_pointer(ik, new_ik, parser, THREAD);
}
static bool _force_instrumentation = false;
void JfrEventClassTransformer::set_force_instrumentation(bool force_instrumentation) {
_force_instrumentation = force_instrumentation;
}
bool JfrEventClassTransformer::is_force_instrumentation() {
return _force_instrumentation;
}
C:\hotspot-69087d08d473\src\share\vm/jfr/instrumentation/jfrEventClassTransformer.hpp
#ifndef SHARE_VM_JFR_INSTRUMENTATION_JFREVENTCLASSTRANSFORMER_HPP
#define SHARE_VM_JFR_INSTRUMENTATION_JFREVENTCLASSTRANSFORMER_HPP
#include "memory/allocation.hpp"
#include "utilities/exceptions.hpp"
class ClassFileParser;
class InstanceKlass;
class JfrEventClassTransformer : AllStatic {
public:
static void on_klass_creation(InstanceKlass*& ik, ClassFileParser& parser, TRAPS);
static void set_force_instrumentation(bool force_instrumentation);
static bool is_force_instrumentation();
};
#endif // SHARE_VM_JFR_INSTRUMENTATION_JFREVENTCLASSTRANSFORMER_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/instrumentation/jfrJvmtiAgent.cpp
#include "precompiled.hpp"
#include "jvm.h"
#include "jfr/instrumentation/jfrJvmtiAgent.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/jni/jfrUpcalls.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/support/jfrEventClass.hpp"
#include "memory/resourceArea.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/interfaceSupport.hpp"
#include "runtime/thread.inline.hpp"
#include "utilities/exceptions.hpp"
static const size_t ERROR_MSG_BUFFER_SIZE = 256;
static JfrJvmtiAgent* agent = NULL;
static jvmtiEnv* jfr_jvmti_env = NULL;
static void check_jvmti_error(jvmtiEnv* jvmti, jvmtiError errnum, const char* str) {
if (errnum != JVMTI_ERROR_NONE) {
char* errnum_str = NULL;
jvmti->GetErrorName(errnum, &errnum_str);
if (true) tty->print_cr("ERROR: JfrJvmtiAgent: " INT32_FORMAT " (%s): %s\n",
errnum,
NULL == errnum_str ? "Unknown" : errnum_str,
NULL == str ? "" : str);
}
}
static jvmtiError set_event_notification_mode(jvmtiEventMode mode,
jvmtiEvent event,
jthread event_thread,
...) {
if (jfr_jvmti_env == NULL) {
return JVMTI_ERROR_NONE;
}
const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventNotificationMode(mode, event, event_thread);
check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventNotificationMode");
return jvmti_ret_code;
}
static jvmtiError update_class_file_load_hook_event(jvmtiEventMode mode) {
return set_event_notification_mode(mode, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
}
static JavaThread* current_java_thread() {
Thread* this_thread = Thread::current();
assert(this_thread != NULL && this_thread->is_Java_thread(), "invariant");
return static_cast<JavaThread*>(this_thread);
}
extern "C" void JNICALL jfr_on_class_file_load_hook(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jclass class_being_redefined,
jobject loader,
const char* name,
jobject protection_domain,
jint class_data_len,
const unsigned char* class_data,
jint* new_class_data_len,
unsigned char** new_class_data) {
if (class_being_redefined == NULL) {
return;
}
JavaThread* jt = JavaThread::thread_from_jni_environment(jni_env);
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));;
ThreadInVMfromNative tvmfn(jt);
JfrUpcalls::on_retransform(JfrTraceId::get(class_being_redefined),
class_being_redefined,
class_data_len,
class_data,
new_class_data_len,
new_class_data,
jt);
}
static jclass* create_classes_array(jint classes_count, TRAPS) {
assert(classes_count > 0, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
ThreadInVMfromNative tvmfn((JavaThread*)THREAD);
jclass* const classes = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, jclass, classes_count);
if (NULL == classes) {
char error_buffer[ERROR_MSG_BUFFER_SIZE];
jio_snprintf(error_buffer, ERROR_MSG_BUFFER_SIZE,
"Thread local allocation (native) of " SIZE_FORMAT " bytes failed "
"in retransform classes", sizeof(jclass) * classes_count);
if (true) tty->print_cr("%s", error_buffer);
JfrJavaSupport::throw_out_of_memory_error(error_buffer, CHECK_NULL);
}
return classes;
}
static void log_and_throw(TRAPS) {
if (!HAS_PENDING_EXCEPTION) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
ThreadInVMfromNative tvmfn((JavaThread*)THREAD);
if (true) tty->print_cr("JfrJvmtiAgent::retransformClasses failed");
JfrJavaSupport::throw_class_format_error("JfrJvmtiAgent::retransformClasses failed", THREAD);
}
}
static void check_exception_and_log(JNIEnv* env, TRAPS) {
assert(env != NULL, "invariant");
if (env->ExceptionOccurred()) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
ThreadInVMfromNative tvmfn((JavaThread*)THREAD);
if (true) tty->print_cr("GetObjectArrayElement threw an exception");
return;
}
}
void JfrJvmtiAgent::retransform_classes(JNIEnv* env, jobjectArray classes_array, TRAPS) {
assert(env != NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
if (classes_array == NULL) {
return;
}
const jint classes_count = env->GetArrayLength(classes_array);
if (classes_count <= 0) {
return;
}
ResourceMark rm(THREAD);
jclass* const classes = create_classes_array(classes_count, CHECK);
assert(classes != NULL, "invariant");
for (jint i = 0; i < classes_count; i++) {
jclass clz = (jclass)env->GetObjectArrayElement(classes_array, i);
check_exception_and_log(env, THREAD);
{
ThreadInVMfromNative transition((JavaThread*)THREAD);
if (JdkJfrEvent::is_a(clz)) {
assert(JdkJfrEvent::is_subklass(clz), "invariant");
} else {
JdkJfrEvent::tag_as_host(clz);
}
}
classes[i] = clz;
}
if (jfr_jvmti_env->RetransformClasses(classes_count, classes) != JVMTI_ERROR_NONE) {
log_and_throw(THREAD);
}
}
static jvmtiError register_callbacks(JavaThread* jt) {
assert(jfr_jvmti_env != NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.ClassFileLoadHook = jfr_on_class_file_load_hook;
const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventCallbacks");
return jvmti_ret_code;
}
static jvmtiError register_capabilities(JavaThread* jt) {
assert(jfr_jvmti_env != NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
jvmtiCapabilities capabilities;
(void)memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_retransform_classes = 1;
capabilities.can_retransform_any_class = 1;
const jvmtiError jvmti_ret_code = jfr_jvmti_env->AddCapabilities(&capabilities);
check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "Add Capabilities");
return jvmti_ret_code;
}
static jint create_jvmti_env(JavaThread* jt) {
assert(jfr_jvmti_env == NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
extern struct JavaVM_ main_vm;
JavaVM* vm = &main_vm;
return vm->GetEnv((void **)&jfr_jvmti_env, JVMTI_VERSION);
}
static jvmtiError unregister_callbacks(JavaThread* jt) {
if (jfr_jvmti_env == NULL) {
return JVMTI_ERROR_NONE;
}
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventCallbacks");
return jvmti_ret_code;
}
JfrJvmtiAgent::JfrJvmtiAgent() {}
JfrJvmtiAgent::~JfrJvmtiAgent() {
JavaThread* jt = current_java_thread();
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
ThreadToNativeFromVM transition(jt);
update_class_file_load_hook_event(JVMTI_DISABLE);
unregister_callbacks(jt);
if (jfr_jvmti_env != NULL) {
jfr_jvmti_env->DisposeEnvironment();
jfr_jvmti_env = NULL;
}
agent = NULL;
}
static bool initialize() {
JavaThread* const jt = current_java_thread();
assert(jt != NULL, "invariant");
assert(jt->thread_state() == _thread_in_vm, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
ThreadToNativeFromVM transition(jt);
if (create_jvmti_env(jt) != JNI_OK) {
assert(jfr_jvmti_env == NULL, "invariant");
return false;
}
assert(jfr_jvmti_env != NULL, "invariant");
if (register_capabilities(jt) != JVMTI_ERROR_NONE) {
return false;
}
if (register_callbacks(jt) != JVMTI_ERROR_NONE) {
return false;
}
if (update_class_file_load_hook_event(JVMTI_ENABLE) != JVMTI_ERROR_NONE) {
return false;
}
return true;
}
bool JfrJvmtiAgent::create() {
assert(jfr_jvmti_env == NULL, "invariant");
agent = new JfrJvmtiAgent();
if (agent == NULL) {
return false;
}
if (!initialize()) {
delete agent;
agent = NULL;
return false;
}
return true;
}
void JfrJvmtiAgent::destroy() {
if (agent != NULL) {
delete agent;
agent = NULL;
}
}
C:\hotspot-69087d08d473\src\share\vm/jfr/instrumentation/jfrJvmtiAgent.hpp
#ifndef SHARE_VM_JFR_INSTRUMENTATION_JFRJVMTIAGENT_HPP
#define SHARE_VM_JFR_INSTRUMENTATION_JFRJVMTIAGENT_HPP
#include "jfr/utilities/jfrAllocation.hpp"
class JfrJvmtiAgent : public JfrCHeapObj {
friend class JfrRecorder;
private:
JfrJvmtiAgent();
~JfrJvmtiAgent();
static bool create();
static void destroy();
public:
static void retransform_classes(JNIEnv* env, jobjectArray classes, TRAPS);
};
#endif // SHARE_VM_JFR_INSTRUMENTATION_JFRJVMTIAGENT_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/jfr.cpp
#include "precompiled.hpp"
#include "jfr/jfr.hpp"
#include "jfr/leakprofiler/leakProfiler.hpp"
#include "jfr/periodic/sampling/jfrThreadSampler.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
#include "jfr/recorder/repository/jfrEmergencyDump.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
#include "runtime/java.hpp"
bool Jfr::is_enabled() {
return JfrRecorder::is_enabled();
}
bool Jfr::is_disabled() {
return JfrRecorder::is_disabled();
}
bool Jfr::is_recording() {
return JfrRecorder::is_recording();
}
void Jfr::on_vm_init() {
if (!JfrRecorder::on_vm_init()) {
vm_exit_during_initialization("Failure when starting JFR on_vm_init");
}
}
void Jfr::on_vm_start() {
if (!JfrRecorder::on_vm_start()) {
vm_exit_during_initialization("Failure when starting JFR on_vm_start");
}
}
void Jfr::on_unloading_classes() {
if (JfrRecorder::is_created()) {
JfrCheckpointManager::write_type_set_for_unloaded_classes();
}
}
void Jfr::on_thread_start(Thread* t) {
JfrThreadLocal::on_start(t);
}
void Jfr::on_thread_exit(Thread* t) {
JfrThreadLocal::on_exit(t);
}
void Jfr::on_java_thread_dismantle(JavaThread* jt) {
if (JfrRecorder::is_recording()) {
JfrCheckpointManager::write_thread_checkpoint(jt);
}
}
void Jfr::on_vm_shutdown(bool exception_handler) {
JfrRecorder::set_is_shutting_down();
if (JfrRecorder::is_recording()) {
JfrEmergencyDump::on_vm_shutdown(exception_handler);
}
}
void Jfr::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) {
LeakProfiler::oops_do(is_alive, f);
}
void Jfr::weak_oops_do(OopClosure* f) {
AlwaysTrueClosure always_true;
LeakProfiler::oops_do(&always_true, f);
}
bool Jfr::on_flight_recorder_option(const JavaVMOption** option, char* delimiter) {
return JfrOptionSet::parse_flight_recorder_option(option, delimiter);
}
bool Jfr::on_start_flight_recording_option(const JavaVMOption** option, char* delimiter) {
return JfrOptionSet::parse_start_flight_recording_option(option, delimiter);
}
Thread* Jfr::sampler_thread() {
return JfrThreadSampling::sampler_thread();
}
C:\hotspot-69087d08d473\src\share\vm/jfr/jfr.hpp
#ifndef SHARE_VM_JFR_JFR_HPP
#define SHARE_VM_JFR_JFR_HPP
#include "jni.h"
#include "memory/allocation.hpp"
class BoolObjectClosure;
class JavaThread;
class OopClosure;
class Thread;
extern "C" void JNICALL jfr_register_natives(JNIEnv*, jclass);
class Jfr : AllStatic {
public:
static bool is_enabled();
static bool is_disabled();
static bool is_recording();
static void on_vm_init();
static void on_vm_start();
static void on_unloading_classes();
static void on_thread_start(Thread* thread);
static void on_thread_exit(Thread* thread);
static void on_java_thread_dismantle(JavaThread* jt);
static void on_vm_shutdown(bool exception_handler = false);
static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
static void weak_oops_do(OopClosure* f);
static Thread* sampler_thread();
};
#endif // SHARE_VM_JFR_JFR_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/jfrEvents.hpp
#ifndef SHARE_VM_JFR_JFREVENTS_HPP
#define SHARE_VM_JFR_JFREVENTS_HPP
#include "jfrfiles/jfrEventClasses.hpp"
#include "jfrfiles/jfrEventIds.hpp"
#endif // SHARE_VM_JFR_JFREVENTS_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrGetAllEventClasses.cpp
#include "precompiled.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "jfr/jni/jfrGetAllEventClasses.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/support/jfrEventClass.hpp"
#include "oops/instanceKlass.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/thread.inline.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/stack.inline.hpp"
static jlong unloaded_event_classes = 0;
jlong JfrEventClasses::unloaded_event_classes_count() {
return unloaded_event_classes;
}
void JfrEventClasses::increment_unloaded_event_class() {
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
++unloaded_event_classes;
}
static jobject empty_java_util_arraylist = NULL;
static oop new_java_util_arraylist(TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
JavaValue result(T_OBJECT);
JfrJavaArguments args(&result, "java/util/ArrayList", "<init>", "()V", CHECK_NULL);
JfrJavaSupport::new_object(&args, CHECK_NULL);
return (oop)result.get_jobject();
}
static bool initialize(TRAPS) {
static bool initialized = false;
if (!initialized) {
unloaded_event_classes = 0;
assert(NULL == empty_java_util_arraylist, "invariant");
const oop array_list = new_java_util_arraylist(CHECK_false);
empty_java_util_arraylist = JfrJavaSupport::global_jni_handle(array_list, THREAD);
initialized = empty_java_util_arraylist != NULL;
}
return initialized;
}
static bool is_whitelisted(const Klass* k) {
assert(k != NULL, "invariant");
return !(k->is_abstract() || k->should_be_initialized());
}
static void fill_klasses(GrowableArray<const void*>& event_subklasses, const Klass* event_klass, Thread* thread) {
assert(event_subklasses.length() == 0, "invariant");
assert(event_klass != NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread));
Stack<const Klass*, mtTracing> mark_stack;
MutexLocker ml(Compile_lock, thread);
mark_stack.push(event_klass->subklass());
while (!mark_stack.is_empty()) {
const Klass* const current = mark_stack.pop();
assert(current != NULL, "null element in stack!");
if (is_whitelisted(current)) {
event_subklasses.append(current);
}
const Klass* next_klass = current->subklass();
if (next_klass != NULL) {
mark_stack.push(next_klass);
}
next_klass = current->next_sibling();
if (next_klass != NULL) {
mark_stack.push(next_klass);
}
}
assert(mark_stack.is_empty(), "invariant");
}
static void transform_klasses_to_local_jni_handles(GrowableArray<const void*>& event_subklasses, Thread* thread) {
assert(event_subklasses.is_nonempty(), "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread));
for (int i = 0; i < event_subklasses.length(); ++i) {
const InstanceKlass* k = static_cast<const InstanceKlass*>(event_subklasses.at(i));
assert(is_whitelisted(k), "invariant");
event_subklasses.at_put(i, JfrJavaSupport::local_jni_handle(k->java_mirror(), thread));
}
}
static const int initial_size_growable_array = 64;
jobject JfrEventClasses::get_all_event_classes(TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
initialize(THREAD);
assert(empty_java_util_arraylist != NULL, "should have been setup already!");
static const char jdk_jfr_event_name[] = "jdk/jfr/Event";
unsigned int unused_hash = 0;
Symbol* const event_klass_name = SymbolTable::lookup_only(jdk_jfr_event_name, sizeof jdk_jfr_event_name - 1, unused_hash);
if (NULL == event_klass_name) {
return empty_java_util_arraylist;
}
const Klass* const klass = SystemDictionary::resolve_or_null(event_klass_name, THREAD);
assert(klass != NULL, "invariant");
assert(JdkJfrEvent::is(klass), "invariant");
if (klass->subklass() == NULL) {
return empty_java_util_arraylist;
}
ResourceMark rm(THREAD);
GrowableArray<const void*> event_subklasses(THREAD, initial_size_growable_array);
fill_klasses(event_subklasses, klass, THREAD);
if (event_subklasses.is_empty()) {
return empty_java_util_arraylist;
}
transform_klasses_to_local_jni_handles(event_subklasses, THREAD);
Handle h_array_list(THREAD, new_java_util_arraylist(THREAD));
assert(h_array_list.not_null(), "invariant");
static const char add_method_name[] = "add";
static const char add_method_signature[] = "(Ljava/lang/Object;)Z";
const Klass* const array_list_klass = JfrJavaSupport::klass(empty_java_util_arraylist);
assert(array_list_klass != NULL, "invariant");
const Symbol* const add_method_sym = SymbolTable::lookup(add_method_name, sizeof add_method_name - 1, THREAD);
assert(add_method_sym != NULL, "invariant");
const Symbol* const add_method_sig_sym = SymbolTable::lookup(add_method_signature, sizeof add_method_signature - 1, THREAD);
assert(add_method_signature != NULL, "invariant");
JavaValue result(T_BOOLEAN);
for (int i = 0; i < event_subklasses.length(); ++i) {
const jclass clazz = (const jclass)event_subklasses.at(i);
assert(JdkJfrEvent::is_subklass(clazz), "invariant");
JfrJavaArguments args(&result, array_list_klass, add_method_sym, add_method_sig_sym);
args.set_receiver(h_array_list());
args.push_jobject(clazz);
JfrJavaSupport::call_virtual(&args, THREAD);
if (HAS_PENDING_EXCEPTION || JNI_FALSE == result.get_jboolean()) {
return empty_java_util_arraylist;
}
}
return JfrJavaSupport::local_jni_handle(h_array_list(), THREAD);
}
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrGetAllEventClasses.hpp
#ifndef SHARE_VM_JFR_JNI_JFRGETALLEVENTCLASSES_HPP
#define SHARE_VM_JFR_JNI_JFRGETALLEVENTCLASSES_HPP
#include "jni.h"
#include "memory/allocation.hpp"
#include "utilities/exceptions.hpp"
class JfrEventClasses : AllStatic {
public:
static void increment_unloaded_event_class();
static jlong unloaded_event_classes_count();
static jobject get_all_event_classes(TRAPS);
};
#endif // SHARE_VM_JFR_JNI_JFRGETALLEVENTCLASSES_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrJavaCall.cpp
#include "precompiled.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "jfr/jni/jfrJavaCall.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/javaCalls.hpp"
#include "utilities/globalDefinitions.hpp"
#ifdef ASSERT
static bool is_large_value(const JavaValue& value) {
return value.get_type() == T_LONG || value.get_type() == T_DOUBLE;
}
#endif // ASSERT
static Symbol* resolve(const char* str, TRAPS) {
assert(str != NULL, "invariant");
return SymbolTable::lookup(str, (int)strlen(str), THREAD);
}
static Klass* resolve(Symbol* k_sym, TRAPS) {
assert(k_sym != NULL, "invariant");
return SystemDictionary::resolve_or_fail(k_sym, true, THREAD);
}
JfrJavaArguments::Parameters::Parameters() : _storage_index(0), _java_stack_slots(0) {
JavaValue value(T_VOID);
push(value);
}
void JfrJavaArguments::Parameters::push(const JavaValue& value) {
assert(_storage != NULL, "invariant");
assert(!is_large_value(value), "invariant");
assert(_storage_index < SIZE, "invariant");
_storage[_storage_index++] = value;
_java_stack_slots++;
}
void JfrJavaArguments::Parameters::push_large(const JavaValue& value) {
assert(_storage != NULL, "invariant");
assert(is_large_value(value), "invariant");
assert(_storage_index < SIZE, "invariant");
_storage[_storage_index++] = value;
_java_stack_slots += 2;
}
void JfrJavaArguments::Parameters::set_receiver(const oop receiver) {
assert(_storage != NULL, "invariant");
assert(receiver != NULL, "invariant");
JavaValue value(T_OBJECT);
value.set_jobject((jobject)receiver);
_storage[0] = value;
}
void JfrJavaArguments::Parameters::set_receiver(Handle receiver) {
set_receiver(receiver());
}
oop JfrJavaArguments::Parameters::receiver() const {
assert(has_receiver(), "invariant");
assert(_storage[0].get_type() == T_OBJECT, "invariant");
return (oop)_storage[0].get_jobject();
}
bool JfrJavaArguments::Parameters::has_receiver() const {
assert(_storage != NULL, "invariant");
assert(_storage_index >= 1, "invariant");
assert(_java_stack_slots >= 1, "invariant");
return _storage[0].get_type() == T_OBJECT;
}
void JfrJavaArguments::Parameters::push_oop(const oop obj) {
JavaValue value(T_OBJECT);
value.set_jobject((jobject)obj);
push(value);
}
void JfrJavaArguments::Parameters::push_oop(Handle h_obj) {
push_oop(h_obj());
}
void JfrJavaArguments::Parameters::push_jobject(jobject h) {
JavaValue value(T_ADDRESS);
value.set_jobject(h);
push(value);
}
void JfrJavaArguments::Parameters::push_jint(jint i) {
JavaValue value(T_INT);
value.set_jint(i);
push(value);
}
void JfrJavaArguments::Parameters::push_jfloat(jfloat f) {
JavaValue value(T_FLOAT);
value.set_jfloat(f);
push(value);
}
void JfrJavaArguments::Parameters::push_jdouble(jdouble d) {
JavaValue value(T_DOUBLE);
value.set_jdouble(d);
push_large(value);
}
void JfrJavaArguments::Parameters::push_jlong(jlong l) {
JavaValue value(T_LONG);
value.set_jlong(l);
push_large(value);
}
inline int JfrJavaArguments::Parameters::length() const {
assert(_storage_index >= 1, "invariant");
return _storage_index;
}
inline int JfrJavaArguments::Parameters::java_stack_slots() const {
return _java_stack_slots;
}
const JavaValue& JfrJavaArguments::Parameters::values(int idx) const {
assert(idx >= 0, "invariant");
assert(idx < SIZE, "invariant");
return _storage[idx];
}
void JfrJavaArguments::Parameters::copy(JavaCallArguments& args, TRAPS) const {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
if (has_receiver()) {
args.set_receiver(Handle(THREAD, receiver()));
}
for (int i = 1; i < length(); ++i) {
switch(values(i).get_type()) {
case T_BOOLEAN:
case T_CHAR:
case T_SHORT:
case T_INT:
args.push_int(values(i).get_jint());
break;
case T_LONG:
args.push_long(values(i).get_jlong());
break;
case T_FLOAT:
args.push_float(values(i).get_jfloat());
break;
case T_DOUBLE:
args.push_double(values(i).get_jdouble());
break;
case T_OBJECT:
args.push_oop(Handle(THREAD, (oop)values(i).get_jobject()));
break;
case T_ADDRESS:
args.push_oop(Handle(THREAD, JNIHandles::resolve(values(i).get_jobject())));
break;
default:
ShouldNotReachHere();
}
}
}
JfrJavaArguments::JfrJavaArguments(JavaValue* result) : _result(result), _klass(NULL), _name(NULL), _signature(NULL), _array_length(-1) {
assert(result != NULL, "invariant");
}
JfrJavaArguments::JfrJavaArguments(JavaValue* result, const char* klass_name, const char* name, const char* signature, TRAPS) :
_result(result),
_klass(NULL),
_name(NULL),
_signature(NULL),
_array_length(-1) {
assert(result != NULL, "invariant");
if (klass_name != NULL) {
set_klass(klass_name, CHECK);
}
if (name != NULL) {
set_name(name, CHECK);
}
if (signature != NULL) {
set_signature(signature, THREAD);
}
}
JfrJavaArguments::JfrJavaArguments(JavaValue* result, const Klass* klass, const Symbol* name, const Symbol* signature) : _result(result),
_klass(NULL),
_name(NULL),
_signature(NULL),
_array_length(-1) {
assert(result != NULL, "invariant");
if (klass != NULL) {
set_klass(klass);
}
if (name != NULL) {
set_name(name);
}
if (signature != NULL) {
set_signature(signature);
}
}
Klass* JfrJavaArguments::klass() const {
assert(_klass != NULL, "invariant");
return const_cast<Klass*>(_klass);
}
void JfrJavaArguments::set_klass(const char* klass_name, TRAPS) {
assert(klass_name != NULL, "invariant");
Symbol* const k_sym = resolve(klass_name, CHECK);
assert(k_sym != NULL, "invariant");
const Klass* const klass = resolve(k_sym, CHECK);
set_klass(klass);
}
void JfrJavaArguments::set_klass(const Klass* klass) {
assert(klass != NULL, "invariant");
_klass = klass;
}
Symbol* JfrJavaArguments::name() const {
assert(_name != NULL, "invariant");
return const_cast<Symbol*>(_name);
}
void JfrJavaArguments::set_name(const char* name, TRAPS) {
assert(name != NULL, "invariant");
const Symbol* const sym = resolve(name, CHECK);
set_name(sym);
}
void JfrJavaArguments::set_name(const Symbol* name) {
assert(name != NULL, "invariant");
_name = name;
}
Symbol* JfrJavaArguments::signature() const {
assert(_signature != NULL, "invariant");
return const_cast<Symbol*>(_signature);
}
void JfrJavaArguments::set_signature(const char* signature, TRAPS) {
assert(signature != NULL, "invariant");
const Symbol* const sym = resolve(signature, CHECK);
set_signature(sym);
}
void JfrJavaArguments::set_signature(const Symbol* signature) {
assert(signature != NULL, "invariant");
_signature = signature;
}
int JfrJavaArguments::array_length() const {
return _array_length;
}
void JfrJavaArguments::set_array_length(int length) {
assert(length >= 0, "invariant");
_array_length = length;
}
JavaValue* JfrJavaArguments::result() const {
assert(_result != NULL, "invariant");
return const_cast<JavaValue*>(_result);
}
int JfrJavaArguments::length() const {
return _params.length();
}
bool JfrJavaArguments::has_receiver() const {
return _params.has_receiver();
}
oop JfrJavaArguments::receiver() const {
return _params.receiver();
}
void JfrJavaArguments::set_receiver(const oop receiver) {
_params.set_receiver(receiver);
}
void JfrJavaArguments::set_receiver(Handle receiver) {
_params.set_receiver(receiver);
}
void JfrJavaArguments::push_oop(const oop obj) {
_params.push_oop(obj);
}
void JfrJavaArguments::push_oop(Handle h_obj) {
_params.push_oop(h_obj);
}
void JfrJavaArguments::push_jobject(jobject h) {
_params.push_jobject(h);
}
void JfrJavaArguments::push_int(jint i) {
_params.push_jint(i);
}
void JfrJavaArguments::push_float(jfloat f) {
_params.push_jfloat(f);
}
void JfrJavaArguments::push_double(jdouble d) {
_params.push_jdouble(d);
}
void JfrJavaArguments::push_long(jlong l) {
_params.push_jlong(l);
}
const JavaValue& JfrJavaArguments::param(int idx) const {
return _params.values(idx);
}
int JfrJavaArguments::java_call_arg_slots() const {
return _params.java_stack_slots();
}
void JfrJavaArguments::copy(JavaCallArguments& args, TRAPS) {
_params.copy(args, THREAD);
}
void JfrJavaCall::call_static(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
JavaCallArguments jcas(args->java_call_arg_slots());
args->copy(jcas, CHECK);
JavaCalls::call_static(args->result(), args->klass(), args->name(), args->signature(), &jcas, THREAD);
}
void JfrJavaCall::call_special(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
assert(args->has_receiver(), "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
JavaCallArguments jcas(args->java_call_arg_slots());
args->copy(jcas, CHECK);
JavaCalls::call_special(args->result(), args->klass(), args->name(), args->signature(), &jcas, THREAD);
}
void JfrJavaCall::call_virtual(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
assert(args->has_receiver(), "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
JavaCallArguments jcas(args->java_call_arg_slots());
args->copy(jcas, CHECK);
JavaCalls::call_virtual(args->result(), args->klass(), args->name(), args->signature(), &jcas, THREAD);
}
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrJavaCall.hpp
#ifndef SHARE_VM_JFR_JNI_JFRJAVACALL_HPP
#define SHARE_VM_JFR_JNI_JFRJAVACALL_HPP
#include "jni.h"
#include "jfr/utilities/jfrAllocation.hpp"
#include "utilities/exceptions.hpp"
class JavaCallArguments;
class JavaThread;
class JavaValue;
class Klass;
class Symbol;
class JfrJavaArguments : public StackObj {
friend class JfrJavaCall;
public:
JfrJavaArguments(JavaValue* result);
JfrJavaArguments(JavaValue* result, const char* klass_name, const char* name, const char* signature, TRAPS);
JfrJavaArguments(JavaValue* result, const Klass* klass, const Symbol* name, const Symbol* signature);
Klass* klass() const;
void set_klass(const char* klass_name, TRAPS);
void set_klass(const Klass* klass);
Symbol* name() const;
void set_name(const char* name, TRAPS);
void set_name(const Symbol* name);
Symbol* signature() const;
void set_signature(const char* signature, TRAPS);
void set_signature(const Symbol* signature);
int array_length() const;
void set_array_length(int length);
JavaValue* result() const;
bool has_receiver() const;
void set_receiver(const oop receiver);
void set_receiver(Handle receiver);
oop receiver() const;
void push_oop(const oop obj);
void push_oop(Handle h_obj);
void push_jobject(jobject h);
void push_int(jint i);
void push_double(jdouble d);
void push_long(jlong l);
void push_float(jfloat f);
int length() const;
const JavaValue& param(int idx) const;
private:
class Parameters {
friend class JfrJavaArguments;
private:
enum { SIZE = 16};
JavaValue _storage[SIZE];
int _storage_index;
int _java_stack_slots;
Parameters();
Parameters(const Parameters&); // no impl
Parameters& operator=(const Parameters&); // no impl
void push(const JavaValue& value);
void push_large(const JavaValue& value);
void push_oop(const oop obj);
void push_oop(Handle h_obj);
void push_jobject(jobject h);
void push_jint(jint i);
void push_jdouble(jdouble d);
void push_jlong(jlong l);
void push_jfloat(jfloat f);
bool has_receiver() const;
void set_receiver(const oop receiver);
void set_receiver(Handle receiver);
oop receiver() const;
int length() const;
int java_stack_slots() const;
void copy(JavaCallArguments& args, TRAPS) const;
const JavaValue& values(int idx) const;
};
Parameters _params;
const JavaValue* const _result;
const Klass* _klass;
const Symbol* _name;
const Symbol* _signature;
int _array_length;
int java_call_arg_slots() const;
void copy(JavaCallArguments& args, TRAPS);
};
class JfrJavaCall : public AllStatic {
friend class JfrJavaSupport;
private:
static void call_static(JfrJavaArguments* args, TRAPS);
static void call_special(JfrJavaArguments* args, TRAPS);
static void call_virtual(JfrJavaArguments* args, TRAPS);
};
#endif // SHARE_VM_JFR_JNI_JFRJAVACALL_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrJavaSupport.cpp
#include "precompiled.hpp"
#include "jni.h"
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
#include "jfr/jni/jfrJavaCall.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/support/jfrThreadId.hpp"
#include "memory/resourceArea.hpp"
#include "oops/instanceOop.hpp"
#include "oops/oop.inline.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/fieldDescriptor.hpp"
#include "runtime/java.hpp"
#include "runtime/jniHandles.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/thread.inline.hpp"
#ifdef ASSERT
void JfrJavaSupport::check_java_thread_in_vm(Thread* t) {
assert(t != NULL, "invariant");
assert(t->is_Java_thread(), "invariant");
assert(((JavaThread*)t)->thread_state() == _thread_in_vm, "invariant");
}
void JfrJavaSupport::check_java_thread_in_native(Thread* t) {
assert(t != NULL, "invariant");
assert(t->is_Java_thread(), "invariant");
assert(((JavaThread*)t)->thread_state() == _thread_in_native, "invariant");
}
#endif
jobject JfrJavaSupport::local_jni_handle(const oop obj, Thread* t) {
DEBUG_ONLY(check_java_thread_in_vm(t));
return t->active_handles()->allocate_handle(obj);
}
jobject JfrJavaSupport::local_jni_handle(const jobject handle, Thread* t) {
DEBUG_ONLY(check_java_thread_in_vm(t));
const oop obj = JNIHandles::resolve(handle);
return obj == NULL ? NULL : local_jni_handle(obj, t);
}
void JfrJavaSupport::destroy_local_jni_handle(jobject handle) {
JNIHandles::destroy_local(handle);
}
jobject JfrJavaSupport::global_jni_handle(const oop obj, Thread* t) {
DEBUG_ONLY(check_java_thread_in_vm(t));
HandleMark hm(t);
return JNIHandles::make_global(Handle(t, obj));
}
jobject JfrJavaSupport::global_jni_handle(const jobject handle, Thread* t) {
const oop obj = JNIHandles::resolve(handle);
return obj == NULL ? NULL : global_jni_handle(obj, t);
}
void JfrJavaSupport::destroy_global_jni_handle(const jobject handle) {
JNIHandles::destroy_global(handle);
}
oop JfrJavaSupport::resolve_non_null(jobject obj) {
return JNIHandles::resolve_non_null(obj);
}
void JfrJavaSupport::call_static(JfrJavaArguments* args, TRAPS) {
JfrJavaCall::call_static(args, THREAD);
}
void JfrJavaSupport::call_special(JfrJavaArguments* args, TRAPS) {
JfrJavaCall::call_special(args, THREAD);
}
void JfrJavaSupport::call_virtual(JfrJavaArguments* args, TRAPS) {
JfrJavaCall::call_virtual(args, THREAD);
}
void JfrJavaSupport::notify_all(jobject object, TRAPS) {
assert(object != NULL, "invariant");
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
HandleMark hm(THREAD);
Handle h_obj(THREAD, resolve_non_null(object));
assert(h_obj.not_null(), "invariant");
ObjectSynchronizer::jni_enter(h_obj, THREAD);
ObjectSynchronizer::notifyall(h_obj, THREAD);
ObjectSynchronizer::jni_exit(h_obj(), THREAD);
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
}
static void object_construction(JfrJavaArguments* args, JavaValue* result, InstanceKlass* klass, TRAPS) {
assert(args != NULL, "invariant");
assert(result != NULL, "invariant");
assert(klass != NULL, "invariant");
assert(klass->is_initialized(), "invariant");
HandleMark hm(THREAD);
instanceOop obj = klass->allocate_instance(CHECK);
instanceHandle h_obj(THREAD, obj);
assert(h_obj.not_null(), "invariant");
args->set_receiver(h_obj);
result->set_type(T_VOID); // constructor result type
JfrJavaSupport::call_special(args, CHECK);
result->set_type(T_OBJECT); // set back to original result type
result->set_jobject((jobject)h_obj());
}
static void array_construction(JfrJavaArguments* args, JavaValue* result, InstanceKlass* klass, int array_length, TRAPS) {
assert(args != NULL, "invariant");
assert(result != NULL, "invariant");
assert(klass != NULL, "invariant");
assert(klass->is_initialized(), "invariant");
Klass* const ak = klass->array_klass(THREAD);
ObjArrayKlass::cast(ak)->initialize(THREAD);
HandleMark hm(THREAD);
objArrayOop arr = ObjArrayKlass::cast(ak)->allocate(array_length, CHECK);
result->set_jobject((jobject)arr);
}
static void create_object(JfrJavaArguments* args, JavaValue* result, TRAPS) {
assert(args != NULL, "invariant");
assert(result != NULL, "invariant");
assert(result->get_type() == T_OBJECT, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
InstanceKlass* const klass = static_cast<InstanceKlass*>(args->klass());
klass->initialize(CHECK);
const int array_length = args->array_length();
if (array_length >= 0) {
array_construction(args, result, klass, array_length, CHECK);
} else {
object_construction(args, result, klass, THREAD);
}
}
static void handle_result(JavaValue* result, bool global_ref, Thread* t) {
assert(result != NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(t));
const oop result_oop = (const oop)result->get_jobject();
if (result_oop == NULL) {
return;
}
result->set_jobject(global_ref ?
JfrJavaSupport::global_jni_handle(result_oop, t) :
JfrJavaSupport::local_jni_handle(result_oop, t));
}
void JfrJavaSupport::new_object(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
create_object(args, args->result(), THREAD);
}
void JfrJavaSupport::new_object_local_ref(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
JavaValue* const result = args->result();
assert(result != NULL, "invariant");
create_object(args, result, CHECK);
handle_result(result, false, THREAD);
}
void JfrJavaSupport::new_object_global_ref(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
JavaValue* const result = args->result();
assert(result != NULL, "invariant");
create_object(args, result, CHECK);
handle_result(result, true, THREAD);
}
jstring JfrJavaSupport::new_string(const char* c_str, TRAPS) {
assert(c_str != NULL, "invariant");
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
const oop result = java_lang_String::create_oop_from_str(c_str, THREAD);
return (jstring)local_jni_handle(result, THREAD);
}
jobjectArray JfrJavaSupport::new_string_array(int length, TRAPS) {
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
JavaValue result(T_OBJECT);
JfrJavaArguments args(&result, "java/lang/String", "<init>", "()V", CHECK_NULL);
args.set_array_length(length);
new_object_local_ref(&args, THREAD);
return (jobjectArray)args.result()->get_jobject();
}
jobject JfrJavaSupport::new_java_lang_Boolean(bool value, TRAPS) {
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
JavaValue result(T_OBJECT);
JfrJavaArguments args(&result, "java/lang/Boolean", "<init>", "(Z)V", CHECK_NULL);
args.push_int(value ? (jint)JNI_TRUE : (jint)JNI_FALSE);
new_object_local_ref(&args, THREAD);
return args.result()->get_jobject();
}
jobject JfrJavaSupport::new_java_lang_Integer(jint value, TRAPS) {
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
JavaValue result(T_OBJECT);
JfrJavaArguments args(&result, "java/lang/Integer", "<init>", "(I)V", CHECK_NULL);
args.push_int(value);
new_object_local_ref(&args, THREAD);
return args.result()->get_jobject();
}
jobject JfrJavaSupport::new_java_lang_Long(jlong value, TRAPS) {
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
JavaValue result(T_OBJECT);
JfrJavaArguments args(&result, "java/lang/Long", "<init>", "(J)V", CHECK_NULL);
args.push_long(value);
new_object_local_ref(&args, THREAD);
return args.result()->get_jobject();
}
void JfrJavaSupport::set_array_element(jobjectArray arr, jobject element, int index, Thread* t) {
assert(arr != NULL, "invariant");
DEBUG_ONLY(check_java_thread_in_vm(t));
HandleMark hm(t);
objArrayHandle a(t, (objArrayOop)resolve_non_null(arr));
a->obj_at_put(index, resolve_non_null(element));
}
static void write_int_field(const Handle& h_oop, fieldDescriptor* fd, jint value) {
assert(h_oop.not_null(), "invariant");
assert(fd != NULL, "invariant");
h_oop->int_field_put(fd->offset(), value);
}
static void write_float_field(const Handle& h_oop, fieldDescriptor* fd, jfloat value) {
assert(h_oop.not_null(), "invariant");
assert(fd != NULL, "invariant");
h_oop->float_field_put(fd->offset(), value);
}
static void write_double_field(const Handle& h_oop, fieldDescriptor* fd, jdouble value) {
assert(h_oop.not_null(), "invariant");
assert(fd != NULL, "invariant");
h_oop->double_field_put(fd->offset(), value);
}
static void write_long_field(const Handle& h_oop, fieldDescriptor* fd, jlong value) {
assert(h_oop.not_null(), "invariant");
assert(fd != NULL, "invariant");
h_oop->long_field_put(fd->offset(), value);
}
static void write_oop_field(const Handle& h_oop, fieldDescriptor* fd, const oop value) {
assert(h_oop.not_null(), "invariant");
assert(fd != NULL, "invariant");
h_oop->obj_field_put(fd->offset(), value);
}
static void write_specialized_field(JfrJavaArguments* args, const Handle& h_oop, fieldDescriptor* fd, bool static_field) {
assert(args != NULL, "invariant");
assert(h_oop.not_null(), "invariant");
assert(fd != NULL, "invariant");
assert(fd->offset() > 0, "invariant");
assert(args->length() >= 1, "invariant");
assert(args->param(1).get_type() != T_VOID, "invariant");
switch(fd->field_type()) {
case T_BOOLEAN:
case T_CHAR:
case T_SHORT:
case T_INT:
write_int_field(h_oop, fd, args->param(1).get_jint());
break;
case T_FLOAT:
write_float_field(h_oop, fd, args->param(1).get_jfloat());
break;
case T_DOUBLE:
write_double_field(h_oop, fd, args->param(1).get_jdouble());
break;
case T_LONG:
write_long_field(h_oop, fd, args->param(1).get_jlong());
break;
case T_OBJECT:
write_oop_field(h_oop, fd, (oop)args->param(1).get_jobject());
break;
case T_ADDRESS:
write_oop_field(h_oop, fd, JfrJavaSupport::resolve_non_null(args->param(1).get_jobject()));
break;
default:
ShouldNotReachHere();
}
}
static void read_specialized_field(JavaValue* result, const Handle& h_oop, fieldDescriptor* fd) {
assert(result != NULL, "invariant");
assert(h_oop.not_null(), "invariant");
assert(fd != NULL, "invariant");
assert(fd->offset() > 0, "invariant");
switch(fd->field_type()) {
case T_BOOLEAN:
case T_CHAR:
case T_SHORT:
case T_INT:
result->set_jint(h_oop->int_field(fd->offset()));
break;
case T_FLOAT:
result->set_jfloat(h_oop->float_field(fd->offset()));
break;
case T_DOUBLE:
result->set_jdouble(h_oop->double_field(fd->offset()));
break;
case T_LONG:
result->set_jlong(h_oop->long_field(fd->offset()));
break;
case T_OBJECT:
result->set_jobject((jobject)h_oop->obj_field(fd->offset()));
break;
default:
ShouldNotReachHere();
}
}
static bool find_field(InstanceKlass* ik,
Symbol* name_symbol,
Symbol* signature_symbol,
fieldDescriptor* fd,
bool is_static = false,
bool allow_super = false) {
if (allow_super || is_static) {
return ik->find_field(name_symbol, signature_symbol, is_static, fd) != NULL;
}
return ik->find_local_field(name_symbol, signature_symbol, fd);
}
static void lookup_field(JfrJavaArguments* args, InstanceKlass* klass, fieldDescriptor* fd, bool static_field) {
assert(args != NULL, "invariant");
assert(klass != NULL, "invariant");
assert(klass->is_initialized(), "invariant");
assert(fd != NULL, "invariant");
find_field(klass, args->name(), args->signature(), fd, static_field, true);
}
static void read_field(JfrJavaArguments* args, JavaValue* result, TRAPS) {
assert(args != NULL, "invariant");
assert(result != NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
InstanceKlass* const klass = static_cast<InstanceKlass*>(args->klass());
klass->initialize(CHECK);
const bool static_field = !args->has_receiver();
fieldDescriptor fd;
lookup_field(args, klass, &fd, static_field);
assert(fd.offset() > 0, "invariant");
HandleMark hm(THREAD);
Handle h_oop(static_field ? Handle(THREAD, klass->java_mirror()) : Handle(THREAD, args->receiver()));
read_specialized_field(result, h_oop, &fd);
}
static void write_field(JfrJavaArguments* args, JavaValue* result, TRAPS) {
assert(args != NULL, "invariant");
assert(result != NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
InstanceKlass* const klass = static_cast<InstanceKlass*>(args->klass());
klass->initialize(CHECK);
const bool static_field = !args->has_receiver();
fieldDescriptor fd;
lookup_field(args, klass, &fd, static_field);
assert(fd.offset() > 0, "invariant");
HandleMark hm(THREAD);
Handle h_oop(static_field ? Handle(THREAD, klass->java_mirror()) : Handle(THREAD, args->receiver()));
write_specialized_field(args, h_oop, &fd, static_field);
}
void JfrJavaSupport::set_field(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
write_field(args, args->result(), THREAD);
}
void JfrJavaSupport::get_field(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
read_field(args, args->result(), THREAD);
}
void JfrJavaSupport::get_field_local_ref(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
JavaValue* const result = args->result();
assert(result != NULL, "invariant");
assert(result->get_type() == T_OBJECT, "invariant");
read_field(args, result, CHECK);
const oop obj = (const oop)result->get_jobject();
if (obj != NULL) {
result->set_jobject(local_jni_handle(obj, THREAD));
}
}
void JfrJavaSupport::get_field_global_ref(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
JavaValue* const result = args->result();
assert(result != NULL, "invariant");
assert(result->get_type() == T_OBJECT, "invariant");
read_field(args, result, CHECK);
const oop obj = (const oop)result->get_jobject();
if (obj != NULL) {
result->set_jobject(global_jni_handle(obj, THREAD));
}
}
Klass* JfrJavaSupport::klass(const jobject handle) {
const oop obj = resolve_non_null(handle);
assert(obj != NULL, "invariant");
return obj->klass();
}
const char* JfrJavaSupport::c_str(jstring string, Thread* t) {
DEBUG_ONLY(check_java_thread_in_vm(t));
if (string == NULL) {
return NULL;
}
const char* temp = NULL;
const oop java_string = resolve_non_null(string);
if (java_lang_String::value(java_string) != NULL) {
const size_t length = java_lang_String::utf8_length(java_string);
temp = NEW_RESOURCE_ARRAY_IN_THREAD(t, const char, (length + 1));
if (temp == NULL) {
JfrJavaSupport::throw_out_of_memory_error("Unable to allocate thread local native memory", t);
return NULL;
}
assert(temp != NULL, "invariant");
java_lang_String::as_utf8_string(java_string, const_cast<char*>(temp), (int) length + 1);
}
return temp;
}
static void create_and_throw(Symbol* name, const char* message, TRAPS) {
assert(name != NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
assert(!HAS_PENDING_EXCEPTION, "invariant");
THROW_MSG(name, message);
}
void JfrJavaSupport::throw_illegal_state_exception(const char* message, TRAPS) {
create_and_throw(vmSymbols::java_lang_IllegalStateException(), message, THREAD);
}
void JfrJavaSupport::throw_internal_error(const char* message, TRAPS) {
create_and_throw(vmSymbols::java_lang_InternalError(), message, THREAD);
}
void JfrJavaSupport::throw_illegal_argument_exception(const char* message, TRAPS) {
create_and_throw(vmSymbols::java_lang_IllegalArgumentException(), message, THREAD);
}
void JfrJavaSupport::throw_out_of_memory_error(const char* message, TRAPS) {
create_and_throw(vmSymbols::java_lang_OutOfMemoryError(), message, THREAD);
}
void JfrJavaSupport::throw_class_format_error(const char* message, TRAPS) {
create_and_throw(vmSymbols::java_lang_ClassFormatError(), message, THREAD);
}
void JfrJavaSupport::abort(jstring errorMsg, Thread* t) {
DEBUG_ONLY(check_java_thread_in_vm(t));
ResourceMark rm(t);
const char* const error_msg = c_str(errorMsg, t);
if (error_msg != NULL) {
if (true) tty->print_cr("%s",error_msg);
}
if (true) tty->print_cr("%s", "An irrecoverable error in Jfr. Shutting down VM...");
vm_abort();
}
JfrJavaSupport::CAUSE JfrJavaSupport::_cause = JfrJavaSupport::VM_ERROR;
void JfrJavaSupport::set_cause(jthrowable throwable, Thread* t) {
DEBUG_ONLY(check_java_thread_in_vm(t));
HandleMark hm(t);
Handle ex(t, JNIHandles::resolve_external_guard(throwable));
if (ex.is_null()) {
return;
}
if (ex->is_a(SystemDictionary::OutOfMemoryError_klass())) {
_cause = OUT_OF_MEMORY;
return;
}
if (ex->is_a(SystemDictionary::StackOverflowError_klass())) {
_cause = STACK_OVERFLOW;
return;
}
if (ex->is_a(SystemDictionary::Error_klass())) {
_cause = VM_ERROR;
return;
}
if (ex->is_a(SystemDictionary::RuntimeException_klass())) {
_cause = RUNTIME_EXCEPTION;
return;
}
if (ex->is_a(SystemDictionary::Exception_klass())) {
_cause = UNKNOWN;
return;
}
}
void JfrJavaSupport::uncaught_exception(jthrowable throwable, Thread* t) {
DEBUG_ONLY(check_java_thread_in_vm(t));
assert(throwable != NULL, "invariant");
set_cause(throwable, t);
}
JfrJavaSupport::CAUSE JfrJavaSupport::cause() {
return _cause;
}
jlong JfrJavaSupport::jfr_thread_id(jobject target_thread) {
JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(target_thread));
return native_thread != NULL ? JFR_THREAD_ID(native_thread) : 0;
}
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrJavaSupport.hpp
#ifndef SHARE_VM_JFR_JNI_JFRJAVASUPPORT_HPP
#define SHARE_VM_JFR_JNI_JFRJAVASUPPORT_HPP
#include "jfr/jni/jfrJavaCall.hpp"
#include "utilities/exceptions.hpp"
class Klass;
class JavaThread;
class outputStream;
class JfrJavaSupport : public AllStatic {
public:
static jobject local_jni_handle(const oop obj, Thread* t);
static jobject local_jni_handle(const jobject handle, Thread* t);
static void destroy_local_jni_handle(const jobject handle);
static jobject global_jni_handle(const oop obj, Thread* t);
static jobject global_jni_handle(const jobject handle, Thread* t);
static void destroy_global_jni_handle(const jobject handle);
static oop resolve_non_null(jobject obj);
static void notify_all(jobject obj, TRAPS);
static void set_array_element(jobjectArray arr, jobject element, int index, Thread* t);
static void call_static(JfrJavaArguments* args, TRAPS);
static void call_special(JfrJavaArguments* args, TRAPS);
static void call_virtual(JfrJavaArguments* args, TRAPS);
static void set_field(JfrJavaArguments* args, TRAPS);
static void get_field(JfrJavaArguments* args, TRAPS);
static void new_object(JfrJavaArguments* args, TRAPS);
static void new_object_global_ref(JfrJavaArguments* args, TRAPS);
static void get_field_global_ref(JfrJavaArguments* args, TRAPS);
static void new_object_local_ref(JfrJavaArguments* args, TRAPS);
static void get_field_local_ref(JfrJavaArguments* args, TRAPS);
static jstring new_string(const char* c_str, TRAPS);
static jobjectArray new_string_array(int length, TRAPS);
static jobject new_java_lang_Boolean(bool value, TRAPS);
static jobject new_java_lang_Integer(jint value, TRAPS);
static jobject new_java_lang_Long(jlong value, TRAPS);
static Klass* klass(const jobject handle);
static const char* c_str(jstring string, Thread* jt);
static void throw_illegal_state_exception(const char* message, TRAPS);
static void throw_illegal_argument_exception(const char* message, TRAPS);
static void throw_internal_error(const char* message, TRAPS);
static void throw_out_of_memory_error(const char* message, TRAPS);
static void throw_class_format_error(const char* message, TRAPS);
static jlong jfr_thread_id(jobject target_thread);
static void abort(jstring errorMsg, TRAPS);
static void uncaught_exception(jthrowable throwable, Thread* t);
DEBUG_ONLY(static void check_java_thread_in_vm(Thread* t);)
DEBUG_ONLY(static void check_java_thread_in_native(Thread* t);)
enum CAUSE {
VM_ERROR,
OUT_OF_MEMORY,
STACK_OVERFLOW,
RUNTIME_EXCEPTION,
UNKNOWN,
NOF_CAUSES
};
static CAUSE cause();
private:
static CAUSE _cause;
static void set_cause(jthrowable throwable, Thread* t);
};
#endif // SHARE_VM_JFR_JNI_JFRJAVASUPPORT_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrJniMethod.cpp
#include "precompiled.hpp"
#include "jni.h"
#include "jvm.h"
#include "jfr/jfr.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/periodic/sampling/jfrThreadSampler.hpp"
#include "jfr/recorder/jfrEventSetting.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/recorder/checkpoint/jfrMetadataEvent.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
#include "jfr/recorder/repository/jfrRepository.hpp"
#include "jfr/recorder/repository/jfrChunkRotation.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
#include "jfr/recorder/stringpool/jfrStringPool.hpp"
#include "jfr/jni/jfrGetAllEventClasses.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/jni/jfrJniMethodRegistration.hpp"
#include "jfr/instrumentation/jfrEventClassTransformer.hpp"
#include "jfr/instrumentation/jfrJvmtiAgent.hpp"
#include "jfr/leakprofiler/leakProfiler.hpp"
#include "jfr/utilities/jfrJavaLog.hpp"
#include "jfr/utilities/jfrTimeConverter.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "jfr/writers/jfrJavaEventWriter.hpp"
#include "jfrfiles/jfrPeriodic.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/interfaceSupport.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
#define NO_TRANSITION(result_type, header) extern "C" { result_type JNICALL header {
#define NO_TRANSITION_END } }
NO_TRANSITION(void, jfr_register_natives(JNIEnv* env, jclass jvmclass))
JfrJniMethodRegistration register_native_methods(env);
NO_TRANSITION_END
NO_TRANSITION(jboolean, jfr_is_enabled())
return Jfr::is_enabled() ? JNI_TRUE : JNI_FALSE;
NO_TRANSITION_END
NO_TRANSITION(jboolean, jfr_is_disabled())
return Jfr::is_disabled() ? JNI_TRUE : JNI_FALSE;
NO_TRANSITION_END
NO_TRANSITION(jboolean, jfr_is_started())
return JfrRecorder::is_created() ? JNI_TRUE : JNI_FALSE;
NO_TRANSITION_END
NO_TRANSITION(jstring, jfr_get_pid(JNIEnv* env, jobject jvm))
char pid_buf[32] = { 0 };
jio_snprintf(pid_buf, sizeof(pid_buf), "%d", os::current_process_id());
jstring pid_string = env->NewStringUTF(pid_buf);
return pid_string; // exception pending if NULL
NO_TRANSITION_END
NO_TRANSITION(jlong, jfr_elapsed_frequency(JNIEnv* env, jobject jvm))
return JfrTime::frequency();
NO_TRANSITION_END
NO_TRANSITION(jlong, jfr_elapsed_counter(JNIEnv* env, jobject jvm))
return JfrTicks::now();
NO_TRANSITION_END
NO_TRANSITION(void, jfr_retransform_classes(JNIEnv* env, jobject jvm, jobjectArray classes))
JfrJvmtiAgent::retransform_classes(env, classes, JavaThread::thread_from_jni_environment(env));
NO_TRANSITION_END
NO_TRANSITION(void, jfr_set_enabled(JNIEnv* env, jobject jvm, jlong event_type_id, jboolean enabled))
JfrEventSetting::set_enabled(event_type_id, JNI_TRUE == enabled);
if (EventOldObjectSample::eventId == event_type_id) {
ThreadInVMfromNative transition(JavaThread::thread_from_jni_environment(env));
if (JNI_TRUE == enabled) {
LeakProfiler::start(JfrOptionSet::old_object_queue_size());
} else {
LeakProfiler::stop();
}
}
NO_TRANSITION_END
NO_TRANSITION(void, jfr_set_file_notification(JNIEnv* env, jobject jvm, jlong threshold))
JfrChunkRotation::set_threshold((intptr_t)threshold);
NO_TRANSITION_END
NO_TRANSITION(void, jfr_set_sample_threads(JNIEnv* env, jobject jvm, jboolean sampleThreads))
JfrOptionSet::set_sample_threads(sampleThreads);
NO_TRANSITION_END
NO_TRANSITION(void, jfr_set_stack_depth(JNIEnv* env, jobject jvm, jint depth))
JfrOptionSet::set_stackdepth((jlong)depth);
NO_TRANSITION_END
NO_TRANSITION(void, jfr_set_stacktrace_enabled(JNIEnv* env, jobject jvm, jlong event_type_id, jboolean enabled))
JfrEventSetting::set_stacktrace(event_type_id, JNI_TRUE == enabled);
NO_TRANSITION_END
NO_TRANSITION(void, jfr_set_global_buffer_count(JNIEnv* env, jobject jvm, jlong count))
JfrOptionSet::set_num_global_buffers(count);
NO_TRANSITION_END
NO_TRANSITION(void, jfr_set_global_buffer_size(JNIEnv* env, jobject jvm, jlong size))
JfrOptionSet::set_global_buffer_size(size);
NO_TRANSITION_END
NO_TRANSITION(void, jfr_set_thread_buffer_size(JNIEnv* env, jobject jvm, jlong size))
JfrOptionSet::set_thread_buffer_size(size);
NO_TRANSITION_END
NO_TRANSITION(void, jfr_set_memory_size(JNIEnv* env, jobject jvm, jlong size))
JfrOptionSet::set_memory_size(size);
NO_TRANSITION_END
NO_TRANSITION(jboolean, jfr_set_threshold(JNIEnv* env, jobject jvm, jlong event_type_id, jlong thresholdTicks))
return JfrEventSetting::set_threshold(event_type_id, thresholdTicks) ? JNI_TRUE : JNI_FALSE;
NO_TRANSITION_END
NO_TRANSITION(jboolean, jfr_allow_event_retransforms(JNIEnv* env, jobject jvm))
return JfrOptionSet::allow_event_retransforms() ? JNI_TRUE : JNI_FALSE;
NO_TRANSITION_END
NO_TRANSITION(jboolean, jfr_is_available(JNIEnv* env, jclass jvm))
return !Jfr::is_disabled() ? JNI_TRUE : JNI_FALSE;
NO_TRANSITION_END
NO_TRANSITION(jlong, jfr_get_epoch_address(JNIEnv* env, jobject jvm))
return JfrTraceIdEpoch::epoch_address();
NO_TRANSITION_END
NO_TRANSITION(jlong, jfr_get_unloaded_event_classes_count(JNIEnv* env, jobject jvm))
return JfrEventClasses::unloaded_event_classes_count();
NO_TRANSITION_END
NO_TRANSITION(jdouble, jfr_time_conv_factor(JNIEnv* env, jobject jvm))
return (jdouble)JfrTimeConverter::nano_to_counter_multiplier();
NO_TRANSITION_END
NO_TRANSITION(jboolean, jfr_set_cutoff(JNIEnv* env, jobject jvm, jlong event_type_id, jlong cutoff_ticks))
return JfrEventSetting::set_cutoff(event_type_id, cutoff_ticks) ? JNI_TRUE : JNI_FALSE;
NO_TRANSITION_END
NO_TRANSITION(jboolean, jfr_should_rotate_disk(JNIEnv* env, jobject jvm))
return JfrChunkRotation::should_rotate() ? JNI_TRUE : JNI_FALSE;
NO_TRANSITION_END
JVM_ENTRY_NO_ENV(jboolean, jfr_create_jfr(JNIEnv* env, jobject jvm, jboolean simulate_failure))
if (JfrRecorder::is_created()) {
return JNI_TRUE;
}
if (!JfrRecorder::create(simulate_failure == JNI_TRUE)) {
JfrJavaSupport::throw_illegal_state_exception("Unable to start Jfr", thread);
return JNI_FALSE;
}
return JNI_TRUE;
JVM_END
JVM_ENTRY_NO_ENV(jboolean, jfr_destroy_jfr(JNIEnv* env, jobject jvm))
JfrRecorder::destroy();
return JNI_TRUE;
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_begin_recording(JNIEnv* env, jobject jvm))
if (JfrRecorder::is_recording()) {
return;
}
JfrRecorder::start_recording();
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_end_recording(JNIEnv* env, jobject jvm))
if (!JfrRecorder::is_recording()) {
return;
}
JfrRecorder::stop_recording();
JVM_END
JVM_ENTRY_NO_ENV(jboolean, jfr_emit_event(JNIEnv* env, jobject jvm, jlong eventTypeId, jlong timeStamp, jlong when))
JfrPeriodicEventSet::requestEvent((JfrEventId)eventTypeId);
return thread->has_pending_exception() ? JNI_FALSE : JNI_TRUE;
JVM_END
JVM_ENTRY_NO_ENV(jobject, jfr_get_all_event_classes(JNIEnv* env, jobject jvm))
return JfrEventClasses::get_all_event_classes(thread);
JVM_END
JVM_ENTRY_NO_ENV(jlong, jfr_class_id(JNIEnv* env, jclass jvm, jclass jc))
return JfrTraceId::use(jc);
JVM_END
JVM_ENTRY_NO_ENV(jlong, jfr_stacktrace_id(JNIEnv* env, jobject jvm, jint skip))
return JfrStackTraceRepository::record(thread, skip);
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_log(JNIEnv* env, jobject jvm, jint tag_set, jint level, jstring message))
JfrJavaLog::log(tag_set, level, message, thread);
JVM_END
JVM_ENTRY_NO_ENV(jboolean, jfr_should_log(JNIEnv* env, jobject jvm, jint level))
return JfrJavaLog::should_log(level, thread) ? JNI_TRUE : JNI_FALSE;
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_subscribe_log_level(JNIEnv* env, jobject jvm, jobject log_tag, jint id))
JfrJavaLog::subscribe_log_level(log_tag, id, thread);
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_set_output(JNIEnv* env, jobject jvm, jstring path))
JfrRepository::set_chunk_path(path, thread);
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_set_method_sampling_interval(JNIEnv* env, jobject jvm, jlong type, jlong intervalMillis))
if (intervalMillis < 0) {
intervalMillis = 0;
}
JfrEventId typed_event_id = (JfrEventId)type;
assert(EventExecutionSample::eventId == typed_event_id || EventNativeMethodSample::eventId == typed_event_id, "invariant");
if (intervalMillis > 0) {
JfrEventSetting::set_enabled(typed_event_id, true); // ensure sampling event is enabled
}
if (EventExecutionSample::eventId == type) {
JfrThreadSampling::set_java_sample_interval(intervalMillis);
} else {
JfrThreadSampling::set_native_sample_interval(intervalMillis);
}
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_store_metadata_descriptor(JNIEnv* env, jobject jvm, jbyteArray descriptor))
JfrMetadataEvent::update(descriptor);
JVM_END
JVM_ENTRY_NO_ENV(jlong, jfr_id_for_thread(JNIEnv* env, jobject jvm, jobject t))
return JfrJavaSupport::jfr_thread_id(t);
JVM_END
JVM_ENTRY_NO_ENV(jobject, jfr_get_event_writer(JNIEnv* env, jclass cls))
return JfrJavaEventWriter::event_writer(thread);
JVM_END
JVM_ENTRY_NO_ENV(jobject, jfr_new_event_writer(JNIEnv* env, jclass cls))
return JfrJavaEventWriter::new_event_writer(thread);
JVM_END
JVM_ENTRY_NO_ENV(jboolean, jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size))
return JfrJavaEventWriter::flush(writer, used_size, requested_size, thread);
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location))
return JfrRepository::set_path(location, thread);
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_uncaught_exception(JNIEnv* env, jobject jvm, jobject t, jthrowable throwable))
JfrJavaSupport::uncaught_exception(throwable, thread);
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg))
JfrJavaSupport::abort(errorMsg, thread);
JVM_END
JVM_ENTRY_NO_ENV(jlong, jfr_type_id(JNIEnv* env, jobject jvm, jclass jc))
return JfrTraceId::get(jc);
JVM_END
JVM_ENTRY_NO_ENV(jboolean, jfr_add_string_constant(JNIEnv* env, jclass jvm, jboolean epoch, jlong id, jstring string))
return JfrStringPool::add(epoch == JNI_TRUE, id, string, thread) ? JNI_TRUE : JNI_FALSE;
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_set_force_instrumentation(JNIEnv* env, jobject jvm, jboolean force_instrumentation))
JfrEventClassTransformer::set_force_instrumentation(force_instrumentation == JNI_TRUE);
JVM_END
JVM_ENTRY_NO_ENV(void, jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean emit_all))
LeakProfiler::emit_events(cutoff_ticks, emit_all == JNI_TRUE);
JVM_END
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrJniMethod.hpp
#ifndef SHARE_VM_JFR_JNI_JFRJNIMETHOD_HPP
#define SHARE_VM_JFR_JNI_JFRJNIMETHOD_HPP
#include "jni.h"
#ifdef __cplusplus
extern "C" {
#endif
jboolean JNICALL jfr_is_enabled();
jboolean JNICALL jfr_is_disabled();
jboolean JNICALL jfr_is_started();
jlong JNICALL jfr_elapsed_counter(JNIEnv* env, jobject jvm);
jboolean JNICALL jfr_create_jfr(JNIEnv* env, jobject jvm, jboolean simulate_failure);
jboolean JNICALL jfr_destroy_jfr(JNIEnv* env, jobject jvm);
void JNICALL jfr_begin_recording(JNIEnv* env, jobject jvm);
void JNICALL jfr_end_recording(JNIEnv* env, jobject jvm);
jboolean JNICALL jfr_emit_event(JNIEnv* env, jobject jvm, jlong eventTypeId, jlong timeStamp, jlong when);
jobject JNICALL jfr_get_all_event_classes(JNIEnv* env, jobject jvm);
jlong JNICALL jfr_class_id(JNIEnv* env, jclass jvm, jclass jc);
jstring JNICALL jfr_get_pid(JNIEnv* env, jobject jvm);
jlong JNICALL jfr_stacktrace_id(JNIEnv* env, jobject jvm, jint skip);
jlong JNICALL jfr_elapsed_frequency(JNIEnv* env, jobject jvm);
void JNICALL jfr_subscribe_log_level(JNIEnv* env, jobject jvm, jobject log_tag, jint id);
void JNICALL jfr_log(JNIEnv* env, jobject jvm, jint tag_set, jint level, jstring message);
jboolean JNICALL jfr_should_log(JNIEnv* env, jobject jvm, jint level);
void JNICALL jfr_retransform_classes(JNIEnv* env, jobject jvm, jobjectArray classes);
void JNICALL jfr_set_enabled(JNIEnv* env, jobject jvm, jlong event_type_id, jboolean enabled);
void JNICALL jfr_set_file_notification(JNIEnv* env, jobject jvm, jlong delta);
void JNICALL jfr_set_global_buffer_count(JNIEnv* env, jobject jvm, jlong count);
void JNICALL jfr_set_global_buffer_size(JNIEnv* env, jobject jvm, jlong size);
void JNICALL jfr_set_method_sampling_interval(JNIEnv* env, jobject jvm, jlong type, jlong intervalMillis);
void JNICALL jfr_set_output(JNIEnv* env, jobject jvm, jstring path);
void JNICALL jfr_set_sample_threads(JNIEnv* env, jobject jvm, jboolean sampleThreads);
void JNICALL jfr_set_stack_depth(JNIEnv* env, jobject jvm, jint depth);
void JNICALL jfr_set_stacktrace_enabled(JNIEnv* env, jobject jvm, jlong event_type_id, jboolean enabled);
void JNICALL jfr_set_thread_buffer_size(JNIEnv* env, jobject jvm, jlong size);
void JNICALL jfr_set_memory_size(JNIEnv* env, jobject jvm, jlong size);
jboolean JNICALL jfr_set_threshold(JNIEnv* env, jobject jvm, jlong event_type_id, jlong thresholdTicks);
void JNICALL jfr_store_metadata_descriptor(JNIEnv* env, jobject jvm, jbyteArray descriptor);
jlong JNICALL jfr_id_for_thread(JNIEnv* env, jobject jvm, jobject t);
jboolean JNICALL jfr_allow_event_retransforms(JNIEnv* env, jobject jvm);
jboolean JNICALL jfr_is_available(JNIEnv* env, jclass jvm);
jdouble JNICALL jfr_time_conv_factor(JNIEnv* env, jobject jvm);
jlong JNICALL jfr_type_id(JNIEnv* env, jobject jvm, jclass jc);
void JNICALL jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location);
jobject JNICALL jfr_get_event_writer(JNIEnv* env, jclass cls);
jobject JNICALL jfr_new_event_writer(JNIEnv* env, jclass cls);
jboolean JNICALL jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size);
void JNICALL jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg);
jlong JNICALL jfr_get_epoch_address(JNIEnv* env, jobject jvm);
jboolean JNICALL jfr_add_string_constant(JNIEnv* env, jclass jvm, jboolean epoch, jlong id, jstring string);
void JNICALL jfr_uncaught_exception(JNIEnv* env, jobject jvm, jobject thread, jthrowable throwable);
void JNICALL jfr_set_force_instrumentation(JNIEnv* env, jobject jvm, jboolean force);
jlong JNICALL jfr_get_unloaded_event_classes_count(JNIEnv* env, jobject jvm);
jboolean JNICALL jfr_set_cutoff(JNIEnv* env, jobject jvm, jlong event_type_id, jlong cutoff_ticks);
void JNICALL jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean);
jboolean JNICALL jfr_should_rotate_disk(JNIEnv* env, jobject jvm);
#ifdef __cplusplus
}
#endif
#endif // SHARE_VM_JFR_JNI_JFRJNIMETHOD_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrJniMethodRegistration.cpp
#include "precompiled.hpp"
#include "jfr/jni/jfrJniMethod.hpp"
#include "jfr/jni/jfrJniMethodRegistration.hpp"
#include "jfr/utilities/jfrJavaLog.hpp"
#include "runtime/interfaceSupport.hpp"
#include "runtime/thread.hpp"
#include "utilities/exceptions.hpp"
JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) {
assert(env != NULL, "invariant");
jclass jfr_clz = env->FindClass("jdk/jfr/internal/JVM");
if (jfr_clz != NULL) {
JNINativeMethod method[] = {
(char*)"beginRecording", (char*)"()V", (void*)jfr_begin_recording,
(char*)"endRecording", (char*)"()V", (void*)jfr_end_recording,
(char*)"counterTime", (char*)"()J", (void*)jfr_elapsed_counter,
(char*)"createJFR", (char*)"(Z)Z", (void*)jfr_create_jfr,
(char*)"destroyJFR", (char*)"()Z", (void*)jfr_destroy_jfr,
(char*)"emitEvent", (char*)"(JJJ)Z", (void*)jfr_emit_event,
(char*)"getAllEventClasses", (char*)"()Ljava/util/List;", (void*)jfr_get_all_event_classes,
(char*)"getClassIdNonIntrinsic", (char*)"(Ljava/lang/Class;)J", (void*)jfr_class_id,
(char*)"getPid", (char*)"()Ljava/lang/String;", (void*)jfr_get_pid,
(char*)"getStackTraceId", (char*)"(I)J", (void*)jfr_stacktrace_id,
(char*)"getThreadId", (char*)"(Ljava/lang/Thread;)J", (void*)jfr_id_for_thread,
(char*)"getTicksFrequency", (char*)"()J", (void*)jfr_elapsed_frequency,
(char*)"subscribeLogLevel", (char*)"(Ljdk/jfr/internal/LogTag;I)V", (void*)jfr_subscribe_log_level,
(char*)"log", (char*)"(IILjava/lang/String;)V", (void*)jfr_log,
(char*)"shouldLog", (char*)"(I)Z", (void*)jfr_should_log,
(char*)"retransformClasses", (char*)"([Ljava/lang/Class;)V", (void*)jfr_retransform_classes,
(char*)"setEnabled", (char*)"(JZ)V", (void*)jfr_set_enabled,
(char*)"setFileNotification", (char*)"(J)V", (void*)jfr_set_file_notification,
(char*)"setGlobalBufferCount", (char*)"(J)V", (void*)jfr_set_global_buffer_count,
(char*)"setGlobalBufferSize", (char*)"(J)V", (void*)jfr_set_global_buffer_size,
(char*)"setMethodSamplingInterval", (char*)"(JJ)V", (void*)jfr_set_method_sampling_interval,
(char*)"setOutput", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_output,
(char*)"setSampleThreads", (char*)"(Z)V", (void*)jfr_set_sample_threads,
(char*)"setStackDepth", (char*)"(I)V", (void*)jfr_set_stack_depth,
(char*)"setStackTraceEnabled", (char*)"(JZ)V", (void*)jfr_set_stacktrace_enabled,
(char*)"setThreadBufferSize", (char*)"(J)V", (void*)jfr_set_thread_buffer_size,
(char*)"setMemorySize", (char*)"(J)V", (void*)jfr_set_memory_size,
(char*)"setThreshold", (char*)"(JJ)Z", (void*)jfr_set_threshold,
(char*)"storeMetadataDescriptor", (char*)"([B)V", (void*)jfr_store_metadata_descriptor,
(char*)"getAllowedToDoEventRetransforms", (char*)"()Z", (void*)jfr_allow_event_retransforms,
(char*)"isAvailable", (char*)"()Z", (void*)jfr_is_available,
(char*)"getTimeConversionFactor", (char*)"()D", (void*)jfr_time_conv_factor,
(char*)"getTypeId", (char*)"(Ljava/lang/Class;)J", (void*)jfr_type_id,
(char*)"getEventWriter", (char*)"()Ljava/lang/Object;", (void*)jfr_get_event_writer,
(char*)"newEventWriter", (char*)"()Ljdk/jfr/internal/EventWriter;", (void*)jfr_new_event_writer,
(char*)"flush", (char*)"(Ljdk/jfr/internal/EventWriter;II)Z", (void*)jfr_event_writer_flush,
(char*)"setRepositoryLocation", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_repository_location,
(char*)"abort", (char*)"(Ljava/lang/String;)V", (void*)jfr_abort,
(char*)"getEpochAddress", (char*)"()J",(void*)jfr_get_epoch_address,
(char*)"addStringConstant", (char*)"(ZJLjava/lang/String;)Z", (void*)jfr_add_string_constant,
(char*)"uncaughtException", (char*)"(Ljava/lang/Thread;Ljava/lang/Throwable;)V", (void*)jfr_uncaught_exception,
(char*)"setForceInstrumentation", (char*)"(Z)V", (void*)jfr_set_force_instrumentation,
(char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count,
(char*)"setCutoff", (char*)"(JJ)Z", (void*)jfr_set_cutoff,
(char*)"emitOldObjectSamples", (char*)"(JZ)V", (void*)jfr_emit_old_object_samples,
(char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk
};
const size_t method_array_length = sizeof(method) / sizeof(JNINativeMethod);
if (env->RegisterNatives(jfr_clz, method, (jint)method_array_length) != JNI_OK) {
JavaThread* jt = JavaThread::thread_from_jni_environment(env);
assert(jt != NULL, "invariant");
assert(jt->thread_state() == _thread_in_native, "invariant");
ThreadInVMfromNative transition(jt);
if (true) tty->print_cr("RegisterNatives for JVM class failed!");
}
env->DeleteLocalRef(jfr_clz);
}
}
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrJniMethodRegistration.hpp
#ifndef SHARE_VM_JFR_JNI_JFRJNIMETHODREGISTRATION_HPP
#define SHARE_VM_JFR_JNI_JFRJNIMETHODREGISTRATION_HPP
#include "jni.h"
#include "memory/allocation.hpp"
class JfrJniMethodRegistration : public StackObj {
public:
JfrJniMethodRegistration(JNIEnv* env);
};
#endif // SHARE_VM_JFR_JNI_JFRJNIMETHODREGISTRATION_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrUpcalls.cpp
#include "precompiled.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/jni/jfrUpcalls.hpp"
#include "jfr/support/jfrEventClass.hpp"
#include "memory/oopFactory.hpp"
#include "oops/oop.inline.hpp"
#include "oops/typeArrayKlass.hpp"
#include "oops/typeArrayOop.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/os.hpp"
#include "runtime/thread.inline.hpp"
#include "utilities/exceptions.hpp"
static Symbol* jvm_upcalls_class_sym = NULL;
static Symbol* on_retransform_method_sym = NULL;
static Symbol* on_retransform_signature_sym = NULL;
static Symbol* bytes_for_eager_instrumentation_sym = NULL;
static Symbol* bytes_for_eager_instrumentation_sig_sym = NULL;
static bool initialize(TRAPS) {
static bool initialized = false;
if (!initialized) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
jvm_upcalls_class_sym = SymbolTable::new_permanent_symbol("jdk/jfr/internal/JVMUpcalls", CHECK_false);
on_retransform_method_sym = SymbolTable::new_permanent_symbol("onRetransform", CHECK_false);
on_retransform_signature_sym = SymbolTable::new_permanent_symbol("(JZLjava/lang/Class;[B)[B", CHECK_false);
bytes_for_eager_instrumentation_sym = SymbolTable::new_permanent_symbol("bytesForEagerInstrumentation", CHECK_false);
bytes_for_eager_instrumentation_sig_sym = SymbolTable::new_permanent_symbol("(JZLjava/lang/Class;[B)[B", THREAD);
initialized = bytes_for_eager_instrumentation_sig_sym != NULL;
}
return initialized;
}
static const typeArrayOop invoke(jlong trace_id,
jboolean force_instrumentation,
jclass class_being_redefined,
jint class_data_len,
const unsigned char* class_data,
Symbol* method_sym,
Symbol* signature_sym,
jint& new_bytes_length,
TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
const Klass* klass = SystemDictionary::resolve_or_fail(jvm_upcalls_class_sym, true, CHECK_NULL);
assert(klass != NULL, "invariant");
typeArrayOop old_byte_array = oopFactory::new_byteArray(class_data_len, CHECK_NULL);
memcpy(old_byte_array->byte_at_addr(0), class_data, class_data_len);
JavaValue result(T_OBJECT);
JfrJavaArguments args(&result, klass, method_sym, signature_sym);
args.push_long(trace_id);
args.push_int(force_instrumentation);
args.push_jobject(class_being_redefined);
args.push_oop(old_byte_array);
JfrJavaSupport::call_static(&args, THREAD);
if (HAS_PENDING_EXCEPTION) {
if (true) tty->print_cr("JfrUpcall failed");
return NULL;
}
const oop res = (oop)result.get_jobject();
assert(res != NULL, "invariant");
assert(res->is_typeArray(), "invariant");
assert(TypeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "invariant");
const typeArrayOop new_byte_array = typeArrayOop(res);
new_bytes_length = (jint)new_byte_array->length();
return new_byte_array;
}
static const size_t ERROR_MSG_BUFFER_SIZE = 256;
static void log_error_and_throw_oom(jint new_bytes_length, TRAPS) {
char error_buffer[ERROR_MSG_BUFFER_SIZE];
jio_snprintf(error_buffer, ERROR_MSG_BUFFER_SIZE,
"Thread local allocation (native) for " SIZE_FORMAT " bytes failed in JfrUpcalls", (size_t)new_bytes_length);
if (true) tty->print_cr("%s", error_buffer);
JfrJavaSupport::throw_out_of_memory_error(error_buffer, CHECK);
}
void JfrUpcalls::on_retransform(jlong trace_id,
jclass class_being_redefined,
jint class_data_len,
const unsigned char* class_data,
jint* new_class_data_len,
unsigned char** new_class_data,
TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
assert(class_being_redefined != NULL, "invariant");
assert(class_data != NULL, "invariant");
assert(new_class_data_len != NULL, "invariant");
assert(new_class_data != NULL, "invariant");
if (!JdkJfrEvent::is_visible(class_being_redefined)) {
return;
}
jint new_bytes_length = 0;
initialize(THREAD);
const typeArrayOop new_byte_array = invoke(trace_id,
false,
class_being_redefined,
class_data_len,
class_data,
on_retransform_method_sym,
on_retransform_signature_sym,
new_bytes_length,
CHECK);
assert(new_byte_array != NULL, "invariant");
assert(new_bytes_length > 0, "invariant");
unsigned char* const new_bytes = (unsigned char* const)os::malloc(new_bytes_length, mtInternal);
if (new_bytes == NULL) {
log_error_and_throw_oom(new_bytes_length, THREAD); // unwinds
}
assert(new_bytes != NULL, "invariant");
memcpy(new_bytes, new_byte_array->byte_at_addr(0), (size_t)new_bytes_length);
}
void JfrUpcalls::new_bytes_eager_instrumentation(jlong trace_id,
jboolean force_instrumentation,
jclass super,
jint class_data_len,
const unsigned char* class_data,
jint* new_class_data_len,
unsigned char** new_class_data,
TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
assert(super != NULL, "invariant");
assert(class_data != NULL, "invariant");
assert(new_class_data_len != NULL, "invariant");
assert(new_class_data != NULL, "invariant");
jint new_bytes_length = 0;
initialize(THREAD);
const typeArrayOop new_byte_array = invoke(trace_id,
force_instrumentation,
super,
class_data_len,
class_data,
bytes_for_eager_instrumentation_sym,
bytes_for_eager_instrumentation_sig_sym,
new_bytes_length,
CHECK);
assert(new_byte_array != NULL, "invariant");
assert(new_bytes_length > 0, "invariant");
unsigned char* const new_bytes = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, unsigned char, new_bytes_length);
if (new_bytes == NULL) {
log_error_and_throw_oom(new_bytes_length, THREAD); // this unwinds
}
assert(new_bytes != NULL, "invariant");
memcpy(new_bytes, new_byte_array->byte_at_addr(0), (size_t)new_bytes_length);
}
instanceKlassHandle JfrUpcalls::load_event_handler_proxy_class(TRAPS) {
JavaValue result(T_OBJECT);
JfrJavaArguments call_args(&result, "jdk/jfr/internal/JVMUpcalls",
"getEventHandlerProxyClass", "()Ljava/lang/Class;", CHECK_NULL);
JfrJavaSupport::call_static(&call_args, CHECK_NULL);
assert(result.get_type() == T_OBJECT, "invariant");
instanceHandle h_java_proxy(THREAD, (instanceOop)result.get_jobject());
assert(h_java_proxy.not_null(), "invariant");
return java_lang_Class::as_Klass(h_java_proxy());
}
C:\hotspot-69087d08d473\src\share\vm/jfr/jni/jfrUpcalls.hpp
#ifndef SHARE_VM_JFR_JNI_JFRUPCALLS_HPP
#define SHARE_VM_JFR_JNI_JFRUPCALLS_HPP
#include "jni.h"
#include "jfr/utilities/jfrAllocation.hpp"
#include "utilities/exceptions.hpp"
class JavaThread;
class JfrUpcalls : AllStatic {
public:
static void new_bytes_eager_instrumentation(jlong trace_id,
jboolean force_instrumentation,
jclass super,
jint class_data_len,
const unsigned char* class_data,
jint* new_class_data_len,
unsigned char** new_class_data,
TRAPS);
static void on_retransform(jlong trace_id,
jclass class_being_redefined,
jint class_data_len,
const unsigned char* class_data,
jint* new_class_data_len,
unsigned char** new_class_data,
TRAPS);
static instanceKlassHandle load_event_handler_proxy_class(TRAPS);
};
#endif // SHARE_VM_JFR_JNI_JFRUPCALLS_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/bfsClosure.cpp
#include "precompiled.hpp"
#include "jfr/leakprofiler/chains/bitset.hpp"
#include "jfr/leakprofiler/chains/bfsClosure.hpp"
#include "jfr/leakprofiler/chains/dfsClosure.hpp"
#include "jfr/leakprofiler/chains/edge.hpp"
#include "jfr/leakprofiler/chains/edgeStore.hpp"
#include "jfr/leakprofiler/chains/edgeQueue.hpp"
#include "jfr/leakprofiler/utilities/granularTimer.hpp"
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
#include "memory/iterator.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/oop.inline.hpp"
#include "utilities/align.hpp"
BFSClosure::BFSClosure(EdgeQueue* edge_queue, EdgeStore* edge_store, BitSet* mark_bits) :
_edge_queue(edge_queue),
_edge_store(edge_store),
_mark_bits(mark_bits),
_current_parent(NULL),
_current_frontier_level(0),
_next_frontier_idx(0),
_prev_frontier_idx(0),
_dfs_fallback_idx(0),
_use_dfs(false) {
}
static void log_frontier_level_summary(size_t level,
size_t high_idx,
size_t low_idx,
size_t edge_size) {
const size_t nof_edges_in_frontier = high_idx - low_idx;
if (LogJFR && Verbose) tty->print_cr(
"BFS front: " SIZE_FORMAT " edges: " SIZE_FORMAT " size: " SIZE_FORMAT " [KB]",
level,
nof_edges_in_frontier,
(nof_edges_in_frontier * edge_size) / K
);
}
void BFSClosure::log_completed_frontier() const {
log_frontier_level_summary(_current_frontier_level,
_next_frontier_idx,
_prev_frontier_idx,
_edge_queue->sizeof_edge());
}
void BFSClosure::log_dfs_fallback() const {
const size_t edge_size = _edge_queue->sizeof_edge();
log_frontier_level_summary(_current_frontier_level,
_next_frontier_idx,
_prev_frontier_idx,
edge_size);
log_frontier_level_summary(_current_frontier_level + 1,
_edge_queue->bottom(),
_next_frontier_idx,
edge_size);
if (LogJFR && Verbose) tty->print_cr(
"BFS front: " SIZE_FORMAT " filled edge queue at edge: " SIZE_FORMAT,
_current_frontier_level,
_dfs_fallback_idx
);
const size_t nof_dfs_completed_edges = _edge_queue->bottom() - _dfs_fallback_idx;
if (LogJFR && Verbose) tty->print_cr(
"DFS to complete " SIZE_FORMAT " edges size: " SIZE_FORMAT " [KB]",
nof_dfs_completed_edges,
(nof_dfs_completed_edges * edge_size) / K
);
}
void BFSClosure::process() {
process_root_set();
process_queue();
}
void BFSClosure::process_root_set() {
for (size_t idx = _edge_queue->bottom(); idx < _edge_queue->top(); ++idx) {
const Edge* edge = _edge_queue->element_at(idx);
assert(edge->parent() == NULL, "invariant");
process(edge->reference(), edge->pointee());
}
}
void BFSClosure::process(const oop* reference, const oop pointee) {
closure_impl(reference, pointee);
}
void BFSClosure::closure_impl(const oop* reference, const oop pointee) {
assert(reference != NULL, "invariant");
assert(UnifiedOop::dereference(reference) == pointee, "invariant");
if (GranularTimer::is_finished()) {
return;
}
if (_use_dfs) {
assert(_current_parent != NULL, "invariant");
DFSClosure::find_leaks_from_edge(_edge_store, _mark_bits, _current_parent);
return;
}
if (!_mark_bits->is_marked(pointee)) {
_mark_bits->mark_obj(pointee);
if (NULL == pointee->mark()) {
add_chain(reference, pointee);
}
if (_current_parent != NULL) {
_edge_queue->add(_current_parent, reference);
}
if (_edge_queue->is_full()) {
dfs_fallback();
}
}
}
void BFSClosure::add_chain(const oop* reference, const oop pointee) {
assert(pointee != NULL, "invariant");
assert(NULL == pointee->mark(), "invariant");
Edge leak_edge(_current_parent, reference);
_edge_store->put_chain(&leak_edge, _current_parent == NULL ? 1 : _current_frontier_level + 2);
}
void BFSClosure::dfs_fallback() {
assert(_edge_queue->is_full(), "invariant");
_use_dfs = true;
_dfs_fallback_idx = _edge_queue->bottom();
while (!_edge_queue->is_empty()) {
const Edge* edge = _edge_queue->remove();
if (edge->pointee() != NULL) {
DFSClosure::find_leaks_from_edge(_edge_store, _mark_bits, edge);
}
}
}
void BFSClosure::process_queue() {
assert(_current_frontier_level == 0, "invariant");
assert(_next_frontier_idx == 0, "invariant");
assert(_prev_frontier_idx == 0, "invariant");
_next_frontier_idx = _edge_queue->top();
while (!is_complete()) {
iterate(_edge_queue->remove()); // edge_queue.remove() increments bottom
}
}
void BFSClosure::step_frontier() const {
log_completed_frontier();
++_current_frontier_level;
_prev_frontier_idx = _next_frontier_idx;
_next_frontier_idx = _edge_queue->top();
}
bool BFSClosure::is_complete() const {
if (_edge_queue->bottom() < _next_frontier_idx) {
return false;
}
if (_edge_queue->bottom() > _next_frontier_idx) {
assert(_dfs_fallback_idx >= _prev_frontier_idx, "invariant");
assert(_dfs_fallback_idx < _next_frontier_idx, "invariant");
log_dfs_fallback();
return true;
}
assert(_edge_queue->bottom() == _next_frontier_idx, "invariant");
if (_edge_queue->is_empty()) {
return true;
}
step_frontier();
return false;
}
void BFSClosure::iterate(const Edge* parent) {
assert(parent != NULL, "invariant");
const oop pointee = parent->pointee();
assert(pointee != NULL, "invariant");
_current_parent = parent;
pointee->oop_iterate(this);
}
void BFSClosure::do_oop(oop* ref) {
assert(ref != NULL, "invariant");
assert(is_aligned(ref, HeapWordSize), "invariant");
const oop pointee = *ref;
if (pointee != NULL) {
closure_impl(ref, pointee);
}
}
void BFSClosure::do_oop(narrowOop* ref) {
assert(ref != NULL, "invariant");
assert(is_aligned(ref, sizeof(narrowOop)), "invariant");
const oop pointee = oopDesc::load_decode_heap_oop(ref);
if (pointee != NULL) {
closure_impl(UnifiedOop::encode(ref), pointee);
}
}
void BFSClosure::do_root(const oop* ref) {
assert(ref != NULL, "invariant");
if (!_edge_queue->is_full()) {
_edge_queue->add(NULL, ref);
}
}
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/bfsClosure.hpp
#ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_BFSCLOSURE_HPP
#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_BFSCLOSURE_HPP
#include "memory/iterator.hpp"
class BitSet;
class Edge;
class EdgeStore;
class EdgeQueue;
class BFSClosure : public ExtendedOopClosure { // XXX BasicOopIterateClosure
private:
EdgeQueue* _edge_queue;
EdgeStore* _edge_store;
BitSet* _mark_bits;
const Edge* _current_parent;
mutable size_t _current_frontier_level;
mutable size_t _next_frontier_idx;
mutable size_t _prev_frontier_idx;
size_t _dfs_fallback_idx;
bool _use_dfs;
void log_completed_frontier() const;
void log_dfs_fallback() const;
bool is_complete() const;
void step_frontier() const;
void closure_impl(const oop* reference, const oop pointee);
void add_chain(const oop* reference, const oop pointee);
void dfs_fallback();
void iterate(const Edge* parent);
void process(const oop* reference, const oop pointee);
void process_root_set();
void process_queue();
public:
BFSClosure(EdgeQueue* edge_queue, EdgeStore* edge_store, BitSet* mark_bits);
void process();
void do_root(const oop* ref);
virtual void do_oop(oop* ref);
virtual void do_oop(narrowOop* ref);
};
#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_BFSCLOSURE_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/bitset.cpp
#include "precompiled.hpp"
#include "jfr/leakprofiler/chains/bitset.hpp"
#include "jfr/recorder/storage/jfrVirtualMemory.hpp"
#include "memory/memRegion.hpp"
BitSet::BitSet(const MemRegion& covered_region) :
_vmm(NULL),
_region_start(covered_region.start()),
_region_size(covered_region.word_size()) {
}
BitSet::~BitSet() {
delete _vmm;
}
bool BitSet::initialize() {
assert(_vmm == NULL, "invariant");
_vmm = new JfrVirtualMemory();
if (_vmm == NULL) {
return false;
}
const BitMap::idx_t bits = _region_size >> LogMinObjAlignment;
const size_t words = bits / BitsPerWord;
const size_t raw_bytes = words * sizeof(BitMap::idx_t);
BitMap::bm_word_t* map = (BitMap::bm_word_t*)_vmm->initialize(raw_bytes, raw_bytes);
if (map == NULL) {
return false;
}
_bits = BitMap(map, bits);
return true;
}
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/bitset.hpp
#ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_BITSET_HPP
#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_BITSET_HPP
#include "memory/allocation.hpp"
#include "oops/oopsHierarchy.hpp"
#include "utilities/bitMap.inline.hpp"
class JfrVirtualMemory;
class MemRegion;
class BitSet : public CHeapObj<mtTracing> {
private:
JfrVirtualMemory* _vmm;
const HeapWord* const _region_start;
BitMap _bits;
const size_t _region_size;
public:
BitSet(const MemRegion& covered_region);
~BitSet();
bool initialize();
BitMap::idx_t mark_obj(const HeapWord* addr) {
const BitMap::idx_t bit = addr_to_bit(addr);
_bits.set_bit(bit);
return bit;
}
BitMap::idx_t mark_obj(oop obj) {
return mark_obj((HeapWord*)obj);
}
bool is_marked(const HeapWord* addr) const {
return is_marked(addr_to_bit(addr));
}
bool is_marked(oop obj) const {
return is_marked((HeapWord*)obj);
}
BitMap::idx_t size() const {
return _bits.size();
}
BitMap::idx_t addr_to_bit(const HeapWord* addr) const {
return pointer_delta(addr, _region_start) >> LogMinObjAlignment;
}
bool is_marked(const BitMap::idx_t bit) const {
return _bits.at(bit);
}
};
#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_BITSET_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/dfsClosure.cpp
#include "precompiled.hpp"
#include "jfr/leakprofiler/chains/bitset.hpp"
#include "jfr/leakprofiler/chains/dfsClosure.hpp"
#include "jfr/leakprofiler/chains/edge.hpp"
#include "jfr/leakprofiler/chains/edgeStore.hpp"
#include "jfr/leakprofiler/chains/rootSetClosure.hpp"
#include "jfr/leakprofiler/utilities/granularTimer.hpp"
#include "jfr/leakprofiler/utilities/rootType.hpp"
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
#include "memory/iterator.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/oop.inline.hpp"
#include "utilities/align.hpp"
static const size_t max_dfs_depth = 5000;
EdgeStore* DFSClosure::_edge_store = NULL;
BitSet* DFSClosure::_mark_bits = NULL;
const Edge* DFSClosure::_start_edge = NULL;
size_t DFSClosure::_max_depth = max_dfs_depth;
bool DFSClosure::_ignore_root_set = false;
DFSClosure::DFSClosure() :
_parent(NULL),
_reference(NULL),
_depth(0) {
}
DFSClosure::DFSClosure(DFSClosure* parent, size_t depth) :
_parent(parent),
_reference(NULL),
_depth(depth) {
}
void DFSClosure::find_leaks_from_edge(EdgeStore* edge_store,
BitSet* mark_bits,
const Edge* start_edge) {
assert(edge_store != NULL, "invariant");
assert(mark_bits != NULL," invariant");
assert(start_edge != NULL, "invariant");
_edge_store = edge_store;
_mark_bits = mark_bits;
_start_edge = start_edge;
_ignore_root_set = false;
assert(_max_depth == max_dfs_depth, "invariant");
DFSClosure dfs;
start_edge->pointee()->oop_iterate(&dfs);
}
void DFSClosure::find_leaks_from_root_set(EdgeStore* edge_store,
BitSet* mark_bits) {
assert(edge_store != NULL, "invariant");
assert(mark_bits != NULL, "invariant");
_edge_store = edge_store;
_mark_bits = mark_bits;
_start_edge = NULL;
_max_depth = 1;
_ignore_root_set = false;
DFSClosure dfs;
RootSetClosure<DFSClosure> rs(&dfs);
rs.process();
_max_depth = max_dfs_depth;
_ignore_root_set = true;
assert(_start_edge == NULL, "invariant");
rs.process();
}
void DFSClosure::closure_impl(const oop* reference, const oop pointee) {
assert(pointee != NULL, "invariant");
assert(reference != NULL, "invariant");
if (GranularTimer::is_finished()) {
return;
}
if (_depth == 0 && _ignore_root_set) {
assert(_mark_bits->is_marked(pointee), "invariant");
} else {
if (_mark_bits->is_marked(pointee)) {
return;
}
}
_reference = reference;
_mark_bits->mark_obj(pointee);
assert(_mark_bits->is_marked(pointee), "invariant");
if (NULL == pointee->mark()) {
add_chain();
}
assert(_max_depth >= 1, "invariant");
if (_depth < _max_depth - 1) {
DFSClosure next_level(this, _depth + 1);
pointee->oop_iterate(&next_level);
}
}
void DFSClosure::add_chain() {
const size_t array_length = _depth + 2;
ResourceMark rm;
Edge* const chain = NEW_RESOURCE_ARRAY(Edge, array_length);
size_t idx = 0;
const DFSClosure* c = this;
while (c != NULL) {
const size_t next = idx + 1;
chain[idx++] = Edge(&chain[next], c->reference());
c = c->parent();
}
assert(_depth + 1 == idx, "invariant");
assert(array_length == idx + 1, "invariant");
if (_start_edge != NULL) {
chain[idx++] = *_start_edge;
} else {
chain[idx - 1] = Edge(NULL, chain[idx - 1].reference());
}
_edge_store->put_chain(chain, idx + (_start_edge != NULL ? _start_edge->distance_to_root() : 0));
}
void DFSClosure::do_oop(oop* ref) {
assert(ref != NULL, "invariant");
assert(is_aligned(ref, HeapWordSize), "invariant");
const oop pointee = *ref;
if (pointee != NULL) {
closure_impl(ref, pointee);
}
}
void DFSClosure::do_oop(narrowOop* ref) {
assert(ref != NULL, "invariant");
assert(is_aligned(ref, sizeof(narrowOop)), "invariant");
const oop pointee = oopDesc::load_decode_heap_oop(ref);
if (pointee != NULL) {
closure_impl(UnifiedOop::encode(ref), pointee);
}
}
void DFSClosure::do_root(const oop* ref) {
assert(ref != NULL, "invariant");
const oop pointee = UnifiedOop::dereference(ref);
assert(pointee != NULL, "invariant");
closure_impl(ref, pointee);
}
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/dfsClosure.hpp
#ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_DFSCLOSURE_HPP
#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_DFSCLOSURE_HPP
#include "memory/iterator.hpp"
class BitSet;
class Edge;
class EdgeStore;
class EdgeQueue;
class DFSClosure : public ExtendedOopClosure { // XXX BasicOopIterateClosure
private:
static EdgeStore* _edge_store;
static BitSet* _mark_bits;
static const Edge*_start_edge;
static size_t _max_depth;
static bool _ignore_root_set;
DFSClosure* _parent;
const oop* _reference;
size_t _depth;
void add_chain();
void closure_impl(const oop* reference, const oop pointee);
DFSClosure* parent() const { return _parent; }
const oop* reference() const { return _reference; }
DFSClosure(DFSClosure* parent, size_t depth);
DFSClosure();
public:
static void find_leaks_from_edge(EdgeStore* edge_store, BitSet* mark_bits, const Edge* start_edge);
static void find_leaks_from_root_set(EdgeStore* edge_store, BitSet* mark_bits);
void do_root(const oop* ref);
virtual void do_oop(oop* ref);
virtual void do_oop(narrowOop* ref);
};
#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_DFSCLOSURE_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/edge.cpp
#include "precompiled.hpp"
#include "classfile/javaClasses.hpp"
#include "jfr/leakprofiler/chains/edge.hpp"
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
Edge::Edge() : _parent(NULL), _reference(NULL) {}
Edge::Edge(const Edge* parent, const oop* reference) : _parent(parent),
_reference(reference) {}
const oop Edge::pointee() const {
return UnifiedOop::dereference(_reference);
}
const oop Edge::reference_owner() const {
return is_root() ? (oop)NULL : UnifiedOop::dereference(_parent->reference());
}
static const Klass* resolve_klass(const oop obj) {
assert(obj != NULL, "invariant");
return java_lang_Class::is_instance(obj) ?
java_lang_Class::as_Klass(obj) : obj->klass();
}
const Klass* Edge::pointee_klass() const {
return resolve_klass(pointee());
}
const Klass* Edge::reference_owner_klass() const {
const oop ref_owner = reference_owner();
return ref_owner != NULL ? resolve_klass(ref_owner) : NULL;
}
size_t Edge::distance_to_root() const {
size_t depth = 0;
const Edge* current = _parent;
while (current != NULL) {
depth++;
current = current->parent();
}
return depth;
}
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/edge.hpp
#ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGE_HPP
#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGE_HPP
#include "memory/allocation.hpp"
#include "oops/oopsHierarchy.hpp"
class Edge {
protected:
const Edge* _parent;
const oop* _reference;
public:
Edge();
Edge(const Edge* parent, const oop* reference);
const oop* reference() const {
return _reference;
}
const Edge* parent() const {
return _parent;
}
bool is_root() const {
return _parent == NULL;
}
const oop pointee() const;
const Klass* pointee_klass() const;
const oop reference_owner() const;
const Klass* reference_owner_klass() const;
size_t distance_to_root() const;
void* operator new (size_t sz, void* here) {
return here;
}
};
#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGE_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/edgeQueue.cpp
#include "precompiled.hpp"
#include "jfr/leakprofiler/chains/edgeQueue.hpp"
#include "jfr/recorder/storage/jfrVirtualMemory.hpp"
EdgeQueue::EdgeQueue(size_t reservation_size_bytes, size_t commit_block_size_bytes) :
_vmm(NULL),
_reservation_size_bytes(reservation_size_bytes),
_commit_block_size_bytes(commit_block_size_bytes),
_top_index(0),
_bottom_index(0) {
}
bool EdgeQueue::initialize() {
assert(_reservation_size_bytes >= _commit_block_size_bytes, "invariant");
assert(_vmm == NULL, "invariant");
_vmm = new JfrVirtualMemory();
return _vmm != NULL && _vmm->initialize(_reservation_size_bytes, _commit_block_size_bytes, sizeof(Edge));
}
EdgeQueue::~EdgeQueue() {
delete _vmm;
}
void EdgeQueue::add(const Edge* parent, const oop* ref) {
assert(ref != NULL, "Null objects not allowed in EdgeQueue");
assert(!is_full(), "EdgeQueue is full. Check is_full before adding another Edge");
assert(!_vmm->is_full(), "invariant");
void* const allocation = _vmm->new_datum();
assert(allocation != NULL, "invariant");
new (allocation)Edge(parent, ref);
_top_index++;
assert(_vmm->count() == _top_index, "invariant");
}
size_t EdgeQueue::top() const {
return _top_index;
}
size_t EdgeQueue::bottom() const {
return EdgeQueue::_bottom_index;
}
bool EdgeQueue::is_empty() const {
return _top_index == _bottom_index;
}
bool EdgeQueue::is_full() const {
return _vmm->is_full();
}
const Edge* EdgeQueue::remove() const {
assert(!is_empty(), "EdgeQueue is empty. Check if empty before removing Edge");
assert(!_vmm->is_empty(), "invariant");
return (const Edge*)_vmm->get(_bottom_index++);
}
const Edge* EdgeQueue::element_at(size_t index) const {
assert(index >= _bottom_index, "invariant");
assert(index <_top_index, "invariant");
return (Edge*)_vmm->get(index);
}
size_t EdgeQueue::reserved_size() const {
assert(_vmm != NULL, "invariant");
return _vmm->reserved_size();
}
size_t EdgeQueue::live_set() const {
assert(_vmm != NULL, "invariant");
return _vmm->live_set();
}
size_t EdgeQueue::sizeof_edge() const {
assert(_vmm != NULL, "invariant");
return _vmm->aligned_datum_size_bytes();
}
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/edgeQueue.hpp
#ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGEQUEUE_HPP
#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGEQUEUE_HPP
#include "memory/allocation.hpp"
#include "jfr/leakprofiler/chains/edge.hpp"
class JfrVirtualMemory;
class EdgeQueue : public CHeapObj<mtTracing> {
private:
JfrVirtualMemory* _vmm;
const size_t _reservation_size_bytes;
const size_t _commit_block_size_bytes;
mutable size_t _top_index;
mutable size_t _bottom_index;
public:
EdgeQueue(size_t reservation_size_bytes, size_t commit_block_size_bytes);
~EdgeQueue();
bool initialize();
void add(const Edge* parent, const oop* ref);
const Edge* remove() const;
const Edge* element_at(size_t index) const;
size_t top() const;
size_t bottom() const;
bool is_empty() const;
bool is_full() const;
size_t reserved_size() const;
size_t live_set() const;
size_t sizeof_edge() const; // with alignments
};
#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGEQUEUE_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/edgeStore.cpp
#include "precompiled.hpp"
#include "jfr/leakprofiler/chains/edgeStore.hpp"
#include "jfr/leakprofiler/chains/edgeUtils.hpp"
#include "oops/oop.inline.hpp"
StoredEdge::StoredEdge() : Edge() {}
StoredEdge::StoredEdge(const Edge* parent, const oop* reference) : Edge(parent, reference), _gc_root_id(0), _skip_length(0) {}
StoredEdge::StoredEdge(const Edge& edge) : Edge(edge), _gc_root_id(0), _skip_length(0) {}
StoredEdge::StoredEdge(const StoredEdge& edge) : Edge(edge), _gc_root_id(edge._gc_root_id), _skip_length(edge._skip_length) {}
void StoredEdge::operator=(const StoredEdge& edge) {
Edge::operator=(edge);
_gc_root_id = edge._gc_root_id;
_skip_length = edge._skip_length;
}
traceid EdgeStore::_edge_id_counter = 0;
EdgeStore::EdgeStore() : _edges(NULL) {
_edges = new EdgeHashTable(this);
}
EdgeStore::~EdgeStore() {
assert(_edges != NULL, "invariant");
delete _edges;
}
bool EdgeStore::is_empty() const {
return !_edges->has_entries();
}
void EdgeStore::assign_id(EdgeEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->id() == 0, "invariant");
entry->set_id(++_edge_id_counter);
}
bool EdgeStore::equals(const Edge& query, uintptr_t hash, const EdgeEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->hash() == hash, "invariant");
return true;
}
#ifdef ASSERT
bool EdgeStore::contains(const oop* reference) const {
return get(reference) != NULL;
}
#endif
StoredEdge* EdgeStore::get(const oop* reference) const {
assert(reference != NULL, "invariant");
const StoredEdge e(NULL, reference);
EdgeEntry* const entry = _edges->lookup_only(e, (uintptr_t)reference);
return entry != NULL ? entry->literal_addr() : NULL;
}
StoredEdge* EdgeStore::put(const oop* reference) {
assert(reference != NULL, "invariant");
const StoredEdge e(NULL, reference);
assert(NULL == _edges->lookup_only(e, (uintptr_t)reference), "invariant");
EdgeEntry& entry = _edges->put(e, (uintptr_t)reference);
return entry.literal_addr();
}
traceid EdgeStore::get_id(const Edge* edge) const {
assert(edge != NULL, "invariant");
EdgeEntry* const entry = _edges->lookup_only(*edge, (uintptr_t)edge->reference());
assert(entry != NULL, "invariant");
return entry->id();
}
traceid EdgeStore::gc_root_id(const Edge* edge) const {
assert(edge != NULL, "invariant");
const traceid gc_root_id = static_cast<const StoredEdge*>(edge)->gc_root_id();
if (gc_root_id != 0) {
return gc_root_id;
}
assert(edge != NULL, "invariant");
const Edge* const root = EdgeUtils::root(*edge);
assert(root != NULL, "invariant");
assert(root->parent() == NULL, "invariant");
return get_id(root);
}
static const Edge* get_skip_ancestor(const Edge** current, size_t distance_to_root, size_t* skip_length) {
assert(distance_to_root >= EdgeUtils::root_context, "invariant");
assert(*skip_length == 0, "invariant");
const Edge* const target = EdgeUtils::ancestor(**current, *skip_length);
assert(target != NULL, "invariant");
assert(target->distance_to_root() + 1 == EdgeUtils::root_context, "invariant");
return target;
}
bool EdgeStore::put_skip_edge(StoredEdge** previous, const Edge** current, size_t distance_to_root) {
assert(*previous != NULL, "invariant");
assert((*previous)->parent() == NULL, "invariant");
assert(*current != NULL, "invariant");
assert((*current)->distance_to_root() == distance_to_root, "invariant");
if (distance_to_root < EdgeUtils::root_context) {
return false;
}
size_t skip_length = 0;
const Edge* const skip_ancestor = get_skip_ancestor(current, distance_to_root, &skip_length);
assert(skip_ancestor != NULL, "invariant");
(*previous)->set_skip_length(skip_length);
StoredEdge* stored_target = get(skip_ancestor->reference());
if (stored_target != NULL) {
(*previous)->set_parent(stored_target);
return true;
}
assert(stored_target == NULL, "invariant");
stored_target = put(skip_ancestor->reference());
assert(stored_target != NULL, "invariant");
(*previous)->set_parent(stored_target);
return false;
}
static void link_edge(const StoredEdge* current_stored, StoredEdge** previous) {
assert(current_stored != NULL, "invariant");
assert(*previous != NULL, "invariant");
assert((*previous)->parent() == NULL, "invariant");
(*previous)->set_parent(current_stored);
}
static const StoredEdge* find_closest_skip_edge(const StoredEdge* edge, size_t* distance) {
assert(edge != NULL, "invariant");
assert(distance != NULL, "invariant");
const StoredEdge* current = edge;
while (current != NULL && !current->is_skip_edge()) {
++(*distance);
current = current->parent();
}
return current;
}
void EdgeStore::link_with_existing_chain(const StoredEdge* current_stored, StoredEdge** previous, size_t previous_length) {
assert(current_stored != NULL, "invariant");
assert((*previous)->parent() == NULL, "invariant");
size_t distance_to_skip_edge; // including the skip edge itself
const StoredEdge* const closest_skip_edge = find_closest_skip_edge(current_stored, &distance_to_skip_edge);
if (closest_skip_edge == NULL) {
if (distance_to_skip_edge + previous_length <= EdgeUtils::max_ref_chain_depth) {
link_edge(current_stored, previous);
return;
}
assert(current_stored->distance_to_root() == distance_to_skip_edge - 2, "invariant");
put_skip_edge(previous, reinterpret_cast<const Edge**>(¤t_stored), distance_to_skip_edge - 2);
return;
}
assert(closest_skip_edge->is_skip_edge(), "invariant");
if (distance_to_skip_edge + previous_length <= EdgeUtils::leak_context) {
link_edge(current_stored, previous);
return;
}
(*previous)->set_skip_length(distance_to_skip_edge + closest_skip_edge->skip_length());
(*previous)->set_parent(closest_skip_edge->parent());
}
StoredEdge* EdgeStore::link_new_edge(StoredEdge** previous, const Edge** current) {
assert(*previous != NULL, "invariant");
assert((*previous)->parent() == NULL, "invariant");
assert(*current != NULL, "invariant");
assert(!contains((*current)->reference()), "invariant");
StoredEdge* const stored_edge = put((*current)->reference());
assert(stored_edge != NULL, "invariant");
link_edge(stored_edge, previous);
return stored_edge;
}
bool EdgeStore::put_edges(StoredEdge** previous, const Edge** current, size_t limit) {
assert(*previous != NULL, "invariant");
assert(*current != NULL, "invariant");
size_t depth = 1;
while (*current != NULL && depth < limit) {
StoredEdge* stored_edge = get((*current)->reference());
if (stored_edge != NULL) {
link_with_existing_chain(stored_edge, previous, depth);
return true;
}
stored_edge = link_new_edge(previous, current);
assert((*previous)->parent() != NULL, "invariant");
++depth;
}
return NULL == *current;
}
StoredEdge* EdgeStore::associate_leak_context_with_candidate(const Edge* edge) {
assert(edge != NULL, "invariant");
assert(!contains(edge->reference()), "invariant");
StoredEdge* const leak_context_edge = put(edge->reference());
oop sample_object = edge->pointee();
assert(sample_object != NULL, "invariant");
assert(NULL == sample_object->mark(), "invariant");
sample_object->set_mark(markOop(leak_context_edge));
return leak_context_edge;
}
void EdgeStore::put_chain(const Edge* chain, size_t length) {
assert(chain != NULL, "invariant");
assert(chain->distance_to_root() + 1 == length, "invariant");
StoredEdge* const leak_context_edge = associate_leak_context_with_candidate(chain);
assert(leak_context_edge != NULL, "invariant");
assert(leak_context_edge->parent() == NULL, "invariant");
if (1 == length) {
return;
}
const Edge* current = chain->parent();
assert(current != NULL, "invariant");
StoredEdge* previous = leak_context_edge;
if (put_edges(&previous, ¤t, EdgeUtils::leak_context)) {
assert(previous != NULL, "invariant");
put_chain_epilogue(leak_context_edge, EdgeUtils::root(*previous));
return;
}
const size_t distance_to_root = length > EdgeUtils::leak_context ? length - 1 - EdgeUtils::leak_context : length - 1;
assert(current->distance_to_root() == distance_to_root, "invariant");
if (put_skip_edge(&previous, ¤t, distance_to_root)) {
assert(previous != NULL, "invariant");
assert(previous->is_skip_edge(), "invariant");
assert(previous->parent() != NULL, "invariant");
put_chain_epilogue(leak_context_edge, EdgeUtils::root(*previous->parent()));
return;
}
assert(current->distance_to_root() < EdgeUtils::root_context, "invariant");
put_edges(&previous, ¤t, EdgeUtils::root_context);
assert(previous != NULL, "invariant");
put_chain_epilogue(leak_context_edge, EdgeUtils::root(*previous));
}
void EdgeStore::put_chain_epilogue(StoredEdge* leak_context_edge, const Edge* root) const {
assert(leak_context_edge != NULL, "invariant");
assert(root != NULL, "invariant");
store_gc_root_id_in_leak_context_edge(leak_context_edge, root);
assert(leak_context_edge->distance_to_root() + 1 <= EdgeUtils::max_ref_chain_depth, "invariant");
}
void EdgeStore::store_gc_root_id_in_leak_context_edge(StoredEdge* leak_context_edge, const Edge* root) const {
assert(leak_context_edge != NULL, "invariant");
assert(leak_context_edge->gc_root_id() == 0, "invariant");
assert(root != NULL, "invariant");
assert(root->parent() == NULL, "invariant");
assert(root->distance_to_root() == 0, "invariant");
const StoredEdge* const stored_root = static_cast<const StoredEdge*>(root);
traceid root_id = stored_root->gc_root_id();
if (root_id == 0) {
root_id = get_id(root);
stored_root->set_gc_root_id(root_id);
}
assert(root_id != 0, "invariant");
leak_context_edge->set_gc_root_id(root_id);
assert(leak_context_edge->gc_root_id() == stored_root->gc_root_id(), "invariant");
}
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/edgeStore.hpp
#ifndef SHARE_VM_LEAKPROFILER_CHAINS_EDGESTORE_HPP
#define SHARE_VM_LEAKPROFILER_CHAINS_EDGESTORE_HPP
#include "jfr/leakprofiler/chains/edge.hpp"
#include "jfr/utilities/jfrHashtable.hpp"
#include "memory/allocation.hpp"
typedef u8 traceid;
class StoredEdge : public Edge {
private:
mutable traceid _gc_root_id;
size_t _skip_length;
public:
StoredEdge();
StoredEdge(const Edge* parent, const oop* reference);
StoredEdge(const Edge& edge);
StoredEdge(const StoredEdge& edge);
void operator=(const StoredEdge& edge);
traceid gc_root_id() const { return _gc_root_id; }
void set_gc_root_id(traceid root_id) const { _gc_root_id = root_id; }
bool is_skip_edge() const { return _skip_length != 0; }
size_t skip_length() const { return _skip_length; }
void set_skip_length(size_t length) { _skip_length = length; }
void set_parent(const Edge* edge) { this->_parent = edge; }
StoredEdge* parent() const {
return const_cast<StoredEdge*>(static_cast<const StoredEdge*>(Edge::parent()));
}
};
class EdgeStore : public CHeapObj<mtTracing> {
typedef HashTableHost<StoredEdge, traceid, Entry, EdgeStore> EdgeHashTable;
typedef EdgeHashTable::HashEntry EdgeEntry;
template <typename,
typename,
template<typename, typename> class,
typename,
size_t>
friend class HashTableHost;
friend class EventEmitter;
friend class ObjectSampleWriter;
friend class ObjectSampleCheckpoint;
private:
static traceid _edge_id_counter;
EdgeHashTable* _edges;
void assign_id(EdgeEntry* entry);
bool equals(const Edge& query, uintptr_t hash, const EdgeEntry* entry);
StoredEdge* get(const oop* reference) const;
StoredEdge* put(const oop* reference);
traceid gc_root_id(const Edge* edge) const;
bool put_edges(StoredEdge** previous, const Edge** current, size_t length);
bool put_skip_edge(StoredEdge** previous, const Edge** current, size_t distance_to_root);
void put_chain_epilogue(StoredEdge* leak_context_edge, const Edge* root) const;
StoredEdge* associate_leak_context_with_candidate(const Edge* edge);
void store_gc_root_id_in_leak_context_edge(StoredEdge* leak_context_edge, const Edge* root) const;
StoredEdge* link_new_edge(StoredEdge** previous, const Edge** current);
void link_with_existing_chain(const StoredEdge* current_stored, StoredEdge** previous, size_t previous_length);
template <typename T>
void iterate(T& functor) const { _edges->iterate_value<T>(functor); }
DEBUG_ONLY(bool contains(const oop* reference) const;)
public:
EdgeStore();
~EdgeStore();
bool is_empty() const;
traceid get_id(const Edge* edge) const;
void put_chain(const Edge* chain, size_t length);
};
#endif // SHARE_VM_LEAKPROFILER_CHAINS_EDGESTORE_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/edgeUtils.cpp
#include "precompiled.hpp"
#include "classfile/javaClasses.hpp"
#include "jfr/leakprofiler/chains/edge.hpp"
#include "jfr/leakprofiler/chains/edgeStore.hpp"
#include "jfr/leakprofiler/chains/edgeUtils.hpp"
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
#include "oops/fieldStreams.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/oopsHierarchy.hpp"
#include "runtime/handles.inline.hpp"
bool EdgeUtils::is_leak_edge(const Edge& edge) {
return (const Edge*)edge.pointee()->mark() == &edge;
}
static int field_offset(const StoredEdge& edge) {
assert(!edge.is_root(), "invariant");
const oop ref_owner = edge.reference_owner();
assert(ref_owner != NULL, "invariant");
const oop* reference = UnifiedOop::decode(edge.reference());
assert(reference != NULL, "invariant");
assert(!UnifiedOop::is_narrow(reference), "invariant");
assert(!ref_owner->is_array(), "invariant");
assert(ref_owner->is_instance(), "invariant");
const int offset = (int)pointer_delta(reference, ref_owner, sizeof(char));
assert(offset < (ref_owner->size() * HeapWordSize), "invariant");
return offset;
}
static const InstanceKlass* field_type(const StoredEdge& edge) {
assert(!edge.is_root() || !EdgeUtils::is_array_element(edge), "invariant");
return (const InstanceKlass*)edge.reference_owner_klass();
}
const Symbol* EdgeUtils::field_name_symbol(const Edge& edge) {
assert(!edge.is_root(), "invariant");
assert(!is_array_element(edge), "invariant");
const int offset = field_offset(edge);
const InstanceKlass* ik = field_type(edge);
while (ik != NULL) {
JavaFieldStream jfs(ik);
while (!jfs.done()) {
if (offset == jfs.offset()) {
return jfs.name();
}
jfs.next();
}
ik = (InstanceKlass*)ik->super();
}
return NULL;
}
jshort EdgeUtils::field_modifiers(const Edge& edge) {
const int offset = field_offset(edge);
const InstanceKlass* ik = field_type(edge);
while (ik != NULL) {
JavaFieldStream jfs(ik);
while (!jfs.done()) {
if (offset == jfs.offset()) {
return jfs.access_flags().as_short();
}
jfs.next();
}
ik = (InstanceKlass*)ik->super();
}
return 0;
}
bool EdgeUtils::is_array_element(const Edge& edge) {
assert(!edge.is_root(), "invariant");
const oop ref_owner = edge.reference_owner();
assert(ref_owner != NULL, "invariant");
return ref_owner->is_objArray();
}
static int array_offset(const Edge& edge) {
assert(!edge.is_root(), "invariant");
const oop ref_owner = edge.reference_owner();
assert(ref_owner != NULL, "invariant");
const oop* reference = UnifiedOop::decode(edge.reference());
assert(reference != NULL, "invariant");
assert(!UnifiedOop::is_narrow(reference), "invariant");
assert(ref_owner->is_array(), "invariant");
const objArrayOop ref_owner_array = static_cast<const objArrayOop>(ref_owner);
const int offset = (int)pointer_delta(reference, ref_owner_array->base(), heapOopSize);
assert(offset >= 0 && offset < ref_owner_array->length(), "invariant");
return offset;
}
int EdgeUtils::array_index(const Edge& edge) {
return is_array_element(edge) ? array_offset(edge) : 0;
}
int EdgeUtils::array_size(const Edge& edge) {
if (is_array_element(edge)) {
const oop ref_owner = edge.reference_owner();
assert(ref_owner != NULL, "invariant");
assert(ref_owner->is_objArray(), "invariant");
return ((objArrayOop)(ref_owner))->length();
}
return 0;
}
const Edge* EdgeUtils::root(const Edge& edge) {
const Edge* current = &edge;
const Edge* parent = current->parent();
while (parent != NULL) {
current = parent;
parent = current->parent();
}
assert(current != NULL, "invariant");
return current;
}
const Edge* EdgeUtils::ancestor(const Edge& edge, size_t distance) {
const Edge* current = &edge;
const Edge* parent = current->parent();
size_t seek = 0;
while (parent != NULL && seek != distance) {
seek++;
current = parent;
parent = parent->parent();
}
return current;
}
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/edgeUtils.hpp
#ifndef SHARE_VM_LEAKPROFILER_CHAINS_EDGEUTILS_HPP
#define SHARE_VM_LEAKPROFILER_CHAINS_EDGEUTILS_HPP
#include "memory/allocation.hpp"
class Edge;
class Symbol;
class EdgeUtils : public AllStatic {
public:
static const size_t leak_context = 100;
static const size_t root_context = 100;
static const size_t max_ref_chain_depth = leak_context + root_context;
static bool is_leak_edge(const Edge& edge);
static const Edge* root(const Edge& edge);
static const Edge* ancestor(const Edge& edge, size_t distance);
static bool is_array_element(const Edge& edge);
static int array_index(const Edge& edge);
static int array_size(const Edge& edge);
static const Symbol* field_name_symbol(const Edge& edge);
static jshort field_modifiers(const Edge& edge);
};
#endif // SHARE_VM_LEAKPROFILER_CHAINS_EDGEUTILS_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/objectSampleMarker.hpp
#ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_OBJECTSAMPLEMARKER_HPP
#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_OBJECTSAMPLEMARKER_HPP
#include "memory/allocation.hpp"
#include "oops/markOop.hpp"
#include "utilities/growableArray.hpp"
class ObjectSampleMarker : public StackObj {
private:
class ObjectSampleMarkOop : public ResourceObj {
friend class ObjectSampleMarker;
private:
oop _obj;
markOop _mark_oop;
ObjectSampleMarkOop(const oop obj,
const markOop mark_oop) : _obj(obj),
_mark_oop(mark_oop) {}
public:
ObjectSampleMarkOop() : _obj(NULL), _mark_oop(NULL) {}
};
GrowableArray<ObjectSampleMarkOop>* _store;
public:
ObjectSampleMarker() :
_store(new GrowableArray<ObjectSampleMarkOop>(16)) {}
~ObjectSampleMarker() {
assert(_store != NULL, "invariant");
while (_store->is_nonempty()) {
ObjectSampleMarkOop sample_oop = _store->pop();
sample_oop._obj->set_mark(sample_oop._mark_oop);
assert(sample_oop._obj->mark() == sample_oop._mark_oop, "invariant");
}
}
void mark(oop obj) {
assert(obj != NULL, "invariant");
_store->push(ObjectSampleMarkOop(obj, obj->mark()));
assert(NULL == markOopDesc::INFLATING(), "invariant");
obj->set_mark(markOopDesc::INFLATING());
assert(NULL == obj->mark(), "invariant");
}
};
#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_OBJECTSAMPLEMARKER_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp
#include "precompiled.hpp"
#include "gc_interface/collectedHeap.hpp"
#include "jfr/leakprofiler/leakProfiler.hpp"
#include "jfr/leakprofiler/chains/bfsClosure.hpp"
#include "jfr/leakprofiler/chains/bitset.hpp"
#include "jfr/leakprofiler/chains/dfsClosure.hpp"
#include "jfr/leakprofiler/chains/edge.hpp"
#include "jfr/leakprofiler/chains/edgeQueue.hpp"
#include "jfr/leakprofiler/chains/edgeStore.hpp"
#include "jfr/leakprofiler/chains/objectSampleMarker.hpp"
#include "jfr/leakprofiler/chains/rootSetClosure.hpp"
#include "jfr/leakprofiler/chains/edgeStore.hpp"
#include "jfr/leakprofiler/chains/objectSampleMarker.hpp"
#include "jfr/leakprofiler/chains/pathToGcRootsOperation.hpp"
#include "jfr/leakprofiler/checkpoint/eventEmitter.hpp"
#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
#include "jfr/leakprofiler/sampling/objectSample.hpp"
#include "jfr/leakprofiler/sampling/objectSampler.hpp"
#include "jfr/leakprofiler/utilities/granularTimer.hpp"
#include "memory/universe.hpp"
#include "oops/markOop.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/safepoint.hpp"
#include "utilities/globalDefinitions.hpp"
PathToGcRootsOperation::PathToGcRootsOperation(ObjectSampler* sampler, EdgeStore* edge_store, int64_t cutoff, bool emit_all) :
_sampler(sampler),_edge_store(edge_store), _cutoff_ticks(cutoff), _emit_all(emit_all) {}
static size_t edge_queue_memory_reservation(const MemRegion& heap_region) {
const size_t memory_reservation_bytes = MAX2(heap_region.byte_size() / 20, 32*M);
assert(memory_reservation_bytes >= (size_t)32*M, "invariant");
return memory_reservation_bytes;
}
static size_t edge_queue_memory_commit_size(size_t memory_reservation_bytes) {
const size_t memory_commit_block_size_bytes = memory_reservation_bytes / 10;
assert(memory_commit_block_size_bytes >= (size_t)3*M, "invariant");
return memory_commit_block_size_bytes;
}
static void log_edge_queue_summary(const EdgeQueue& edge_queue) {
if (LogJFR && Verbose) tty->print_cr("EdgeQueue reserved size total: " SIZE_FORMAT " [KB]", edge_queue.reserved_size() / K);
if (LogJFR && Verbose) tty->print_cr("EdgeQueue edges total: " SIZE_FORMAT, edge_queue.top());
if (LogJFR && Verbose) tty->print_cr("EdgeQueue liveset total: " SIZE_FORMAT " [KB]", edge_queue.live_set() / K);
if (edge_queue.reserved_size() > 0) {
if (LogJFR && Verbose) tty->print_cr("EdgeQueue commit reserve ratio: %f\n",
((double)edge_queue.live_set() / (double)edge_queue.reserved_size()));
}
}
void PathToGcRootsOperation::doit() {
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
assert(_cutoff_ticks > 0, "invariant");
const MemRegion heap_region = Universe::heap()->reserved_region();
BitSet mark_bits(heap_region);
const size_t edge_queue_reservation_size = edge_queue_memory_reservation(heap_region);
EdgeQueue edge_queue(edge_queue_reservation_size, edge_queue_memory_commit_size(edge_queue_reservation_size));
if (!(mark_bits.initialize() && edge_queue.initialize())) {
if (LogJFR) tty->print_cr("Unable to allocate memory for root chain processing");
return;
}
ObjectSampleMarker marker;
if (ObjectSampleCheckpoint::mark(_sampler, marker, _emit_all) == 0) {
return;
}
Universe::heap()->ensure_parsability(false);
BFSClosure bfs(&edge_queue, _edge_store, &mark_bits);
RootSetClosure<BFSClosure> roots(&bfs);
GranularTimer::start(_cutoff_ticks, 1000000);
roots.process();
if (edge_queue.is_full()) {
DFSClosure::find_leaks_from_root_set(_edge_store, &mark_bits);
} else {
bfs.process();
}
GranularTimer::stop();
log_edge_queue_summary(edge_queue);
EventEmitter emitter(GranularTimer::start_time(), GranularTimer::end_time());
emitter.write_events(_sampler, _edge_store, _emit_all);
}
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/pathToGcRootsOperation.hpp
#ifndef SHARE_JFR_LEAKPROFILER_CHAINS_PATHTOGCROOTSOPERATION_HPP
#define SHARE_JFR_LEAKPROFILER_CHAINS_PATHTOGCROOTSOPERATION_HPP
#include "jfr/leakprofiler/utilities/vmOperation.hpp"
class EdgeStore;
class ObjectSampler;
class PathToGcRootsOperation : public OldObjectVMOperation {
private:
ObjectSampler* _sampler;
EdgeStore* const _edge_store;
const int64_t _cutoff_ticks;
const bool _emit_all;
public:
PathToGcRootsOperation(ObjectSampler* sampler, EdgeStore* edge_store, int64_t cutoff, bool emit_all);
virtual void doit();
};
#endif // SHARE_JFR_LEAKPROFILER_CHAINS_PATHTOGCROOTSOPERATION_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/rootSetClosure.cpp
#include "precompiled.hpp"
#include "classfile/classLoaderData.hpp"
#include "classfile/systemDictionary.hpp"
#include "jfr/leakprofiler/chains/bfsClosure.hpp"
#include "jfr/leakprofiler/chains/dfsClosure.hpp"
#include "jfr/leakprofiler/chains/edgeQueue.hpp"
#include "jfr/leakprofiler/chains/rootSetClosure.hpp"
#include "jfr/leakprofiler/utilities/saveRestore.hpp"
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
#include "memory/universe.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/jniHandles.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/thread.hpp"
#include "services/management.hpp"
#include "utilities/align.hpp"
template <typename Delegate>
RootSetClosure<Delegate>::RootSetClosure(Delegate* delegate) : _delegate(delegate) {}
template <typename Delegate>
void RootSetClosure<Delegate>::do_oop(oop* ref) {
assert(ref != NULL, "invariant");
if (!is_aligned(ref, HeapWordSize)) {
return;
}
assert(is_aligned(ref, HeapWordSize), "invariant");
if (*ref != NULL) {
_delegate->do_root(ref);
}
}
template <typename Delegate>
void RootSetClosure<Delegate>::do_oop(narrowOop* ref) {
assert(ref != NULL, "invariant");
assert(is_aligned(ref, sizeof(narrowOop)), "invariant");
const oop pointee = oopDesc::load_decode_heap_oop(ref);
if (pointee != NULL) {
_delegate->do_root(UnifiedOop::encode(ref));
}
}
class RootSetClosureMarkScope : public MarkingCodeBlobClosure::MarkScope {};
template <typename Delegate>
void RootSetClosure<Delegate>::process() {
RootSetClosureMarkScope mark_scope;
CLDToOopClosure cldt_closure(this);
ClassLoaderDataGraph::always_strong_cld_do(&cldt_closure);
CodeBlobToOopClosure blobs(this, false);
Threads::oops_do(this, NULL, &blobs); // XXX set CLDClosure to NULL
ObjectSynchronizer::oops_do(this);
Universe::oops_do(this);
JNIHandles::oops_do(this);
JvmtiExport::oops_do(this);
SystemDictionary::oops_do(this);
Management::oops_do(this);
StringTable::oops_do(this);
}
template class RootSetClosure<BFSClosure>;
template class RootSetClosure<DFSClosure>;
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/chains/rootSetClosure.hpp
#ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_ROOTSETCLOSURE_HPP
#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_ROOTSETCLOSURE_HPP
#include "memory/iterator.hpp"
template <typename Delegate>
class RootSetClosure: public ExtendedOopClosure { // BasicOopIterateClosure
private:
Delegate* const _delegate;
public:
RootSetClosure(Delegate* delegate);
void process();
virtual void do_oop(oop* reference);
virtual void do_oop(narrowOop* reference);
};
#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_ROOTSETCLOSURE_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/checkpoint/eventEmitter.cpp
#include "precompiled.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/leakprofiler/chains/edgeStore.hpp"
#include "jfr/leakprofiler/chains/pathToGcRootsOperation.hpp"
#include "jfr/leakprofiler/checkpoint/eventEmitter.hpp"
#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
#include "jfr/leakprofiler/sampling/objectSample.hpp"
#include "jfr/leakprofiler/sampling/objectSampler.hpp"
#include "memory/resourceArea.hpp"
#include "oops/markOop.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/vmThread.hpp"
EventEmitter::EventEmitter(const JfrTicks& start_time, const JfrTicks& end_time) :
_start_time(start_time),
_end_time(end_time),
_thread(Thread::current()),
_jfr_thread_local(_thread->jfr_thread_local()),
_thread_id(_thread->jfr_thread_local()->thread_id()) {}
EventEmitter::~EventEmitter() {
_jfr_thread_local->set_thread_id(_thread_id);
_jfr_thread_local->clear_cached_stack_trace();
}
void EventEmitter::emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_all) {
assert(sampler != NULL, "invariant");
ResourceMark rm;
EdgeStore edge_store;
if (cutoff_ticks <= 0) {
JfrTicks time_stamp = JfrTicks::now();
EventEmitter emitter(time_stamp, time_stamp);
emitter.write_events(sampler, &edge_store, emit_all);
return;
}
PathToGcRootsOperation op(sampler, &edge_store, cutoff_ticks, emit_all);
VMThread::execute(&op);
}
size_t EventEmitter::write_events(ObjectSampler* object_sampler, EdgeStore* edge_store, bool emit_all) {
assert(_thread == Thread::current(), "invariant");
assert(_thread->jfr_thread_local() == _jfr_thread_local, "invariant");
assert(object_sampler != NULL, "invariant");
assert(edge_store != NULL, "invariant");
const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value();
size_t count = 0;
const ObjectSample* current = object_sampler->first();
while (current != NULL) {
ObjectSample* prev = current->prev();
if (current->is_alive_and_older_than(last_sweep)) {
write_event(current, edge_store);
++count;
}
current = prev;
}
if (count > 0) {
ObjectSampleCheckpoint::write(object_sampler, edge_store, emit_all, _thread);
}
return count;
}
static int array_size(const oop object) {
assert(object != NULL, "invariant");
if (object->is_array()) {
return arrayOop(object)->length();
}
return min_jint;
}
void EventEmitter::write_event(const ObjectSample* sample, EdgeStore* edge_store) {
assert(sample != NULL, "invariant");
assert(!sample->is_dead(), "invariant");
assert(edge_store != NULL, "invariant");
assert(_jfr_thread_local != NULL, "invariant");
const oop* object_addr = sample->object_addr();
traceid gc_root_id = 0;
const Edge* edge = NULL;
if (SafepointSynchronize::is_at_safepoint()) {
edge = (const Edge*)(*object_addr)->mark();
}
if (edge == NULL) {
edge = edge_store->put(object_addr);
} else {
gc_root_id = edge_store->gc_root_id(edge);
}
assert(edge != NULL, "invariant");
const traceid object_id = edge_store->get_id(edge);
assert(object_id != 0, "invariant");
EventOldObjectSample e(UNTIMED);
e.set_starttime(_start_time);
e.set_endtime(_end_time);
e.set_allocationTime(sample->allocation_time());
e.set_lastKnownHeapUsage(sample->heap_used_at_last_gc());
e.set_object(object_id);
e.set_arrayElements(array_size(edge->pointee()));
e.set_root(gc_root_id);
_jfr_thread_local->set_cached_stack_trace_id(sample->stack_trace_id());
assert(sample->has_thread(), "invariant");
_jfr_thread_local->set_thread_id(sample->thread_id());
e.commit();
}
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/checkpoint/eventEmitter.hpp
#ifndef SHARE_JFR_LEAKPROFILER_CHECKPOINT_EVENTEMITTER_HPP
#define SHARE_JFR_LEAKPROFILER_CHECKPOINT_EVENTEMITTER_HPP
#include "memory/allocation.hpp"
#include "jfr/utilities/jfrTime.hpp"
typedef u8 traceid;
class EdgeStore;
class JfrThreadLocal;
class ObjectSample;
class ObjectSampler;
class Thread;
class EventEmitter : public CHeapObj<mtTracing> {
friend class LeakProfiler;
friend class PathToGcRootsOperation;
private:
const JfrTicks& _start_time;
const JfrTicks& _end_time;
Thread* _thread;
JfrThreadLocal* _jfr_thread_local;
traceid _thread_id;
EventEmitter(const JfrTicks& start_time, const JfrTicks& end_time);
~EventEmitter();
void write_event(const ObjectSample* sample, EdgeStore* edge_store);
size_t write_events(ObjectSampler* sampler, EdgeStore* store, bool emit_all);
static void emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_all);
};
#endif // SHARE_JFR_LEAKPROFILER_CHECKPOINT_EVENTEMITTER_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp
#include "precompiled.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
#include "jfr/leakprofiler/chains/edgeStore.hpp"
#include "jfr/leakprofiler/chains/objectSampleMarker.hpp"
#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
#include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp"
#include "jfr/leakprofiler/leakProfiler.hpp"
#include "jfr/leakprofiler/sampling/objectSample.hpp"
#include "jfr/leakprofiler/sampling/objectSampler.hpp"
#include "jfr/leakprofiler/utilities/rootType.hpp"
#include "jfr/metadata/jfrSerializer.hpp"
#include "runtime/interfaceSupport.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/thread.inline.hpp"
template <typename SampleProcessor>
static void do_samples(ObjectSample* sample, const ObjectSample* const end, SampleProcessor& processor) {
assert(sample != NULL, "invariant");
while (sample != end) {
processor.sample_do(sample);
sample = sample->next();
}
}
class RootSystemType : public JfrSerializer {
public:
void serialize(JfrCheckpointWriter& writer) {
const u4 nof_root_systems = OldObjectRoot::_number_of_systems;
writer.write_count(nof_root_systems);
for (u4 i = 0; i < nof_root_systems; ++i) {
writer.write_key(i);
writer.write(OldObjectRoot::system_description((OldObjectRoot::System)i));
}
}
};
class RootType : public JfrSerializer {
public:
void serialize(JfrCheckpointWriter& writer) {
const u4 nof_root_types = OldObjectRoot::_number_of_types;
writer.write_count(nof_root_types);
for (u4 i = 0; i < nof_root_types; ++i) {
writer.write_key(i);
writer.write(OldObjectRoot::type_description((OldObjectRoot::Type)i));
}
}
};
class CheckpointInstall {
private:
const JfrCheckpointBlobHandle& _cp;
public:
CheckpointInstall(const JfrCheckpointBlobHandle& cp) : _cp(cp) {}
void sample_do(ObjectSample* sample) {
assert(sample != NULL, "invariant");
if (!sample->is_dead()) {
sample->set_klass_checkpoint(_cp);
}
}
};
class CheckpointWrite {
private:
JfrCheckpointWriter& _writer;
const jlong _last_sweep;
public:
CheckpointWrite(JfrCheckpointWriter& writer, jlong last_sweep) : _writer(writer), _last_sweep(last_sweep) {}
void sample_do(ObjectSample* sample) {
assert(sample != NULL, "invariant");
if (sample->is_alive_and_older_than(_last_sweep)) {
if (sample->has_thread_checkpoint()) {
const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint();
thread_cp->exclusive_write(_writer);
}
if (sample->has_klass_checkpoint()) {
const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint();
klass_cp->exclusive_write(_writer);
}
}
}
};
class CheckpointStateReset {
private:
const jlong _last_sweep;
public:
CheckpointStateReset(jlong last_sweep) : _last_sweep(last_sweep) {}
void sample_do(ObjectSample* sample) {
assert(sample != NULL, "invariant");
if (sample->is_alive_and_older_than(_last_sweep)) {
if (sample->has_thread_checkpoint()) {
const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint();
thread_cp->reset_write_state();
}
if (sample->has_klass_checkpoint()) {
const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint();
klass_cp->reset_write_state();
}
}
}
};
class StackTraceWrite {
private:
JfrStackTraceRepository& _stack_trace_repo;
JfrCheckpointWriter& _writer;
int _count;
public:
StackTraceWrite(JfrStackTraceRepository& stack_trace_repo, JfrCheckpointWriter& writer) :
_stack_trace_repo(stack_trace_repo), _writer(writer), _count(0) {
JfrStacktrace_lock->lock();
}
~StackTraceWrite() {
assert(JfrStacktrace_lock->owned_by_self(), "invariant");
JfrStacktrace_lock->unlock();
}
void sample_do(ObjectSample* sample) {
assert(sample != NULL, "invariant");
if (!sample->is_dead()) {
if (sample->has_stack_trace()) {
JfrTraceId::use(sample->klass(), true);
_stack_trace_repo.write(_writer, sample->stack_trace_id(), sample->stack_trace_hash());
++_count;
}
}
}
int count() const {
return _count;
}
};
class SampleMark {
private:
ObjectSampleMarker& _marker;
jlong _last_sweep;
int _count;
public:
SampleMark(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker),
_last_sweep(last_sweep),
_count(0) {}
void sample_do(ObjectSample* sample) {
assert(sample != NULL, "invariant");
if (sample->is_alive_and_older_than(_last_sweep)) {
_marker.mark(sample->object());
++_count;
}
}
int count() const {
return _count;
}
};
void ObjectSampleCheckpoint::install(JfrCheckpointWriter& writer, bool class_unload, bool type_set) {
if (!writer.has_data()) {
return;
}
assert(writer.has_data(), "invariant");
const JfrCheckpointBlobHandle h_cp = writer.checkpoint_blob();
CheckpointInstall install(h_cp);
ObjectSampler* const object_sampler = ObjectSampler::sampler();
assert(object_sampler != NULL, "invariant");
ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last());
const ObjectSample* const last_resolved = object_sampler->last_resolved();
if (last != last_resolved) {
do_samples(last, last_resolved, install);
if (class_unload) {
return;
}
if (type_set) {
object_sampler->set_last_resolved(last);
}
}
}
void ObjectSampleCheckpoint::write(ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {
assert(sampler != NULL, "invariant");
assert(edge_store != NULL, "invariant");
assert(thread != NULL, "invariant");
static bool types_registered = false;
if (!types_registered) {
JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType());
JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType());
types_registered = true;
}
const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value();
ObjectSample* const last = const_cast<ObjectSample*>(sampler->last());
{
JfrCheckpointWriter writer(false, false, thread);
CheckpointWrite checkpoint_write(writer, last_sweep);
do_samples(last, NULL, checkpoint_write);
}
CheckpointStateReset state_reset(last_sweep);
do_samples(last, NULL, state_reset);
if (!edge_store->is_empty()) {
JfrCheckpointWriter writer(false, true, thread);
ObjectSampleWriter osw(writer, edge_store);
edge_store->iterate(osw);
}
}
int ObjectSampleCheckpoint::mark(ObjectSampler* object_sampler, ObjectSampleMarker& marker, bool emit_all) {
assert(object_sampler != NULL, "invariant");
ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last());
if (last == NULL) {
return 0;
}
const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value();
SampleMark mark(marker, last_sweep);
do_samples(last, NULL, mark);
return mark.count();
}
WriteObjectSampleStacktrace::WriteObjectSampleStacktrace(ObjectSampler* sampler, JfrStackTraceRepository& repo) :
_sampler(sampler), _stack_trace_repo(repo) {}
bool WriteObjectSampleStacktrace::process() {
assert(LeakProfiler::is_running(), "invariant");
assert(_sampler != NULL, "invariant");
ObjectSample* const last = const_cast<ObjectSample*>(_sampler->last());
const ObjectSample* const last_resolved = _sampler->last_resolved();
if (last == last_resolved) {
return true;
}
JfrCheckpointWriter writer(false, true, Thread::current());
const JfrCheckpointContext ctx = writer.context();
writer.write_type(TYPE_STACKTRACE);
const jlong count_offset = writer.reserve(sizeof(u4));
int count = 0;
{
StackTraceWrite stack_trace_write(_stack_trace_repo, writer); // JfrStacktrace_lock
do_samples(last, last_resolved, stack_trace_write);
count = stack_trace_write.count();
}
if (count == 0) {
writer.set_context(ctx);
return true;
}
assert(count > 0, "invariant");
writer.write_count((u4)count, count_offset);
JfrStackTraceRepository::write_metadata(writer);
ObjectSampleCheckpoint::install(writer, false, false);
return true;
}
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp
#ifndef SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP
#define SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP
#include "memory/allocation.hpp"
class EdgeStore;
class JfrCheckpointWriter;
class JfrStackTraceRepository;
class ObjectSampleMarker;
class ObjectSampler;
class ObjectSampleCheckpoint : AllStatic {
public:
static void install(JfrCheckpointWriter& writer, bool class_unload, bool type_set);
static void write(ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread);
static int mark(ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all);
};
class WriteObjectSampleStacktrace : public StackObj {
private:
ObjectSampler* const _sampler;
JfrStackTraceRepository& _stack_trace_repo;
public:
WriteObjectSampleStacktrace(ObjectSampler* sampler, JfrStackTraceRepository& repo);
bool process();
};
#endif // SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP
C:\hotspot-69087d08d473\src\share\vm/jfr/leakprofiler/checkpoint/objectSampleDescription.cpp
#include "precompiled.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "jfr/leakprofiler/checkpoint/objectSampleDescription.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/thread.hpp"
#include "utilities/ostream.hpp"
static Symbol* symbol_size = NULL;
ObjectDescriptionBuilder::ObjectDescriptionBuilder() {
reset();
}
void ObjectDescriptionBuilder::write_int(jint value) {
char buf[20];
jio_snprintf(buf, sizeof(buf), "%d", value);
write_text(buf);
}
void ObjectDescriptionBuilder::write_text(const char* text) {
if (_index == sizeof(_buffer) - 2) {
return;
}
while (*text != '\0' && _index < sizeof(_buffer) - 2) {
_buffer[_index] = *text;
_index++;
text++;
}
assert(_index < sizeof(_buffer) - 1, "index should not exceed buffer size");
if (_index == sizeof(_buffer) - 2) {
_buffer[_index-3] = '.';
_buffer[_index-2] = '.';
_buffer[_index-1] = '.';
}
_buffer[_index] = '\0';
}
void ObjectDescriptionBuilder::reset() {
_index = 0;
_buffer[0] = '\0';
}
void ObjectDescriptionBuilder::print_description(outputStream* out) {
out->print("%s", (const char*)_buffer);
}
sssssssssss37
最新推荐文章于 2024-07-08 00:01:13 发布