Skip to content

Commit 310b3d1

Browse files
malinahdpgeorge
authored andcommitted
py: Integrate sys.settrace feature into the VM and runtime.
This commit adds support for sys.settrace, allowing to install Python handlers to trace execution of Python code. The interface follows CPython as closely as possible. The feature is disabled by default and can be enabled via MICROPY_PY_SYS_SETTRACE.
1 parent c96aeda commit 310b3d1

File tree

15 files changed

+201
-1
lines changed

15 files changed

+201
-1
lines changed

ports/unix/main.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,12 @@ MP_NOINLINE int main_(int argc, char **argv) {
655655
}
656656
}
657657

658+
#if MICROPY_PY_SYS_SETTRACE
659+
MP_STATE_THREAD(prof_trace_callback) = MP_OBJ_NULL;
660+
#endif
661+
658662
#if MICROPY_PY_SYS_ATEXIT
663+
// Beware, the sys.settrace callback should be disabled before running sys.atexit.
659664
if (mp_obj_is_callable(MP_STATE_VM(sys_exitfunc))) {
660665
mp_call_function_0(MP_STATE_VM(sys_exitfunc));
661666
}

ports/unix/mpconfigport.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@
9191
#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1)
9292
#define MICROPY_PY_SYS_EXIT (1)
9393
#define MICROPY_PY_SYS_ATEXIT (1)
94+
#if MICROPY_PY_SYS_SETTRACE
95+
#define MICROPY_PERSISTENT_CODE_SAVE (1)
96+
#define MICROPY_COMP_CONST (0)
97+
#endif
9498
#if defined(__APPLE__) && defined(__MACH__)
9599
#define MICROPY_PY_SYS_PLATFORM "darwin"
96100
#else

py/bc.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw
119119
code_state->prev = NULL;
120120
#endif
121121

122+
#if MICROPY_PY_SYS_SETTRACE
123+
code_state->prev_state = NULL;
124+
code_state->frame = NULL;
125+
#endif
126+
122127
// get params
123128
size_t n_state = mp_decode_uint(&code_state->ip);
124129
code_state->ip = mp_decode_uint_skip(code_state->ip); // skip n_exc_stack

py/bc.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@
6060
// const0 : obj
6161
// constN : obj
6262

63+
typedef struct _mp_bytecode_prelude_t {
64+
uint n_state;
65+
uint n_exc_stack;
66+
uint scope_flags;
67+
uint n_pos_args;
68+
uint n_kwonly_args;
69+
uint n_def_pos_args;
70+
qstr qstr_block_name;
71+
qstr qstr_source_file;
72+
const byte *line_info;
73+
const byte *locals;
74+
const byte *opcodes;
75+
} mp_bytecode_prelude_t;
76+
6377
// Exception stack entry
6478
typedef struct _mp_exc_stack_t {
6579
const byte *handler;
@@ -84,6 +98,10 @@ typedef struct _mp_code_state_t {
8498
#if MICROPY_STACKLESS
8599
struct _mp_code_state_t *prev;
86100
#endif
101+
#if MICROPY_PY_SYS_SETTRACE
102+
struct _mp_code_state_t *prev_state;
103+
struct _mp_obj_frame_t *frame;
104+
#endif
87105
// Variable-length
88106
mp_obj_t state[0];
89107
// Variable-length, never accessed by name, only as (void*)(state + n_state)

py/compile.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,9 @@ STATIC void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_
16081608

16091609
qstr qstr_exception_local = 0;
16101610
uint end_finally_label = comp_next_label(comp);
1611+
#if MICROPY_PY_SYS_SETTRACE
1612+
EMIT_ARG(set_source_line, pns_except->source_line);
1613+
#endif
16111614

16121615
if (MP_PARSE_NODE_IS_NULL(pns_except->nodes[0])) {
16131616
// this is a catch all exception handler
@@ -3157,6 +3160,9 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
31573160
scope_find_or_add_id(scope, MP_QSTR___class__, ID_INFO_KIND_LOCAL);
31583161
}
31593162

3163+
#if MICROPY_PY_SYS_SETTRACE
3164+
EMIT_ARG(set_source_line, pns->source_line);
3165+
#endif
31603166
compile_load_id(comp, MP_QSTR___name__);
31613167
compile_store_id(comp, MP_QSTR___module__);
31623168
EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // 0 is class name

py/emitbc.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,9 @@ STATIC void emit_write_bytecode_byte_raw_code(emit_t *emit, int stack_adj, byte
284284
assert(c == MP_ALIGN(c, sizeof(void*)));
285285
*c = rc;
286286
#endif
287+
#if MICROPY_PY_SYS_SETTRACE
288+
rc->line_of_definition = emit->last_source_line;
289+
#endif
287290
}
288291

289292
// unsigned labels are relative to ip following this instruction, stored as 16 bits

py/emitglue.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "py/emitglue.h"
3535
#include "py/runtime0.h"
3636
#include "py/bc.h"
37+
#include "py/profile.h"
3738

3839
#if MICROPY_DEBUG_VERBOSE // print debugging info
3940
#define DEBUG_PRINT (1)
@@ -52,6 +53,9 @@ mp_uint_t mp_verbose_flag = 0;
5253
mp_raw_code_t *mp_emit_glue_new_raw_code(void) {
5354
mp_raw_code_t *rc = m_new0(mp_raw_code_t, 1);
5455
rc->kind = MP_CODE_RESERVED;
56+
#if MICROPY_PY_SYS_SETTRACE
57+
rc->line_of_definition = 0;
58+
#endif
5559
return rc;
5660
}
5761

@@ -75,6 +79,11 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code,
7579
rc->n_raw_code = n_raw_code;
7680
#endif
7781

82+
#if MICROPY_PY_SYS_SETTRACE
83+
mp_bytecode_prelude_t *prelude = &rc->prelude;
84+
mp_prof_extract_prelude(code, prelude);
85+
#endif
86+
7887
#ifdef DEBUG_PRINT
7988
#if !MICROPY_DEBUG_PRINTERS
8089
const size_t len = 0;
@@ -172,6 +181,12 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar
172181
if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
173182
((mp_obj_base_t*)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap;
174183
}
184+
185+
#if MICROPY_PY_SYS_SETTRACE
186+
mp_obj_fun_bc_t *self_fun = (mp_obj_fun_bc_t *)MP_OBJ_TO_PTR(fun);
187+
self_fun->rc = rc;
188+
#endif
189+
175190
break;
176191
}
177192

py/emitglue.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#define MICROPY_INCLUDED_PY_EMITGLUE_H
2828

2929
#include "py/obj.h"
30+
#include "py/bc.h"
3031

3132
// These variables and functions glue the code emitters to the runtime.
3233

@@ -63,6 +64,14 @@ typedef struct _mp_raw_code_t {
6364
size_t fun_data_len;
6465
uint16_t n_obj;
6566
uint16_t n_raw_code;
67+
#if MICROPY_PY_SYS_SETTRACE
68+
mp_bytecode_prelude_t prelude;
69+
// line_of_definition is a Python source line where the raw_code was
70+
// created e.g. MP_BC_MAKE_FUNCTION. This is different from lineno info
71+
// stored in prelude, which provides line number for first statement of
72+
// a function. Required to properly implement "call" trace event.
73+
mp_uint_t line_of_definition;
74+
#endif
6675
#if MICROPY_EMIT_MACHINE_CODE
6776
uint16_t prelude_offset;
6877
uint16_t n_qstr;

py/modsys.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
#include "py/smallint.h"
3636
#include "py/runtime.h"
3737

38+
#if MICROPY_PY_SYS_SETTRACE
39+
#include "py/objmodule.h"
40+
#include "py/profile.h"
41+
#endif
42+
3843
#if MICROPY_PY_SYS
3944

4045
// defined per port; type of these is irrelevant, just need pointer
@@ -156,6 +161,14 @@ STATIC mp_obj_t mp_sys_atexit(mp_obj_t obj) {
156161
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_atexit_obj, mp_sys_atexit);
157162
#endif
158163

164+
#if MICROPY_PY_SYS_SETTRACE
165+
// settrace(tracefunc): Set the system’s trace function.
166+
STATIC mp_obj_t mp_sys_settrace(mp_obj_t obj) {
167+
return mp_prof_settrace(obj);
168+
}
169+
MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace);
170+
#endif // MICROPY_PY_SYS_SETTRACE
171+
159172
STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = {
160173
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys) },
161174

@@ -190,6 +203,10 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = {
190203
{ MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mp_sys_exit_obj) },
191204
#endif
192205

206+
#if MICROPY_PY_SYS_SETTRACE
207+
{ MP_ROM_QSTR(MP_QSTR_settrace), MP_ROM_PTR(&mp_sys_settrace_obj) },
208+
#endif
209+
193210
#if MICROPY_PY_SYS_STDFILES
194211
{ MP_ROM_QSTR(MP_QSTR_stdin), MP_ROM_PTR(&mp_sys_stdin_obj) },
195212
{ MP_ROM_QSTR(MP_QSTR_stdout), MP_ROM_PTR(&mp_sys_stdout_obj) },

py/mpconfig.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,11 @@ typedef double mp_float_t;
11711171
#define MICROPY_PY_SYS_ATEXIT (0)
11721172
#endif
11731173

1174+
// Whether to provide "sys.settrace" function
1175+
#ifndef MICROPY_PY_SYS_SETTRACE
1176+
#define MICROPY_PY_SYS_SETTRACE (0)
1177+
#endif
1178+
11741179
// Whether to provide "sys.getsizeof" function
11751180
#ifndef MICROPY_PY_SYS_GETSIZEOF
11761181
#define MICROPY_PY_SYS_GETSIZEOF (0)
@@ -1571,4 +1576,14 @@ typedef double mp_float_t;
15711576
# define MP_WARN_CAT(x) (NULL)
15721577
#endif
15731578

1579+
// Feature dependency check.
1580+
#if MICROPY_PY_SYS_SETTRACE
1581+
#if !MICROPY_PERSISTENT_CODE_SAVE
1582+
#error "MICROPY_PY_SYS_SETTRACE requires MICROPY_PERSISTENT_CODE_SAVE to be enabled"
1583+
#endif
1584+
#if MICROPY_COMP_CONST
1585+
#error "MICROPY_PY_SYS_SETTRACE requires MICROPY_COMP_CONST to be disabled"
1586+
#endif
1587+
#endif
1588+
15741589
#endif // MICROPY_INCLUDED_PY_MPCONFIG_H

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy