Thread: [Sablevm-developer] types of stack frames
Brought to you by:
egagnon
From: Chris P. <chr...@ma...> - 2004-04-01 21:53:24
|
Hi Etienne (and possibly David or Greg, but not sure), Can you please write about the types of stack frames / methods that might be on the stack in SableVM, and when they are pushed and popped? For one, I would like to know about pushing and popping native methods. I wrote a simple function to count Java stack frames, but it gives me more frames than those that are pushed directly with (PREPARE_)INVOKE<X> (as calculated by a more indirect method). I assume it's because some Java frames are pushed without (PREPARE_)INVOKE<X>. I would really like to know if it's possible to count frames pushed only with (PREPARE_)INVOKE<X> by simply looking at the stack, or whether I have to go in and increment a counter on every push and keep track of pops explicitly (which gets a little complicated because of exceptions). I would like to do this so that I can make some basic assertions about post-execution system properties of programs running with spmt. I'll take whatever you write and incorporate it into David's "stack_layout.txt" document. Thanks very much, Chris ======================================================================== inline static size_t _svmf_count_java_stack_frames (_svmt_JNIEnv *env) { _svmt_stack_frame *frame = env->stack.current_frame; _svmt_method_info *method = frame->method; jint count = 0; while (method != &env->vm->stack_bottom_method) { if (_svmf_is_set_flag (method->access_flags, SVM_ACC_INTERNAL) == JNI_FALSE && _svmf_is_set_flag (method->access_flags, SVM_ACC_NATIVE) == JNI_FALSE) { count++; } frame = (_svmt_stack_frame *) (((char *) frame) - frame->previous_offset); method = frame->method; } return count; } |
From: Chris P. <chr...@ma...> - 2004-04-02 03:16:50
|
Chris Pickett wrote: > I wrote a simple function to count Java stack frames, but it gives me > more frames than those that are pushed directly with (PREPARE_)INVOKE<X> > (as calculated by a more indirect method). I assume it's because some > Java frames are pushed without (PREPARE_)INVOKE<X>. I would really like > to know if it's possible to count frames pushed only with > (PREPARE_)INVOKE<X> by simply looking at the stack It seems that I can do this if I don't count any frame that has a non-"pushed by (PREPARE_)INVOKE<X>" frame below it. The code following almost works, but appears to be off-by-one. If you can spot an obvious error I'd be grateful, otherwise I'll just keep digging (into source and specifications). Cheers, Chris inline static size_t _svmf_count_java_stack_frames (_svmt_JNIEnv *env) { _svmt_stack_frame *frame = env->stack.current_frame; _svmt_method_info *method = frame->method; jint count = 0; while (method != &env->vm->stack_bottom_method) { jboolean found_java_frame = JNI_FALSE; /* is the current frame internal? */ if (_svmf_is_set_flag (method->access_flags, SVM_ACC_INTERNAL) == JNI_FALSE) { found_java_frame = JNI_TRUE; } /* pop stack frame */ frame = (_svmt_stack_frame *) (((char *) frame) - frame->previous_offset); method = frame->method; /* if the previous frame was not internal and this one is not internal, increment the count of pushed-by-java frames */ if (_svmf_is_set_flag (method->access_flags, SVM_ACC_INTERNAL) == JNI_FALSE && found_java_frame == JNI_TRUE) { count++; } } return count; } |
From: Chris P. <chr...@ma...> - 2004-04-02 03:26:26
|
sorry for all the postings -- i promise this is the last one -- count is declared in the function as a jint, but the method returns a size_t (because all stats are size_t's) -- i just wanted to point out that this doesn't affect the result. Chris Pickett wrote: > > inline static size_t > _svmf_count_java_stack_frames (_svmt_JNIEnv *env) > > { [...] > jint count = 0; [...] > return count; > } Cheers, Chris |
From: Etienne G. <gag...@uq...> - 2004-04-02 08:12:21
|
Chris Pickett wrote: > Can you please write about the types of stack frames / methods that > might be on the stack in SableVM, and when they are pushed and popped? > For one, I would like to know about pushing and popping native methods. SableVM has 3 types of "method frames" on a thread stack: - Normal Java method frame - Native method frame - Internal method frame Both the normal Java method and native method frames are already explained in stack_layout.txt. Internal method frame ===================== When JNI_CreateJavaVM is invoked (and for each new thread), an initial stack is created. (a) It needs a bottom "method frame", yet, we are not yet executing any specific Java method. So, the frame type needs to be some non-Java frame type. We do need a frame as JNI calls need to work, and particularly local native reference must be allocatable. [local native references are stored on the stack]. (b) But, we are not executing any specific "native" method, so, the method frame cannot be of type "native method". But it should be pretty similar, as we need native locals, etc. So, SableVM adds a third type of frame: Internal method frame. An internal method frame is identified by the SVM_ACC_INTERNAL access flag. There are, in fact, 3 "subtypes" of internal method frames: - stack_bottom_method: The initial frame of the stack of a thread. - vm_initiated_call_method: Used by the VM when it calls (of its own) a Java method. (See below for motivation.) - internal_call_method: Used by the VM when executing JNI Call* instructions. stack_bottom_method ------------------- mostly self explanatory. vm_initiated_call_method ------------------------ SableVM often calls Java methods of its own. A typical example of such a call is the invocation of static initializers. A static initializer is never invoked directly from Java code, nor is it called from JNI library code. So, what's the problem? Imagine the situation: SableVM is executing some Java code and reaches a GETSTATIC instruction. A potential side effect of executing such instruction is the invocation of a static initializer ("<clinit>") *before* actually executing the real access to the static field. Yet, the current stack looks like: | | current_frame - +--------------------+ method->start_offset |local 0 | * | 1 | * | 2 | * | ... | * |local n | * env->stack. +--------------------+ * current_frame ----> | stack_frame_struct | * current frame | | * | | * +--------------------+ * | padding to align | * current_frame + +--------------------+ * _svmv_stack_offset -> | expression stack | * | | * | | * | | * | | * current_frame + +--------------------+ * end_off ---> | | If we simply put the static initializer frame after this frame, and then invoke _svmf_interpreter() to start executing "<clinit>" the RETURN instruction of the static initializer will look for its previous frame and find the "current" frame, and discover that it simply is a normal Java method frame, so it will continue bytecode interpretation at current_frame->pc. In other words, the called _svmf_interpreter() will continue executing code. | | +--------------------+ |local 0 | * | 1 | * | 2 | * | ... | * |local n | * +--------------------+ * | stack_frame_struct | * current frame | | * | | * +--------------------+ * | padding to align | * +--------------------+ * | expression stack | * | | * | | * | | * | | * +--------------------+ * |local 0 | @ | 1 | @ | 2 | @ | ... | @ |local n | @ +--------------------+ @ | stack_frame_struct | @ static initializer frame | | @ | | @ +--------------------+ @ | padding to align | @ +--------------------+ @ | expression stack | @ | | @ | | @ | | @ | | @ +--------------------+ @ But, we DON'T want normal execution to continue! We want _svmf_interpreter() to "return" so that we can resume the execution of the GETSTATIC instruction. Of course, one try to make a more complex implementation of the RETURN bytecode that would be smarter and somehow detect that the method was the bottom one of the "current" invocation of _svmf_interpreter(), and that would thus simply "return". Instead, SableVM uses a simpler strategy; it inserts a "fake" method frame (e.g. internal method frame), which has a frame->pc pointing to a INTERNAL_CALL_END (sablevm-specific) bytecode instruction. e.g. | | +--------------------+ |local 0 | * | 1 | * | 2 | * | ... | * |local n | * +--------------------+ * | stack_frame_struct | * current frame | | * | | * +--------------------+ * | padding to align | * +--------------------+ * | expression stack | * | | * | | * | | * | | * +--------------------+ * | stack_frame_struct | |(pc = INT._CALL_END)| | | | | +--------------------+ |local 0 | @ | 1 | @ | 2 | @ | ... | @ |local n | @ +--------------------+ @ | stack_frame_struct | @ static initializer frame | | @ | | @ +--------------------+ @ | padding to align | @ +--------------------+ @ | expression stack | @ | | @ | | @ | | @ | | @ +--------------------+ @ This way, when _svmf_interpreter() executes the static initializer's RETURN bytecode, it simply continues execution at the previous frame's frame->pc bytecode, which happens, in this case, to be INTERNAL_CALL_END. As you can guess, INTERNAL_CALL_END's body first removes the "internal frame", and executes a C "return" statement that terminates the _svmf_interpreter() function call. internal_call_method -------------------- It's the exact same idea as vm_initiated_call_method, but it is used when executing a JNI Call* function. We use a "different" internal frame to distinguish between "application call stack" (which includes Java methods and JNI functions), and "vm initiated" call stack. This is necessary for all sort of things like implementing SecurityManager.currentClassLoader() calls reliably. >... I'll > take whatever you write and incorporate it into David's > "stack_layout.txt" document. Thanks! Etienne -- Etienne M. Gagnon, Ph.D. http://www.info.uqam.ca/~egagnon/ SableVM: http://www.sablevm.org/ SableCC: http://www.sablecc.org/ |