0

I am trying to log every access to a field/static variable for an analysis tool I'm building, so far I have found this interpreter rt function,

 void InterpreterRuntime::resolve_get_put(JavaThread* current, Bytecodes::Code bytecode) { // resolve field fieldDescriptor info; LastFrameAccessor last_frame(current); constantPoolHandle pool(current, last_frame.method()->constants()); methodHandle m(current, last_frame.method()); bool is_put = (bytecode == Bytecodes::_putfield || bytecode == Bytecodes::_nofast_putfield || bytecode == Bytecodes::_putstatic); bool is_static = (bytecode == Bytecodes::_getstatic || bytecode == Bytecodes::_putstatic); { JvmtiHideSingleStepping jhss(current); JavaThread* THREAD = current; // For exception macros. LinkResolver::resolve_field_access(info, pool, last_frame.get_index_u2_cpcache(bytecode), m, bytecode, CHECK); } // end JvmtiHideSingleStepping fprintf(stderr, "%s access to %s.%s%s\n", is_put ? "put" : "get", info.field_holder()->external_name(), info.name()->as_C_string(), info.signature()->as_C_string()); // check if link resolution caused cpCache to be updated ConstantPoolCacheEntry* cp_cache_entry = last_frame.cache_entry(); if (cp_cache_entry->is_resolved(bytecode)) return; // compute auxiliary field attributes TosState state = as_TosState(info.field_type()); // Resolution of put instructions on final fields is delayed. That is required so that // exceptions are thrown at the correct place (when the instruction is actually invoked). // If we do not resolve an instruction in the current pass, leaving the put_code // set to zero will cause the next put instruction to the same field to reresolve. // Resolution of put instructions to final instance fields with invalid updates (i.e., // to final instance fields with updates originating from a method different than <init>) // is inhibited. A putfield instruction targeting an instance final field must throw // an IllegalAccessError if the instruction is not in an instance // initializer method <init>. If resolution were not inhibited, a putfield // in an initializer method could be resolved in the initializer. Subsequent // putfield instructions to the same field would then use cached information. // As a result, those instructions would not pass through the VM. That is, // checks in resolve_field_access() would not be executed for those instructions // and the required IllegalAccessError would not be thrown. // // Also, we need to delay resolving getstatic and putstatic instructions until the // class is initialized. This is required so that access to the static // field will call the initialization function every time until the class // is completely initialized ala. in 2.17.5 in JVM Specification. InstanceKlass* klass = info.field_holder(); bool uninitialized_static = is_static && !klass->is_initialized(); bool has_initialized_final_update = info.field_holder()->major_version() >= 53 && info.has_initialized_final_update(); assert(!(has_initialized_final_update && !info.access_flags().is_final()), "Fields with initialized final updates must be final"); Bytecodes::Code get_code = (Bytecodes::Code)0; Bytecodes::Code put_code = (Bytecodes::Code)0; if (!uninitialized_static) { get_code = ((is_static) ? Bytecodes::_getstatic : Bytecodes::_getfield); if ((is_put && !has_initialized_final_update) || !info.access_flags().is_final()) { put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield); } } cp_cache_entry->set_field( get_code, put_code, info.field_holder(), info.index(), info.offset(), state, info.access_flags().is_final(), info.access_flags().is_volatile() ); } 

However, this only logs the first access to a field/static variable, I am assuming that this is then cached and placed on some table to be used later for faster access, but I didn't find anything useful so far.

Where is the caching taking place and is there a way to get every access, maybe I should edit cpu specific code?

P.S I am using openjdk 21 and the file can be found at src/hotspot/share/interpreter/interpreterRuntime.cpp.

Thanks

1 Answer 1

2

You don't need to modify JDK code to trace field access.
JVM Tool Interface (JVM TI) is desgined specifically for such tools. See SetFieldAccessWatch.

Alternatively, you may create a bytecode instrumentation agent that modifies loaded classes by adding a call to your tracing method next to each getfield/putfield/getstatic/putstatic bytecode.

Sign up to request clarification or add additional context in comments.

2 Comments

Interesting. I was modifying the jdk code since I wanted to do that at runtime, probably by just adding a switch (e.g java -analyze... Classfile), what I am really trying to do is find data races. Would your proposal have a big performance impact?
@loukritios Bytecode instrumentation will be the most efficient. Interpreter-only solution (including JVM TI field watches) are all slow by definition, since they cannot benefit from JIT compilation.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.