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])