@@ -235,6 +235,30 @@ _py_proc__infer_python_version(py_proc_t * self) {
235
235
236
236
int major = 0 , minor = 0 , patch = 0 ;
237
237
238
+ // Starting with Python 3.13 we can use the PyRuntime structure
239
+ if (isvalid (self -> symbols [DYNSYM_RUNTIME ])) {
240
+ _Py_DebugOffsets py_d ;
241
+ if (fail (py_proc__get_type (self , self -> symbols [DYNSYM_RUNTIME ], py_d ))) {
242
+ log_e ("Cannot copy PyRuntimeState structure from remote address" );
243
+ FAIL ;
244
+ }
245
+
246
+ if (0 == memcmp (py_d .v3_13 .cookie , _Py_Debug_Cookie , sizeof (py_d .v3_13 .cookie ))) {
247
+ uint64_t version = py_d .v3_13 .version ;
248
+ major = (version >>24 ) & 0xFF ;
249
+ minor = (version >>16 ) & 0xFF ;
250
+ patch = (version >>8 ) & 0xFF ;
251
+
252
+ log_d ("Python version (from debug offsets): %d.%d.%d" , major , minor , patch );
253
+
254
+ self -> py_v = get_version_descriptor (major , minor , patch );
255
+
256
+ init_version_descriptor (self -> py_v , & py_d );
257
+
258
+ SUCCESS ;
259
+ }
260
+ }
261
+
238
262
// Starting with Python 3.11 we can rely on the Py_Version symbol
239
263
if (isvalid (self -> symbols [DYNSYM_HEX_VERSION ])) {
240
264
unsigned long py_version = 0 ;
@@ -322,17 +346,14 @@ _py_proc__check_interp_state(py_proc_t * self, void * raddr) {
322
346
323
347
V_DESC (self -> py_v );
324
348
325
- PyInterpreterState is ;
326
- PyThreadState tstate_head ;
327
-
328
- if (py_proc__get_type (self , raddr , is )) {
349
+ if (py_proc__copy_v (self , is , raddr , self -> is )) {
329
350
log_ie ("Cannot get remote interpreter state" );
330
351
FAIL ;
331
352
}
353
+ log_d ("Interpreter state buffer %p" , self -> is );
354
+ void * tstate_head_addr = V_FIELD_PTR (void * , self -> is , py_is , o_tstate_head );
332
355
333
- void * tstate_head_addr = V_FIELD (void * , is , py_is , o_tstate_head );
334
-
335
- if (fail (py_proc__get_type (self , tstate_head_addr , tstate_head ))) {
356
+ if (fail (py_proc__copy_v (self , thread , tstate_head_addr , self -> ts ))) {
336
357
log_e (
337
358
"Cannot copy PyThreadState head at %p from PyInterpreterState instance" ,
338
359
tstate_head_addr
@@ -342,7 +363,7 @@ _py_proc__check_interp_state(py_proc_t * self, void * raddr) {
342
363
343
364
log_t ("PyThreadState head loaded @ %p" , V_FIELD (void * , is , py_is , o_tstate_head ));
344
365
345
- if (V_FIELD (void * , tstate_head , py_thread , o_interp ) != raddr ) {
366
+ if (V_FIELD_PTR (void * , self -> ts , py_thread , o_interp ) != raddr ) {
346
367
log_d ("PyThreadState head does not point to interpreter state" );
347
368
set_error (EPROC );
348
369
FAIL ;
@@ -358,7 +379,7 @@ _py_proc__check_interp_state(py_proc_t * self, void * raddr) {
358
379
raddr , V_FIELD (void * , is , py_is , o_tstate_head )
359
380
);
360
381
361
- raddr_t thread_raddr = {self -> proc_ref , V_FIELD (void * , is , py_is , o_tstate_head )};
382
+ raddr_t thread_raddr = {self -> proc_ref , V_FIELD_PTR (void * , self -> is , py_is , o_tstate_head )};
362
383
py_thread_t thread ;
363
384
364
385
if (fail (py_thread__fill_from_raddr (& thread , & thread_raddr , self ))) {
@@ -489,7 +510,6 @@ _py_proc__deref_interp_head(py_proc_t * self) {
489
510
490
511
void * interp_head_raddr = NULL ;
491
512
492
- _PyRuntimeState py_runtime ;
493
513
void * runtime_addr = self -> symbols [DYNSYM_RUNTIME ];
494
514
#if defined PL_LINUX
495
515
const size_t size = getpagesize ();
@@ -510,15 +530,15 @@ _py_proc__deref_interp_head(py_proc_t * self) {
510
530
#endif
511
531
512
532
for (void * current_addr = lower ; current_addr <= upper ; current_addr += sizeof (void * )) {
513
- if (py_proc__get_type (self , current_addr , py_runtime )) {
533
+ if (py_proc__copy_v (self , runtime , current_addr , self -> rs )) {
514
534
log_d (
515
535
"Cannot copy runtime state structure from remote address %p" ,
516
536
current_addr
517
537
);
518
538
continue ;
519
539
}
520
540
521
- interp_head_raddr = V_FIELD (void * , py_runtime , py_runtime , o_interp_head );
541
+ interp_head_raddr = V_FIELD_PTR (void * , self -> rs , py_runtime , o_interp_head );
522
542
if (V_MAX (3 , 8 )) {
523
543
self -> gc_state_raddr = current_addr + py_v -> py_runtime .o_gc ;
524
544
log_d ("GC runtime state @ %p" , self -> gc_state_raddr );
@@ -560,6 +580,46 @@ _py_proc__get_current_thread_state_raddr(py_proc_t * self) {
560
580
return (void * ) -1 ;
561
581
}
562
582
583
+ // ----------------------------------------------------------------------------
584
+ static void
585
+ _py_proc__free_local_buffers (py_proc_t * self ) {
586
+ sfree (self -> is );
587
+ sfree (self -> ts );
588
+ sfree (self -> rs );
589
+ }
590
+
591
+ // ----------------------------------------------------------------------------
592
+ #define LOCAL_ALLOC (dest , src , name ) { \
593
+ self->dest = calloc(1, self->py_v->py_##src.size); \
594
+ if (!isvalid(self->dest)) { \
595
+ log_e("Cannot allocate memory for " #name); \
596
+ goto error; \
597
+ } \
598
+ }
599
+
600
+ static int
601
+ _py_proc__init_local_buffers (py_proc_t * self ) {
602
+ if (!isvalid (self )) {
603
+ set_error (EPROC );
604
+ FAIL ;
605
+ }
606
+
607
+ LOCAL_ALLOC (rs , runtime , "PyRuntimeState" );
608
+ LOCAL_ALLOC (is , is , "PyInterpreterState" );
609
+ LOCAL_ALLOC (ts , thread , "PyThreadState" );
610
+
611
+ log_d ("Local buffers initialised" );
612
+
613
+ SUCCESS ;
614
+
615
+ error :
616
+ set_error (ENOMEM );
617
+
618
+ _py_proc__free_local_buffers (self );
619
+
620
+ FAIL ;
621
+ }
622
+
563
623
// ----------------------------------------------------------------------------
564
624
static int
565
625
_py_proc__find_interpreter_state (py_proc_t * self ) {
@@ -575,6 +635,9 @@ _py_proc__find_interpreter_state(py_proc_t * self) {
575
635
if (fail (_py_proc__infer_python_version (self )))
576
636
FAIL ;
577
637
638
+ if (fail (_py_proc__init_local_buffers (self )))
639
+ FAIL ;
640
+
578
641
if (self -> sym_loaded || isvalid (self -> map .runtime .base )) {
579
642
// Try to resolve the symbols or the runtime section, if we have them
580
643
@@ -703,6 +766,11 @@ py_proc_new(int child) {
703
766
704
767
py_proc -> child = child ;
705
768
py_proc -> gc_state_raddr = NULL ;
769
+ py_proc -> py_v = NULL ;
770
+
771
+ py_proc -> is = NULL ;
772
+ py_proc -> ts = NULL ;
773
+ py_proc -> rs = NULL ;
706
774
707
775
_prehash_symbols ();
708
776
@@ -951,12 +1019,11 @@ _py_proc__find_current_thread_offset(py_proc_t * self, void * thread_raddr) {
951
1019
V_DESC (self -> py_v );
952
1020
953
1021
void * interp_head_raddr ;
954
- _PyRuntimeState py_runtime ;
955
1022
956
- if (py_proc__get_type (self , self -> symbols [DYNSYM_RUNTIME ], py_runtime ))
1023
+ if (py_proc__copy_v (self , runtime , self -> symbols [DYNSYM_RUNTIME ], self -> rs ))
957
1024
FAIL ;
958
1025
959
- interp_head_raddr = V_FIELD (void * , py_runtime , py_runtime , o_interp_head );
1026
+ interp_head_raddr = V_FIELD_PTR (void * , self -> rs , py_runtime , o_interp_head );
960
1027
961
1028
// Search offset of current thread in _PyRuntimeState structure
962
1029
PyInterpreterState is ;
@@ -1200,15 +1267,13 @@ py_proc__sample(py_proc_t * self) {
1200
1267
1201
1268
V_DESC (self -> py_v );
1202
1269
1203
- PyInterpreterState is ;
1204
-
1205
1270
do {
1206
- if (fail (py_proc__get_type (self , current_interp , is ))) {
1271
+ if (fail (py_proc__copy_v (self , is , current_interp , self -> is ))) {
1207
1272
log_ie ("Failed to get interpreter state while sampling" );
1208
1273
FAIL ;
1209
1274
}
1210
1275
1211
- void * tstate_head = V_FIELD (void * , is , py_is , o_tstate_head );
1276
+ void * tstate_head = V_FIELD_PTR (void * , self -> is , py_is , o_tstate_head );
1212
1277
if (!isvalid (tstate_head ))
1213
1278
// Maybe the interpreter state is in an invalid state. We'll try again
1214
1279
// unless there is a fatal error.
@@ -1223,7 +1288,7 @@ py_proc__sample(py_proc_t * self) {
1223
1288
time_delta = gettime () - self -> timestamp ;
1224
1289
#endif
1225
1290
1226
- int result = _py_proc__sample_interpreter (self , & is , time_delta );
1291
+ int result = _py_proc__sample_interpreter (self , self -> is , time_delta );
1227
1292
1228
1293
#ifdef NATIVE
1229
1294
if (fail (_py_proc__resume_threads (self , & raddr ))) {
@@ -1234,7 +1299,7 @@ py_proc__sample(py_proc_t * self) {
1234
1299
1235
1300
if (fail (result ))
1236
1301
FAIL ;
1237
- } while (isvalid (current_interp = V_FIELD (void * , is , py_is , o_next )));
1302
+ } while (isvalid (current_interp = V_FIELD_PTR (void * , self -> is , py_is , o_next )));
1238
1303
1239
1304
#ifdef NATIVE
1240
1305
self -> timestamp = gettime ();
@@ -1321,6 +1386,8 @@ py_proc__destroy(py_proc_t * self) {
1321
1386
hash_table__destroy (self -> base_table );
1322
1387
#endif
1323
1388
1389
+ _py_proc__free_local_buffers (self );
1390
+
1324
1391
sfree (self -> bin_path );
1325
1392
sfree (self -> lib_path );
1326
1393
sfree (self -> extra );
0 commit comments