diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 21f8fe7ddad4a7..20d68597f6f3b0 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -210,3 +210,22 @@ PyAPI_FUNC(int) _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra); PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra); + +/* API for debug code object */ +void _PyCode_DebugDump(PyCodeObject *code, const char *debug_tag); + +/* Dump the specified `code` by co_name and co_filename. + * + * Dump current interpreter frame's code in lldb/gdb command: + * (lldb) exp _PyCode_DebugDump(frame->f_code, "lldb") + * + * Write source when debug mode: + * #ifdef Py_DEBUG + * PyCode_DUMP_IF(code, "foo", "foo", "foo.py"); + * #endif +*/ +#define PyCode_DUMP_IF(code, TAG, CO_NAME, CO_FILENAME) \ + if (((CO_NAME) == NULL || strcmp(PyUnicode_AsUTF8((code)->co_name), CO_NAME) == 0) && \ + ((CO_FILENAME) == NULL || strcmp(PyUnicode_AsUTF8((code)->co_filename), CO_FILENAME) == 0)) {\ + _PyCode_DebugDump(code, TAG);\ + } diff --git a/Include/opcode_names.h b/Include/opcode_names.h new file mode 100644 index 00000000000000..e948971d43827a --- /dev/null +++ b/Include/opcode_names.h @@ -0,0 +1,189 @@ +/* Auto-generated by Tools/scripts/generate_opcode_h.py */ +#ifndef Py_OPCODE_NAME_H +#define Py_OPCODE_NAME_H +#ifdef __cplusplus +extern "C" { +#endif + /* Instruction opcode names */ +static const char *OPCODE_NAMES[256] = { + [1] = "POP_TOP", + [2] = "PUSH_NULL", + [3] = "CACHE", + [4] = "BINARY_OP_ADAPTIVE", + [5] = "BINARY_OP_ADD_INT", + [6] = "BINARY_OP_ADD_FLOAT", + [7] = "BINARY_OP_ADD_UNICODE", + [8] = "BINARY_OP_INPLACE_ADD_UNICODE", + [9] = "NOP", + [10] = "UNARY_POSITIVE", + [11] = "UNARY_NEGATIVE", + [12] = "UNARY_NOT", + [13] = "BINARY_OP_MULTIPLY_INT", + [14] = "BINARY_OP_MULTIPLY_FLOAT", + [15] = "UNARY_INVERT", + [16] = "BINARY_OP_SUBTRACT_INT", + [17] = "BINARY_OP_SUBTRACT_FLOAT", + [18] = "COMPARE_OP_ADAPTIVE", + [19] = "COMPARE_OP_FLOAT_JUMP", + [20] = "COMPARE_OP_INT_JUMP", + [21] = "COMPARE_OP_STR_JUMP", + [22] = "BINARY_SUBSCR_ADAPTIVE", + [23] = "BINARY_SUBSCR_GETITEM", + [24] = "BINARY_SUBSCR_LIST_INT", + [25] = "BINARY_SUBSCR", + [26] = "BINARY_SUBSCR_TUPLE_INT", + [27] = "BINARY_SUBSCR_DICT", + [28] = "STORE_SUBSCR_ADAPTIVE", + [29] = "STORE_SUBSCR_LIST_INT", + [30] = "GET_LEN", + [31] = "MATCH_MAPPING", + [32] = "MATCH_SEQUENCE", + [33] = "MATCH_KEYS", + [34] = "STORE_SUBSCR_DICT", + [35] = "PUSH_EXC_INFO", + [36] = "CALL_ADAPTIVE", + [37] = "CALL_PY_EXACT_ARGS", + [38] = "CALL_PY_WITH_DEFAULTS", + [39] = "JUMP_ABSOLUTE_QUICK", + [40] = "LOAD_ATTR_ADAPTIVE", + [41] = "LOAD_ATTR_INSTANCE_VALUE", + [42] = "LOAD_ATTR_WITH_HINT", + [43] = "LOAD_ATTR_SLOT", + [44] = "LOAD_ATTR_MODULE", + [45] = "LOAD_GLOBAL_ADAPTIVE", + [46] = "LOAD_GLOBAL_MODULE", + [47] = "LOAD_GLOBAL_BUILTIN", + [48] = "LOAD_METHOD_ADAPTIVE", + [49] = "WITH_EXCEPT_START", + [50] = "GET_AITER", + [51] = "GET_ANEXT", + [52] = "BEFORE_ASYNC_WITH", + [53] = "BEFORE_WITH", + [54] = "END_ASYNC_FOR", + [55] = "LOAD_METHOD_CLASS", + [56] = "LOAD_METHOD_MODULE", + [57] = "LOAD_METHOD_NO_DICT", + [58] = "LOAD_METHOD_WITH_DICT", + [59] = "LOAD_METHOD_WITH_VALUES", + [60] = "STORE_SUBSCR", + [61] = "DELETE_SUBSCR", + [62] = "PRECALL_ADAPTIVE", + [63] = "PRECALL_BUILTIN_CLASS", + [64] = "PRECALL_NO_KW_BUILTIN_O", + [65] = "PRECALL_NO_KW_BUILTIN_FAST", + [66] = "PRECALL_BUILTIN_FAST_WITH_KEYWORDS", + [67] = "PRECALL_NO_KW_LEN", + [68] = "GET_ITER", + [69] = "GET_YIELD_FROM_ITER", + [70] = "PRINT_EXPR", + [71] = "LOAD_BUILD_CLASS", + [72] = "PRECALL_NO_KW_ISINSTANCE", + [73] = "PRECALL_NO_KW_LIST_APPEND", + [74] = "LOAD_ASSERTION_ERROR", + [75] = "RETURN_GENERATOR", + [76] = "PRECALL_NO_KW_METHOD_DESCRIPTOR_O", + [77] = "PRECALL_NO_KW_METHOD_DESCRIPTOR_NOARGS", + [78] = "PRECALL_NO_KW_STR_1", + [79] = "PRECALL_NO_KW_TUPLE_1", + [80] = "PRECALL_NO_KW_TYPE_1", + [81] = "PRECALL_NO_KW_METHOD_DESCRIPTOR_FAST", + [82] = "LIST_TO_TUPLE", + [83] = "RETURN_VALUE", + [84] = "IMPORT_STAR", + [85] = "SETUP_ANNOTATIONS", + [86] = "YIELD_VALUE", + [87] = "ASYNC_GEN_WRAP", + [88] = "PREP_RERAISE_STAR", + [89] = "POP_EXCEPT", + [90] = "STORE_NAME", + [91] = "DELETE_NAME", + [92] = "UNPACK_SEQUENCE", + [93] = "FOR_ITER", + [94] = "UNPACK_EX", + [95] = "STORE_ATTR", + [96] = "DELETE_ATTR", + [97] = "STORE_GLOBAL", + [98] = "DELETE_GLOBAL", + [99] = "SWAP", + [100] = "LOAD_CONST", + [101] = "LOAD_NAME", + [102] = "BUILD_TUPLE", + [103] = "BUILD_LIST", + [104] = "BUILD_SET", + [105] = "BUILD_MAP", + [106] = "LOAD_ATTR", + [107] = "COMPARE_OP", + [108] = "IMPORT_NAME", + [109] = "IMPORT_FROM", + [110] = "JUMP_FORWARD", + [111] = "JUMP_IF_FALSE_OR_POP", + [112] = "JUMP_IF_TRUE_OR_POP", + [113] = "JUMP_ABSOLUTE", + [114] = "POP_JUMP_IF_FALSE", + [115] = "POP_JUMP_IF_TRUE", + [116] = "LOAD_GLOBAL", + [117] = "IS_OP", + [118] = "CONTAINS_OP", + [119] = "RERAISE", + [120] = "COPY", + [121] = "JUMP_IF_NOT_EXC_MATCH", + [122] = "BINARY_OP", + [123] = "SEND", + [124] = "LOAD_FAST", + [125] = "STORE_FAST", + [126] = "DELETE_FAST", + [127] = "JUMP_IF_NOT_EG_MATCH", + [128] = "POP_JUMP_IF_NOT_NONE", + [129] = "POP_JUMP_IF_NONE", + [130] = "RAISE_VARARGS", + [131] = "GET_AWAITABLE", + [132] = "MAKE_FUNCTION", + [133] = "BUILD_SLICE", + [134] = "JUMP_NO_INTERRUPT", + [135] = "MAKE_CELL", + [136] = "LOAD_CLOSURE", + [137] = "LOAD_DEREF", + [138] = "STORE_DEREF", + [139] = "DELETE_DEREF", + [140] = "PRECALL_BOUND_METHOD", + [141] = "PRECALL_PYFUNC", + [142] = "CALL_FUNCTION_EX", + [143] = "RESUME_QUICK", + [144] = "EXTENDED_ARG", + [145] = "LIST_APPEND", + [146] = "SET_ADD", + [147] = "MAP_ADD", + [148] = "LOAD_CLASSDEREF", + [149] = "COPY_FREE_VARS", + [150] = "STORE_ATTR_ADAPTIVE", + [151] = "RESUME", + [152] = "MATCH_CLASS", + [153] = "STORE_ATTR_INSTANCE_VALUE", + [154] = "STORE_ATTR_SLOT", + [155] = "FORMAT_VALUE", + [156] = "BUILD_CONST_KEY_MAP", + [157] = "BUILD_STRING", + [158] = "STORE_ATTR_WITH_HINT", + [159] = "UNPACK_SEQUENCE_ADAPTIVE", + [160] = "LOAD_METHOD", + [161] = "UNPACK_SEQUENCE_LIST", + [162] = "LIST_EXTEND", + [163] = "SET_UPDATE", + [164] = "DICT_MERGE", + [165] = "DICT_UPDATE", + [166] = "PRECALL", + [167] = "UNPACK_SEQUENCE_TUPLE", + [168] = "UNPACK_SEQUENCE_TWO_TUPLE", + [169] = "LOAD_FAST__LOAD_FAST", + [170] = "STORE_FAST__LOAD_FAST", + [171] = "CALL", + [172] = "KW_NAMES", + [173] = "LOAD_FAST__LOAD_CONST", + [174] = "LOAD_CONST__LOAD_FAST", + [175] = "STORE_FAST__STORE_FAST", +}; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OPCODE_NAME_H */ diff --git a/Makefile.pre.in b/Makefile.pre.in index 7b6f54a9ae0a7a..0d788004b4aee9 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1296,8 +1296,10 @@ regen-opcode: # using Tools/scripts/generate_opcode_h.py $(PYTHON_FOR_REGEN) $(srcdir)/Tools/scripts/generate_opcode_h.py \ $(srcdir)/Lib/opcode.py \ - $(srcdir)/Include/opcode.h.new + $(srcdir)/Include/opcode.h.new \ + $(srcdir)/Include/opcode_names.h.new $(UPDATE_FILE) $(srcdir)/Include/opcode.h $(srcdir)/Include/opcode.h.new + $(UPDATE_FILE) $(srcdir)/Include/opcode_names.h $(srcdir)/Include/opcode_names.h.new .PHONY: regen-token regen-token: @@ -1468,6 +1470,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/object.h \ $(srcdir)/Include/objimpl.h \ $(srcdir)/Include/opcode.h \ + $(srcdir)/Include/opcode_names.h \ $(srcdir)/Include/osdefs.h \ $(srcdir)/Include/osmodule.h \ $(srcdir)/Include/patchlevel.h \ diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-10-27-16-50-33.bpo-45630.xNUB2B.rst b/Misc/NEWS.d/next/Core and Builtins/2021-10-27-16-50-33.bpo-45630.xNUB2B.rst new file mode 100644 index 00000000000000..9a1f360a8c44ef --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-10-27-16-50-33.bpo-45630.xNUB2B.rst @@ -0,0 +1 @@ +Add code debug dump interface \ No newline at end of file diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 5a87e6c4ff8777..80da293ee2458d 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1948,3 +1948,33 @@ _PyStaticCode_InternStrings(PyCodeObject *co) } return 0; } + +void +_PyCode_DebugDump(PyCodeObject *code, const char *debug_tag) +{ + #include "opcode_names.h" + #define DUMP_FIELD(__x) \ + fprintf(stderr, "%-16s = %d\n", #__x, (code->__x)) + + fprintf(stderr, "\n============= %s =============\n", debug_tag); + DUMP_FIELD(co_argcount); + DUMP_FIELD(co_kwonlyargcount); + DUMP_FIELD(co_nlocals); + DUMP_FIELD(co_stacksize); + DUMP_FIELD(co_firstlineno); + fprintf(stderr, "co_filename = %s\n", PyUnicode_AsUTF8(code->co_filename)); + fprintf(stderr, "co_name = %s\n", PyUnicode_AsUTF8(code->co_name)); + + Py_ssize_t instr_count = PyBytes_GET_SIZE(code->co_code) / ((Py_ssize_t) sizeof(_Py_CODEUNIT)); + const _Py_CODEUNIT *inst_array = code->co_firstinstr; + fprintf(stderr, "Dumping raw code:\n"); + for (Py_ssize_t i = 0; i < instr_count; i++) { + uint8_t opcode = _Py_OPCODE(inst_array[i]); + uint8_t oparg = _Py_OPARG(inst_array[i]); + fprintf(stderr, "%-8ld%-20s%-8d\n", i * 2, OPCODE_NAMES[opcode], oparg); + } + + fprintf(stderr, "\n============= %s end =============\n", debug_tag); + fflush(stderr); + #undef DUMP_FIELD +} \ No newline at end of file diff --git a/PCbuild/regen.targets b/PCbuild/regen.targets index a49d97190ce20b..3ea9c565607eee 100644 --- a/PCbuild/regen.targets +++ b/PCbuild/regen.targets @@ -59,7 +59,7 @@ Inputs="@(_OpcodeSources)" Outputs="@(_OpcodeOutputs)" DependsOnTargets="FindPythonForBuild"> - diff --git a/Tools/scripts/generate_opcode_h.py b/Tools/scripts/generate_opcode_h.py index 75a9c3f3bfadbc..ec77a5651c992d 100644 --- a/Tools/scripts/generate_opcode_h.py +++ b/Tools/scripts/generate_opcode_h.py @@ -43,7 +43,7 @@ def write_int_array_from_ops(name, ops, out): assert bits == 0 out.write(f"}};\n") -def main(opcode_py, outfile='Include/opcode.h'): +def main(opcode_py, outfile='Include/opcode.h', namefile='Include/opcode_name.h'): opcode = {} if hasattr(tokenize, 'open'): fp = tokenize.open(opcode_py) # Python 3.2+ @@ -58,6 +58,7 @@ def main(opcode_py, outfile='Include/opcode.h'): hasjrel = opcode['hasjrel'] hasjabs = opcode['hasjabs'] used = [ False ] * 256 + op_names = [ None ] * 256 next_op = 1 for name, op in opmap.items(): used[op] = True @@ -66,6 +67,7 @@ def main(opcode_py, outfile='Include/opcode.h'): for name in opname: if name in opmap: fobj.write(DEFINE.format(name, opmap[name])) + op_names[opmap[name]] = name if name == 'POP_EXCEPT': # Special entry for HAVE_ARGUMENT fobj.write(DEFINE.format("HAVE_ARGUMENT", opcode["HAVE_ARGUMENT"])) @@ -74,6 +76,7 @@ def main(opcode_py, outfile='Include/opcode.h'): next_op += 1 fobj.write(DEFINE.format(name, next_op)) used[next_op] = True + op_names[next_op] = name fobj.write(DEFINE.format('DO_TRACING', 255)) fobj.write("\nextern const uint8_t _PyOpcode_InlineCacheEntries[256];\n") fobj.write("\n#ifdef NEED_OPCODE_TABLES\n") @@ -98,10 +101,31 @@ def main(opcode_py, outfile='Include/opcode.h'): fobj.write(DEFINE.format(op, i)) fobj.write(footer) + with open(namefile, 'w') as fobj: + fobj.write(""" +/* Auto-generated by Tools/scripts/generate_opcode_h.py */ +#ifndef Py_OPCODE_NAME_H +#define Py_OPCODE_NAME_H +#ifdef __cplusplus +extern "C" { +#endif + /* Instruction opcode names */ +""".lstrip()) + fobj.write("static const char *OPCODE_NAMES[256] = {\n") + for i, v in enumerate(op_names): + if v is not None: + fobj.write(f"\t[{i}] = \"{v}\",\n") + fobj.write("};\n") + fobj.write(""" +#ifdef __cplusplus +} +#endif +#endif /* !Py_OPCODE_NAME_H */ +""") print(f"{outfile} regenerated from {opcode_py}") if __name__ == '__main__': - main(sys.argv[1], sys.argv[2]) + main(sys.argv[1], sys.argv[2], sys.argv[3])