Skip to content

Commit d0950d3

Browse files
committed
fix the broken test
1 parent ab31d6b commit d0950d3

File tree

1 file changed

+34
-32
lines changed

1 file changed

+34
-32
lines changed

ddtrace/profiling/collector/_memalloc_tb.cpp

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -243,76 +243,78 @@ push_threadinfo_to_sample(Datadog::Sample& sample)
243243
// Error will be restored automatically by error_restorer destructor
244244
}
245245

246-
/* Helper function to extract code object and line number from a PyFrameObject */
246+
/* Helper function to extract frame info from PyFrameObject and push to sample */
247247
static void
248-
extract_frame_info_from_pyframe(PyFrameObject* frame, PyCodeObject** code_out, int* lineno_out)
248+
push_pyframe_to_sample(Datadog::Sample& sample, PyFrameObject* frame)
249249
{
250-
*code_out = NULL;
251-
*lineno_out = 0;
252-
250+
// Get line number
253251
int lineno_val = PyFrame_GetLineNumber(frame);
254252
if (lineno_val < 0)
255253
lineno_val = 0;
256254

255+
// Get code object
257256
#ifdef _PY39_AND_LATER
258257
PyCodeObject* code = PyFrame_GetCode(frame);
259258
#else
260259
PyCodeObject* code = frame->f_code;
261260
#endif
262261

263-
*code_out = code;
264-
*lineno_out = lineno_val;
265-
}
262+
std::string_view name_sv = "<unknown>";
263+
std::string_view filename_sv = "<unknown>";
266264

267-
/* Helper function to push frame info to sample */
268-
static void
269-
push_frame_to_sample(Datadog::Sample& sample, PyCodeObject* code, int lineno_val)
270-
{
271-
// Extract frame info for Sample using helper function
265+
if (code != NULL) {
266+
// Extract function name (use co_qualname for Python 3.11+ for better context)
272267
#if defined(_PY311_AND_LATER)
273-
// Python 3.11+ has co_qualname which provides fully qualified names (e.g., "MyClass.method")
274-
PyObject* name_obj = code ? (code->co_qualname ? code->co_qualname : code->co_name) : nullptr;
268+
PyObject* name_obj = code->co_qualname ? code->co_qualname : code->co_name;
275269
#else
276-
PyObject* name_obj = code ? code->co_name : nullptr;
270+
PyObject* name_obj = code->co_name;
277271
#endif
278-
std::string_view name_sv = unicode_to_string_view(name_obj);
279-
std::string_view filename_sv = code ? unicode_to_string_view(code->co_filename) : "<unknown>";
272+
name_sv = unicode_to_string_view(name_obj);
273+
filename_sv = unicode_to_string_view(code->co_filename);
274+
}
280275

281276
// Push frame to Sample (root to leaf order)
282277
// push_frame copies the strings immediately into its StringArena
283278
sample.push_frame(name_sv, filename_sv, 0, lineno_val);
279+
280+
#ifdef _PY39_AND_LATER
281+
Py_XDECREF(code);
282+
#endif
284283
}
285284

286285
/* Helper function to collect frames from PyFrameObject chain and push to sample */
287286
static void
288287
push_stacktrace_to_sample(Datadog::Sample& sample)
289288
{
290289
PyThreadState* tstate = PyThreadState_Get();
291-
if (tstate == NULL)
290+
if (tstate == NULL) {
291+
// Push a placeholder frame when thread state is unavailable
292+
sample.push_frame("<no thread state>", "<unknown>", 0, 0);
292293
return;
294+
}
293295

294296
#ifdef _PY39_AND_LATER
295297
PyFrameObject* pyframe = PyThreadState_GetFrame(tstate);
296298
#else
297299
PyFrameObject* pyframe = tstate->frame;
298300
#endif
299301

300-
if (pyframe == NULL)
302+
if (pyframe == NULL) {
303+
// No Python frames available (e.g., during thread initialization/cleanup in "Dummy" threads).
304+
// This occurs in Python 3.10-3.12 but not in 3.13+ due to threading implementation changes.
305+
//
306+
// The previous implementation (before dd_wrapper Sample refactor) dropped these samples entirely
307+
// by returning NULL from traceback_new(). This new approach is strictly better: we still capture
308+
// allocation metrics and make it explicit in profiles that the stack wasn't available.
309+
//
310+
// TODO(profiling): Investigate if there's a way to capture C-level stack traces or other context
311+
// when Python frames aren't available during thread initialization/cleanup.
312+
sample.push_frame("<no Python frames>", "<unknown>", 0, 0);
301313
return;
314+
}
302315

303316
for (PyFrameObject* frame = pyframe; frame != NULL;) {
304-
PyCodeObject* code = NULL;
305-
int lineno_val = 0;
306-
307-
extract_frame_info_from_pyframe(frame, &code, &lineno_val);
308-
309-
if (code != NULL) {
310-
push_frame_to_sample(sample, code, lineno_val);
311-
}
312-
313-
#ifdef _PY39_AND_LATER
314-
Py_XDECREF(code);
315-
#endif
317+
push_pyframe_to_sample(sample, frame);
316318

317319
#ifdef _PY39_AND_LATER
318320
PyFrameObject* back = PyFrame_GetBack(frame);

0 commit comments

Comments
 (0)