In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly explains "what is the underlying principle of AsyncGetCallTrace source code". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "what is the underlying principle of AsyncGetCallTrace source code".
Preface
AsyncGetCallTrace is a function provided within OracleJDK/OpenJDK that normally gets the call stack of the current thread when the JVM does not enter the safepoint (in other words, when you use this function to get the thread stack, the JVM is not required to enter the safepoint. Entering safepoint means the occurrence of STW for OpenJDK or OracleJDK, so this means that using this function to get the thread stack does not produce STW,It's amazing.) Currently, this function is only supported on Linux X86, Solaris SPARC, and Solaris X86 systems.
In addition, it also supports asynchronous calls in the UNIX signal processor, so we just need to register a UNIX signal processor and call AsyncGetCallTrace in Handler to get the call stack of the current thread. Because the UNIX signal is randomly sent to a thread of the process for processing, it can be considered uniform to get the call stack samples of all threads.
However, it is worth noting that this function is not a standard JVM API, so there may be the following problems when using it:
Portability problem, because you can only run on OpenJDK or OracleJDK
Because it is not a standard JVMTI API, the user needs to obtain the function in some special way, which brings some inconvenience to the user, but it doesn't matter.
Source code implementation
The method of how to use this function to sample hot methods is outside the scope of this section. In Resources, there are some descriptions. If you are still unclear, you can leave me a message to communicate.
Core data structure / / call frame copied from old .h file and renamed// Fields:// 1) For Java frame (interpreted and compiled), / / lineno-bci of the method being executed or-1 if bci is not available// method_id-jmethodID of the method being executed// 2) For native method// lineno-(- 3) / / method_id-jmethodID of the method being executedtypedef struct {jint lineno / / numberline number in the source file jmethodID method_id; / / method executed in this frame} ASGCT_CallFrame;// call trace copied from old .h file and renamed// Fields:// env_id-ID of thread which executed this trace.// num_frames-number of frames in the trace.// (
< 0 indicates the frame is not walkable).// frames - the ASGCT_CallFrames that make up this trace. Callee followed by callers.typedef struct { JNIEnv *env_id; // Env where trace was recorded jint num_frames; // number of frames in this trace ASGCT_CallFrame *frames; // frames} ASGCT_CallTrace;// These name match the names reported by the forte quality kit// 这些枚举是对应到 ASGCT_CallTrace 中的 num_frames 的返回值的// 举个例子,当 JVM 在进行 GC 时,返回值中// ASGCT_CallTrace.num_frames == ticks_GC_activeenum { ticks_no_Java_frame = 0, ticks_no_class_load = -1, ticks_GC_active = -2, ticks_unknown_not_Java = -3, ticks_not_walkable_not_Java = -4, ticks_unknown_Java = -5, ticks_not_walkable_Java = -6, ticks_unknown_state = -7, ticks_thread_exit = -8, ticks_deopt = -9, ticks_safepoint = -10};函数申明// trace - trace data structure to be filled by the VM.// depth - depth of the call stack trace.// ucontext - ucontext_t of the LWPvoid AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) 该函数的调用者获取到的栈是属于某一个特定线程的,这个线程是由trace->Uniquely identified by the env_id, and the identified thread must be the same thread as the thread currently executing the AsyncGetCallTrace method. At the same time, the caller needs to allocate enough memory for trace- > frames to save the stack with a maximum stack depth of depth. If the relevant stack is obtained, JVM will automatically write the relevant stack information to trace. Next, let's look at the internal implementation from the source code.
AsyncGetCallTrace implementation
The specific analysis directly looks at the comments in the code. In order to maintain integrity, I did not delete any code of this method. Source location: / hotspot/src/share/vm/prims/forte.cpp
Void AsyncGetCallTrace (ASGCT_CallTrace * trace, jint depth, void* ucontext) {JavaThread* thread; / / 1. First of all, determine whether the jniEnv is empty, or whether the thread corresponding to jniEnv is valid, or whether the thread has exited, / / any condition is met Return ticks_thread_exit (corresponding to enumerated types in the core data structure) if (trace- > env_id = = NULL | | (thread = JavaThread::thread_from_jni_environment (trace- > env_id)) = = NULL | | thread- > is_exiting () {/ / bad env_id, thread has exited or thread is exiting trace- > num_frames = ticks_thread_exit / /-8 return;} if (thread- > in_deopt_handler ()) {/ / thread is in the deoptimization handler so return no frames trace- > num_frames = ticks_deopt; / /-9 return;} / / 2. Here we assert whether the thread referred to by jniEnv is the current thread. If it is not equal, we will directly report assert (JavaThread::current () = = thread, "AsyncGetCallTrace must be called by the current interrupted thread"); / / 3. The JVMTI_EVENT_CLASS_LOAD event must be enable, otherwise it will directly return ticks_no_class_load if (! JvmtiExport::should_post_class_load ()) {trace- > num_frames = ticks_no_class_load. / /-1 return;} / / 4. The current heap must not perform GC, otherwise it will directly return ticks_GC_active if (Universe::heap ()-> is_gc_active ()) {trace- > num_frames = ticks_GC_active; / /-2 return;} / / 5. Get the corresponding thread stack according to the current state of the thread Thread stack crawling switch (thread- > thread_state ()) {case _ thread_new: case _ thread_uninitialized: case _ thread_new_trans: / / We found the thread on the threads list above, but it is too / / young to be useful so return that there are no Java frames is performed only when the state of the thread is _ thread_in_vm/_thread_in_vm_trans / / and _ thread_in_Java/_thread_in_Java_trans. Trace- > num_frames = 0; break; case _ thread_in_native: case _ thread_in_native_trans: case _ thread_blocked: case _ thread_blocked_trans: case _ thread_in_vm: case _ thread_in_vm_trans: {frame fr / / first get the top stack frame of the current thread / / param isInJava = = false-indicate we aren't in Java code if (! thread- > pd_get_top_frame_for_signal_handler (& fr, ucontext, false)) {trace- > num_frames = ticks_unknown_not_Java / /-3 unknown frame} else {/ / if the thread does not have any Java stack frames, it directly returns 0 frames if (! thread- > has_last_Java_frame ()) {trace- > num_frames = 0; / / No Java frames} else {trace- > num_frames = ticks_not_walkable_not_Java / /-4 non walkable frame by default / / if there is a valid stack frame, fill the frames and num_frames forte_fill_call_trace_given_top (thread, trace, depth, fr) in trace;.}} break; case _ thread_in_Java: case _ thread_in_Java_trans: {frame fr / / param isInJava = = true-indicate we are in Java code if (! thread- > pd_get_top_frame_for_signal_handler (& fr, ucontext, true)) {trace- > num_frames = ticks_unknown_Java; / /-5 unknown frame} else {trace- > num_frames = ticks_not_walkable_Java; / /-6, non walkable frame by default forte_fill_call_trace_given_top (thread, trace, depth, fr) } break; default: / / Unknown thread state trace- > num_frames = ticks_unknown_state; / /-7 break;}}
From the above analysis, the final place to get the thread stack is mainly in these two methods pd_get_top_frame_for_signal_handler and forte_fill_call_trace_given_top, let's take a look at the implementation of these two methods.
Pd_get_top_frame_for_signal_handler implementation
It is not difficult to see from the method name that the main function of this method is to get the top frame of the stack of the current thread. Followed by a signal_handler, the initial idea, I guess, is to respond to the SIGPROF signal under UNIX. Because that's what AsyncGetCallTrace is made for. Source code location / hotspot/src/os_cpu/linux_x86/vm/thread_linux_x86.cpp of the method
Bool JavaThread::pd_get_top_frame_for_signal_handler (frame* fr_addr, void* ucontext, bool isInJava) {assert (Thread::current () = = this, "caller must be current thread"); return pd_get_top_frame (fr_addr, ucontext, isInJava);}
It is very simple to determine whether it is the current thread, as for the isInJava input parameter is related to the state of the current thread, if it is running in the java code, it is true, otherwise it is false.
Pd_get_top_frame implements bool JavaThread::pd_get_top_frame (frame* fr_addr, void* ucontext, bool isInJava) {assert (this- > is_Java_thread (), "must be JavaThread"); JavaThread* jt = (JavaThread*) this; / / If we have a last_Java_frame, then we should use it even if / / isInJava = = true. It should be more reliable than ucontext info. If (jt- > has_last_Java_frame () & & jt- > frame_anchor ()-> walkable ()) {* fr_addr = jt- > pd_last_frame (); return true;} / / At this point, we don't have a last_Java_frame, so / / we try to glean some information out of the ucontext / / if we were running Java code when SIGPROF came in. If (isInJava) {ucontext_t* uc = (ucontext_t*) ucontext; intptr_t* ret_fp; intptr_t* ret_sp; ExtendedPC addr = os::Linux::fetch_frame_from_ucontext (this, uc, & ret_sp, & ret_fp); if (addr.pc () = = NULL | | ret_sp = = NULL) {/ / ucontext wasn't useful return false } frame ret_frame (ret_sp, ret_fp, addr.pc ()); if (! ret_frame.safe_for_sender (jt)) {# ifdef COMPILER2 / / C2 uses ebp as a general register see if NULL fp helps frame ret_frame2 (ret_sp, NULL, addr.pc (); if (! ret_frame2.safe_for_sender (jt)) {/ / nothing else to try if the frame isn't good return false } ret_frame = ret_frame2;#else / / nothing else to try if the frame isn't good return false;#endif / * COMPILER2 * /} * fr_addr = ret_frame; return true;} / / nothing else to try return false;}
In fact, take the function of the top frame of the stack, because the source code of the function is longer, I will briefly describe the logic.
The current thread can only be a java thread
Determine whether the top frame of the stack exists, and the current stack is walkable. If both are satisfied, return the pd_last_frame of javaThread, that is, the top frame of the stack, and end; otherwise, continue.
If the current thread is running java code, then we try to collect some information we need in the ucontext_t, such as stack frames
Forte_fill_call_trace_given_top implementation
When we get the frame at the top of the stack, it makes sense. As long as we start from the top of the stack and walk through the entire stack, we can get all the methods, and save the obtained results to ASGCT_CallTrace, source location: / hotspot/src/share/vm/prims/forte.cpp.
Static void forte_fill_call_trace_given_top (JavaThread* thd, ASGCT_CallTrace* trace, int depth, frame top_frame) {NoHandleMark nhm; frame initial_Java_frame; Method* method; int bci =-1 / / assume BCI is not available for method / / update with correct information if available int count; count = 0; assert (trace- > frames! = NULL, "trace- > frames must be non-NULL"); / / 1. Get the first java stack frame / / Walk the stack starting from 'top_frame' and search for an initial Java frame at the top of the stack. Find_initial_Java_frame (thd, & top_frame, & initial_Java_frame, & method, & bci); / / Check if a Java Method has been found. If (method = = NULL) return; / / 2. If it is not a legal method, directly return if (! method- > is_valid_method ()) {trace- > num_frames = ticks_GC_active; / /-2 return;} vframeStreamForte st (thd, initial_Java_frame, false); / / iterate through all stack frames on the stack, and get each method bci and method id one by one. Here the maximum stack depth depth for passed from the outside will be used. ! st.at_end () & & count
< depth; st.forte_next(), count++) { bci = st.bci(); method = st.method(); if (!method->Is_valid_method () {/ / we throw away everything we've gathered in this sample since / / none of it is safe trace- > num_frames = ticks_GC_active; / /-2 return;} / / get the method id according to the method object. If the method id has not been generated at this time, return NULL trace- > frame [count] .method _ id = method- > find_jmethod_id_or_null () / / if the method is not a native method, set lineno to the value of bci, otherwise set-3 if (! method- > is_native ()) {trace- > frames.lineno = bci;} else {trace- > frames.lineno =-3;} trace- > num_frames = count; return } Thank you for reading, the above is the content of "what is the underlying principle of AsyncGetCallTrace source code". After the study of this article, I believe you have a deeper understanding of what the underlying principle of AsyncGetCallTrace source code is, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.