< prev index next >

src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp

Print this page

  1 /*
  2  * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #include "classfile/javaThreadStatus.hpp"
 26 #include "code/codeCache.inline.hpp"
 27 #include "code/debugInfoRec.hpp"
 28 #include "code/nmethod.hpp"

 29 #include "interpreter/interpreter.hpp"
 30 #include "jfr/jfrEvents.hpp"
 31 #include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp"
 32 #include "jfr/periodic/sampling/jfrSampleMonitor.hpp"
 33 #include "jfr/periodic/sampling/jfrSampleRequest.hpp"
 34 #include "jfr/periodic/sampling/jfrThreadSampling.hpp"
 35 #include "jfr/recorder/stacktrace/jfrStackTrace.hpp"
 36 #include "jfr/utilities/jfrTypes.hpp"
 37 #include "memory/resourceArea.hpp"
 38 #include "oops/method.hpp"
 39 #include "runtime/continuation.hpp"
 40 #include "runtime/frame.inline.hpp"
 41 #include "runtime/javaThread.inline.hpp"
 42 #include "runtime/stackFrameStream.inline.hpp"
 43 
 44 template <typename EventType>
 45 static inline void send_sample_event(const JfrTicks& start_time, const JfrTicks& end_time, traceid sid, traceid tid) {
 46   EventType event(UNTIMED);
 47   event.set_starttime(start_time);
 48   event.set_endtime(end_time);

131 
132   // Step to sender.
133   stream.next();
134 
135   // If the top frame is in a continuation, check that the sender frame is too.
136   if (in_continuation && !is_in_continuation(*stream.current(), jt)) {
137     // Leave sender frame empty.
138     return true;
139   }
140 
141   sender_frame = *stream.current();
142 
143   assert(request._sample_pc != nullptr, "invariant");
144   assert(request._sample_bcp != nullptr, "invariant");
145   assert(Method::is_valid_method(static_cast<const Method*>(request._sample_pc)), "invariant");
146   assert(static_cast<const Method*>(request._sample_pc)->is_native() ||
147          static_cast<const Method*>(request._sample_pc)->contains(static_cast<address>(request._sample_bcp)), "invariant");
148   return true;
149 }
150 
151 static inline const PcDesc* get_pc_desc(nmethod* nm, void* pc) {
152   assert(nm != nullptr, "invariant");
153   assert(pc != nullptr, "invariant");
154   return nm->pc_desc_near(static_cast<address>(pc));
155 }
156 
157 static inline bool is_valid(const PcDesc* pc_desc) {
158   return pc_desc != nullptr && pc_desc->scope_decode_offset() != DebugInformationRecorder::serialized_null;
159 }
160 
161 static bool compute_top_frame(const JfrSampleRequest& request, frame& top_frame, bool& in_continuation, JavaThread* jt, bool& biased) {
162   assert(jt != nullptr, "invariant");
163 
164   if (!jt->has_last_Java_frame()) {
165     return false;
166   }
167 
168   if (is_interpreter(request)) {
169     return compute_sender_frame(const_cast<JfrSampleRequest&>(request), top_frame, in_continuation, jt);
170   }
171 

192   if (stream.current()->is_safepoint_blob_frame()) {
193     if (sampled_nm != nullptr) {
194       // Move to the physical sender frame of the SafepointBlob stub frame using the frame size, not the logical iterator.
195       const int safepoint_blob_stub_frame_size = stream.current()->cb()->frame_size();
196       intptr_t* const sender_sp = stream.current()->unextended_sp() + safepoint_blob_stub_frame_size;
197       if (sender_sp > sampled_sp) {
198         const address saved_exception_pc = jt->saved_exception_pc();
199         assert(saved_exception_pc != nullptr, "invariant");
200         const nmethod* const exception_nm = CodeCache::find_blob(saved_exception_pc)->as_nmethod();
201         assert(exception_nm != nullptr, "invariant");
202         if (exception_nm == sampled_nm && sampled_nm->is_at_poll_return(saved_exception_pc)) {
203           // We sit at the poll return site in the sampled compiled nmethod with only the return address on the stack.
204           // The sampled_nm compiled frame is no longer extant, but we might be able to reconstruct a synthetic
205           // compiled frame at this location. We do this by overlaying a reconstructed frame on top of
206           // the huge SafepointBlob stub frame. Of course, the synthetic frame only contains random stack memory,
207           // but it is safe because stack walking cares only about the form of the frame (i.e., an sp and a pc).
208           // We also do not have to worry about stackbanging because we currently have a huge SafepointBlob stub frame
209           // on the stack. For extra assurance, we know that we can create this frame size at this
210           // very location because we just popped such a frame before we hit the return poll site.
211           //
212           // Let's attempt to correct for the safepoint bias.
213           const PcDesc* const pc_desc = get_pc_desc(sampled_nm, sampled_pc);








214           if (is_valid(pc_desc)) {
215             intptr_t* const synthetic_sp = sender_sp - sampled_nm->frame_size();


















216             intptr_t* const synthetic_fp = sender_sp AARCH64_ONLY( - frame::sender_sp_offset);
217             top_frame = frame(synthetic_sp, synthetic_sp, synthetic_fp, pc_desc->real_pc(sampled_nm), sampled_nm);
218             in_continuation = is_in_continuation(top_frame, jt);
219             return true;
220           }
221         }
222       }
223     }
224     stream.next(); // skip the SafepointBlob stub frame
225   }
226 
227   assert(!stream.current()->is_safepoint_blob_frame(), "invariant");
228 
229   biased = true;
230 
231   // Search the first frame that is above the sampled sp.
232   for (; !stream.is_done(); stream.next()) {
233     frame* const current = stream.current();
234 
235     if (current->real_fp() <= sampled_sp) {
236       // Continue searching for a matching frame.
237       continue;
238     }

  1 /*
  2  * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #include "classfile/javaThreadStatus.hpp"
 26 #include "code/codeCache.inline.hpp"
 27 #include "code/debugInfoRec.hpp"
 28 #include "code/nmethod.hpp"
 29 #include "code/pcDesc.hpp"
 30 #include "interpreter/interpreter.hpp"
 31 #include "jfr/jfrEvents.hpp"
 32 #include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp"
 33 #include "jfr/periodic/sampling/jfrSampleMonitor.hpp"
 34 #include "jfr/periodic/sampling/jfrSampleRequest.hpp"
 35 #include "jfr/periodic/sampling/jfrThreadSampling.hpp"
 36 #include "jfr/recorder/stacktrace/jfrStackTrace.hpp"
 37 #include "jfr/utilities/jfrTypes.hpp"
 38 #include "memory/resourceArea.hpp"
 39 #include "oops/method.hpp"
 40 #include "runtime/continuation.hpp"
 41 #include "runtime/frame.inline.hpp"
 42 #include "runtime/javaThread.inline.hpp"
 43 #include "runtime/stackFrameStream.inline.hpp"
 44 
 45 template <typename EventType>
 46 static inline void send_sample_event(const JfrTicks& start_time, const JfrTicks& end_time, traceid sid, traceid tid) {
 47   EventType event(UNTIMED);
 48   event.set_starttime(start_time);
 49   event.set_endtime(end_time);

132 
133   // Step to sender.
134   stream.next();
135 
136   // If the top frame is in a continuation, check that the sender frame is too.
137   if (in_continuation && !is_in_continuation(*stream.current(), jt)) {
138     // Leave sender frame empty.
139     return true;
140   }
141 
142   sender_frame = *stream.current();
143 
144   assert(request._sample_pc != nullptr, "invariant");
145   assert(request._sample_bcp != nullptr, "invariant");
146   assert(Method::is_valid_method(static_cast<const Method*>(request._sample_pc)), "invariant");
147   assert(static_cast<const Method*>(request._sample_pc)->is_native() ||
148          static_cast<const Method*>(request._sample_pc)->contains(static_cast<address>(request._sample_bcp)), "invariant");
149   return true;
150 }
151 
152 static inline PcDesc* get_pc_desc(nmethod* nm, void* pc) {
153   assert(nm != nullptr, "invariant");
154   assert(pc != nullptr, "invariant");
155   return nm->pc_desc_near(static_cast<address>(pc));
156 }
157 
158 static inline bool is_valid(const PcDesc* pc_desc) {
159   return pc_desc != nullptr && pc_desc->scope_decode_offset() != DebugInformationRecorder::serialized_null;
160 }
161 
162 static bool compute_top_frame(const JfrSampleRequest& request, frame& top_frame, bool& in_continuation, JavaThread* jt, bool& biased) {
163   assert(jt != nullptr, "invariant");
164 
165   if (!jt->has_last_Java_frame()) {
166     return false;
167   }
168 
169   if (is_interpreter(request)) {
170     return compute_sender_frame(const_cast<JfrSampleRequest&>(request), top_frame, in_continuation, jt);
171   }
172 

193   if (stream.current()->is_safepoint_blob_frame()) {
194     if (sampled_nm != nullptr) {
195       // Move to the physical sender frame of the SafepointBlob stub frame using the frame size, not the logical iterator.
196       const int safepoint_blob_stub_frame_size = stream.current()->cb()->frame_size();
197       intptr_t* const sender_sp = stream.current()->unextended_sp() + safepoint_blob_stub_frame_size;
198       if (sender_sp > sampled_sp) {
199         const address saved_exception_pc = jt->saved_exception_pc();
200         assert(saved_exception_pc != nullptr, "invariant");
201         const nmethod* const exception_nm = CodeCache::find_blob(saved_exception_pc)->as_nmethod();
202         assert(exception_nm != nullptr, "invariant");
203         if (exception_nm == sampled_nm && sampled_nm->is_at_poll_return(saved_exception_pc)) {
204           // We sit at the poll return site in the sampled compiled nmethod with only the return address on the stack.
205           // The sampled_nm compiled frame is no longer extant, but we might be able to reconstruct a synthetic
206           // compiled frame at this location. We do this by overlaying a reconstructed frame on top of
207           // the huge SafepointBlob stub frame. Of course, the synthetic frame only contains random stack memory,
208           // but it is safe because stack walking cares only about the form of the frame (i.e., an sp and a pc).
209           // We also do not have to worry about stackbanging because we currently have a huge SafepointBlob stub frame
210           // on the stack. For extra assurance, we know that we can create this frame size at this
211           // very location because we just popped such a frame before we hit the return poll site.
212           //
213           // For frames that need stack repair, special care is needed. This is because the general stack-walking code
214           // reads the frame size from the stack, but here that memory is already overwritten by the SafepointBlob.
215           // If we are careful, we don't need to reconstruct a frame that needs stack repair, because we can process
216           // the nmethod directly, unpacking it in the first part of the stack trace.
217           // To accomplish this, we must provide both the PcDesc and the nmethod to the stack-walking code,
218           // which is done by updating the JfrSampleRequest. A special marker, NEEDS_STACK_REPAIR, is set in the bcp field.
219           // In this case, the top_frame becomes the sender frame of the nmethod, similar to how interpreter frames are handled.
220           //
221           // Let's attempt to correct for the safepoint bias
222           PcDesc* const pc_desc = get_pc_desc(sampled_nm, sampled_pc);
223           if (is_valid(pc_desc)) {
224             intptr_t* const synthetic_sp = sender_sp - sampled_nm->frame_size();
225             in_continuation = Continuation::get_continuation_entry_for_sp(jt, synthetic_sp) != nullptr;
226             if (sampled_nm->needs_stack_repair()) {
227               JfrSampleRequest& modified_request = const_cast<JfrSampleRequest&>(request);
228               modified_request._sample_pc = pc_desc;
229               modified_request._sample_sp = sampled_nm;
230               modified_request._sample_bcp = reinterpret_cast<address>(JfrSampleRequestFrameType::NEEDS_STACK_REPAIR);
231               if (!stream.is_done()) {
232                 stream.next();
233                 // If the needs stack repair frame is in a continuation, check that the sender frame is too.
234                 if (in_continuation && !is_in_continuation(*stream.current(), jt)) {
235                   // Leave sender frame empty.
236                   return true;
237                 }
238                 // The top_frame becomes the sender of the nmethod that needs stack repair.
239                 top_frame = *stream.current();
240               }
241               return true;
242             }
243             intptr_t* const synthetic_fp = sender_sp AARCH64_ONLY( - frame::sender_sp_offset);
244             top_frame = frame(synthetic_sp, synthetic_sp, synthetic_fp, pc_desc->real_pc(sampled_nm), sampled_nm);

245             return true;
246           }
247         }
248       }
249     }
250     stream.next(); // skip the SafepointBlob stub frame
251   }
252 
253   assert(!stream.current()->is_safepoint_blob_frame(), "invariant");
254 
255   biased = true;
256 
257   // Search the first frame that is above the sampled sp.
258   for (; !stream.is_done(); stream.next()) {
259     frame* const current = stream.current();
260 
261     if (current->real_fp() <= sampled_sp) {
262       // Continue searching for a matching frame.
263       continue;
264     }
< prev index next >