Skip to content

Commit 0049066

Browse files
committed
Can walk the stack, but no func names yet
1 parent bc65357 commit 0049066

File tree

1 file changed

+85
-6
lines changed

1 file changed

+85
-6
lines changed

ext/libdatadog_api/crashtracker.c

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -184,16 +184,95 @@ static void ruby_runtime_stack_callback(
184184
// Only using emit_frame - ignore emit_stacktrace_string parameter
185185
(void)emit_stacktrace_string;
186186

187-
// Use the simplest possible implementation that we know works
188-
// This avoids any risky Ruby VM API calls that might cause hangs
189-
ddog_crasht_RuntimeStackFrame frame = {
190-
.function_name = "ruby_runtime_callback",
187+
// Try to safely walk Ruby stack using direct VM structure access
188+
// Avoid Ruby API calls that might hang in crash context
189+
190+
// First emit a marker frame to show callback is working
191+
ddog_crasht_RuntimeStackFrame marker_frame = {
192+
.function_name = "ruby_stack_walker_start",
191193
.file_name = "crashtracker.c",
192-
.line_number = 42,
194+
.line_number = 1,
193195
.column_number = 0
194196
};
197+
emit_frame(&marker_frame);
198+
199+
// Try to get current thread and execution context safely
200+
VALUE current_thread = rb_thread_current();
201+
if (current_thread == Qnil) return;
195202

196-
emit_frame(&frame);
203+
// Get thread struct carefully
204+
static const rb_data_type_t *thread_data_type = NULL;
205+
if (thread_data_type == NULL) {
206+
thread_data_type = RTYPEDDATA_TYPE(current_thread);
207+
if (!thread_data_type) return;
208+
}
209+
210+
rb_thread_t *th = (rb_thread_t *) rb_check_typeddata(current_thread, thread_data_type);
211+
if (!th) return;
212+
213+
const rb_execution_context_t *ec = th->ec;
214+
if (!ec) return;
215+
216+
// Safety checks
217+
if (th->status == THREAD_KILLED) return;
218+
if (!ec->vm_stack || ec->vm_stack_size == 0) return;
219+
220+
const rb_control_frame_t *cfp = ec->cfp;
221+
const rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
222+
223+
if (!cfp || !end_cfp) return;
224+
225+
// Skip dummy frames
226+
end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
227+
if (end_cfp <= cfp) return;
228+
229+
// Walk stack frames with extreme caution
230+
int frame_count = 0;
231+
const int MAX_FRAMES = 10; // Keep very low to minimize crash risk
232+
233+
for (; cfp != RUBY_VM_NEXT_CONTROL_FRAME(end_cfp) && frame_count < MAX_FRAMES;
234+
cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
235+
236+
if (VM_FRAME_RUBYFRAME_P(cfp) && cfp->iseq) {
237+
// Instead of calling rb_iseq_* functions, work directly with iseq struct
238+
const rb_iseq_t *iseq = cfp->iseq;
239+
240+
// Basic frame info without risky API calls
241+
char frame_desc[64];
242+
snprintf(frame_desc, sizeof(frame_desc), "ruby_frame_%d", frame_count);
243+
244+
// Try to get basic line info safely using direct struct access
245+
int line_no = 0;
246+
if (iseq && cfp->pc) {
247+
// Use direct access to iseq body instead of helper functions
248+
if (iseq->body && iseq->body->iseq_encoded && iseq->body->iseq_size > 0) {
249+
ptrdiff_t pc_offset = cfp->pc - iseq->body->iseq_encoded;
250+
if (pc_offset >= 0 && pc_offset < iseq->body->iseq_size) {
251+
line_no = frame_count + 1; // Use frame position as approximation
252+
}
253+
}
254+
}
255+
256+
ddog_crasht_RuntimeStackFrame frame = {
257+
.function_name = frame_desc,
258+
.file_name = "ruby_source.rb",
259+
.line_number = line_no,
260+
.column_number = 0
261+
};
262+
263+
emit_frame(&frame);
264+
frame_count++;
265+
}
266+
}
267+
268+
// Emit end marker
269+
ddog_crasht_RuntimeStackFrame end_frame = {
270+
.function_name = "ruby_stack_walker_end",
271+
.file_name = "crashtracker.c",
272+
.line_number = frame_count,
273+
.column_number = 0
274+
};
275+
emit_frame(&end_frame);
197276
}
198277

199278
static VALUE _native_register_runtime_stack_callback(DDTRACE_UNUSED VALUE _self, VALUE callback_type) {

0 commit comments

Comments
 (0)