Modules/cinder.c (658 lines of code) (raw):

/* Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) */ #include "Python.h" #include "internal/pycore_shadow_frame.h" #include "frameobject.h" #include "Jit/pyjit.h" PyAPI_FUNC(void) _PyShadow_ClearCache(PyObject *co); extern int _PyShadow_PolymorphicCacheEnabled; extern int _Py_SkipFinalCleanup; extern int _Py_SetShortcutTypeCall; /* facebook begin */ static PyObject * cinder_setknobs(PyObject *self, PyObject *o) { if (!PyDict_CheckExact(o)) { PyErr_SetString(PyExc_ValueError, "expected dictionary of knobs"); return NULL; } PyObject* shadowcode = PyDict_GetItemString(o, "shadowcode"); if (shadowcode != NULL) { int enabled = PyObject_IsTrue(shadowcode); _PyEval_ShadowByteCodeEnabled = enabled != -1 && enabled; } PyObject* lazy_imports = PyDict_GetItemString(o, "lazyimports"); if (lazy_imports != NULL) { int enabled = PyObject_IsTrue(lazy_imports); _PyEval_LazyImportsEnabled = enabled != -1 && enabled; } PyObject *genfreelist = PyDict_GetItemString(o, "genfreelist"); if (genfreelist != NULL) { int enabled = PyObject_IsTrue(genfreelist); _PyGen_FreeListEnabled = enabled != -1 && enabled; if (!enabled) { _PyGen_ClearFreeList(); } } PyObject *polymorphic = PyDict_GetItemString(o, "polymorphiccache"); if (polymorphic != NULL) { int enabled = PyObject_IsTrue(polymorphic); _PyShadow_PolymorphicCacheEnabled = enabled != -1 && enabled; } PyObject *skip_final_cleanup = PyDict_GetItemString(o, "skipfinalcleanup"); if (skip_final_cleanup) { int skip_cleanup = PyObject_IsTrue(skip_final_cleanup); _Py_SkipFinalCleanup = skip_cleanup != -1 && skip_cleanup; } PyObject *set_shortcut_typecall = PyDict_GetItemString(o, "setshortcuttypecall"); if (set_shortcut_typecall) { int ok = PyObject_IsTrue(set_shortcut_typecall); _Py_SetShortcutTypeCall = ok != -1 && ok; } Py_RETURN_NONE; } PyDoc_STRVAR(setknobs_doc, "setknobs(knobs)\n\ \n\ Sets the currently enabled knobs. Knobs are provided as a dictionary of\n\ names and a value indicating if they are enabled.\n\ \n\ See cinder.getknobs() for a list of recognized knobs."); static PyObject * cinder_getknobs(PyObject *self, PyObject *args) { PyObject* res = PyDict_New(); if (res == NULL) { return NULL; } int err = PyDict_SetItemString(res, "shadowcode", _PyEval_ShadowByteCodeEnabled ? Py_True : Py_False); if (err == -1) return NULL; err = PyDict_SetItemString(res, "lazyimports", _PyEval_LazyImportsEnabled ? Py_True : Py_False); if (err == -1) return NULL; err = PyDict_SetItemString( res, "genfreelist", _PyGen_FreeListEnabled ? Py_True : Py_False); if (err == -1) { return NULL; } err = PyDict_SetItemString(res, "skipfinalcleanup", _Py_SkipFinalCleanup ? Py_True : Py_False); if (err == -1) { return NULL; } err = PyDict_SetItemString(res, "polymorphiccache", _PyShadow_PolymorphicCacheEnabled ? Py_True : Py_False); if (err == -1) { return NULL; } return res; } PyDoc_STRVAR(getknobs_doc, "getcinderknobs()\n\ \n\ Gets the available knobs and their current status."); /* facebook end */ static PyObject * cinder_freeze_type(PyObject *self, PyObject *o) { if (!PyType_Check(o)) { PyErr_SetString( PyExc_TypeError, "freeze_type requires a type"); return NULL; } ((PyTypeObject*)o)->tp_flags |= Py_TPFLAGS_FROZEN; Py_INCREF(o); return o; } PyDoc_STRVAR(freeze_type_doc, "freeze_type(t)\n\ \n\ Marks a type as being frozen and disallows any future mutations to it." ); static PyObject * cinder_warn_on_inst_dict(PyObject *self, PyObject *o) { if (!PyType_Check(o)) { PyErr_SetString( PyExc_TypeError, "warn_on_inst_dict requires a type"); return NULL; } else if (((PyTypeObject *)o)->tp_flags & Py_TPFLAGS_FROZEN) { PyErr_SetString( PyExc_TypeError, "can't call warn_on_inst_dict on a frozen type"); return NULL; } ((PyTypeObject *)o)->tp_flags |= Py_TPFLAGS_WARN_ON_SETATTR; Py_INCREF(o); return o; } PyDoc_STRVAR(cinder_warn_on_inst_dict_doc, "warn_on_inst_dict(t)\n\ \n\ Causes a warning to be emitted when a type dictionary is created." ); static PyObject * cinder_set_warn_handler(PyObject *self, PyObject *o) { Py_XDECREF(_PyErr_CinderWarnHandler); if (o == Py_None) { _PyErr_CinderWarnHandler = NULL; } else { _PyErr_CinderWarnHandler = o; Py_INCREF(_PyErr_CinderWarnHandler); } Py_RETURN_NONE; } PyDoc_STRVAR(cinder_set_warn_handler_doc, "set_warn_handler(cb)\n\ \n\ Sets a callback that receives Cinder specific warnings.\ \ Callback should be a callable that accepts:\ \ (message, *args)" ); static PyObject * cinder_get_warn_handler(PyObject *self, PyObject *args) { if (_PyErr_CinderWarnHandler != NULL) { Py_INCREF(_PyErr_CinderWarnHandler); return _PyErr_CinderWarnHandler; } Py_RETURN_NONE; } PyDoc_STRVAR(cinder_get_warn_handler_doc, "get_warn_handler()\n\ \n\ Gets the callback that receives Cinder specific warnings."); static PyObject * cinder_set_immutable_warn_handler(PyObject *self, PyObject *o) { Py_XDECREF(_PyErr_ImmutableWarnHandler); if (o == Py_None) { _PyErr_ImmutableWarnHandler = NULL; } else { _PyErr_ImmutableWarnHandler = o; Py_INCREF(_PyErr_ImmutableWarnHandler); } Py_RETURN_NONE; } PyDoc_STRVAR(cinder_set_immutable_warn_handler_doc, "set_immutable_warn_handler(cb)\n\ \n\ Sets a callback that receives immutability specific warnings in Cinder.\ \ Callback should be a callable that accepts:\ \ (err_code, message, *args)" ); static PyObject * cinder_get_immutable_warn_handler(PyObject *self, PyObject *args) { if (_PyErr_ImmutableWarnHandler != NULL) { Py_INCREF(_PyErr_ImmutableWarnHandler); return _PyErr_ImmutableWarnHandler; } Py_RETURN_NONE; } PyDoc_STRVAR(cinder_get_immutable_warn_handler_doc, "get_immutable_warn_handler()\n\ \n\ Gets the callback that receives immutability specific warnings in Cinder."); static PyObject * cinder_raise_immutable_warning(PyObject *self, PyObject *args) { int code; PyObject* msg = NULL; PyObject* value = NULL; if (!PyArg_ParseTuple(args, "iU|O", &code, &msg, &value)) { return NULL; } if (_PyErr_RaiseImmutableWarning(code, msg, value) < 0) { return NULL; } Py_RETURN_NONE; } PyDoc_STRVAR(cinder_raise_immutable_warning_doc, "raise_immutable_warn(code, msg, [arg])\n\ \n\ Manually raise a immutability warning."); static PyObject * cinder_flush_immutable_warnings(PyObject *self, PyObject *args) { if (_PyErr_FlushImmutabilityWarningsBuffer() != 0) { return NULL; } Py_RETURN_NONE; } PyDoc_STRVAR(cinder_flush_immutable_warnings_doc, "flush_immutable_warnings()\n\ \n\ Manually flush the a immutability warning buffer."); PyAPI_FUNC(void) _PyJIT_ClearDictCaches(void); static PyObject * clear_caches(PyObject *self, PyObject *obj) { _PyJIT_ClearDictCaches(); Py_RETURN_NONE; } static PyObject * clear_shadow_cache(PyObject *self, PyObject *obj) { _PyShadow_ClearCache(obj); Py_RETURN_NONE; } PyDoc_STRVAR(strict_module_patch_doc, "strict_module_patch(mod, name, value)\n\ Patch a field in a strict module\n\ Requires patching to be enabled" ); static PyObject * strict_module_patch(PyObject *self, PyObject *args) { PyObject* mod; PyObject* name; PyObject* value; if (!PyArg_ParseTuple(args, "OUO", &mod, &name, &value)) { return NULL; } if (_Py_do_strictmodule_patch(mod, name, value) < 0) { return NULL; } Py_RETURN_NONE; } PyDoc_STRVAR(strict_module_patch_delete_doc, "strict_module_patch_delete(mod, name)\n\ Delete a field in a strict module\n\ Requires patching to be enabled" ); static PyObject * strict_module_patch_delete(PyObject *self, PyObject *args) { PyObject* mod; PyObject* name; if (!PyArg_ParseTuple(args, "OU", &mod, &name)) { return NULL; } if (_Py_do_strictmodule_patch(mod, name, NULL) < 0) { return NULL; } Py_RETURN_NONE; } PyDoc_STRVAR(strict_module_patch_enabled_doc, "strict_module_patch_enabled(mod)\n\ Gets whether patching is enabled on the strict module" ); static PyObject * strict_module_patch_enabled(PyObject *self, PyObject *mod) { if (((PyStrictModuleObject *) mod) -> global_setter != NULL) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } PyAPI_FUNC(int) _PyClassLoader_ClearVtables(void); PyAPI_FUNC(int) _PyClassLoader_ClearCache(void); static PyObject * clear_classloader_caches(PyObject *self, PyObject *obj) { _PyClassLoader_ClearVtables(); _PyClassLoader_ClearCache(); Py_RETURN_NONE; } static PyObject * get_qualname_of_code(PyObject *Py_UNUSED(module), PyObject *arg) { if (!PyCode_Check(arg)) { PyErr_SetString(PyExc_TypeError, "Expected code object"); return NULL; } PyObject *qualname = ((PyCodeObject *)arg)->co_qualname; if (qualname != NULL) { Py_INCREF(qualname); return qualname; } Py_RETURN_NONE; } static PyObject * set_qualname_of_code(PyObject *Py_UNUSED(module), PyObject **args, Py_ssize_t nargs) { if (nargs != 2) { PyErr_SetString(PyExc_TypeError, "Expected 2 arguments"); return NULL; } PyObject *arg = args[0]; if (!PyCode_Check(arg)) { PyErr_SetString(PyExc_TypeError, "Expected code object as 1st argument"); return NULL; } PyObject *qualname = args[1]; if (qualname != Py_None) { if (!PyUnicode_Check(qualname)) { PyErr_SetString(PyExc_TypeError, "Expected str as 2nd argument"); return NULL; } Py_XSETREF(((PyCodeObject *)arg)->co_qualname, qualname); Py_INCREF(qualname); } Py_RETURN_NONE; } static PyObject* set_profile_interp(PyObject *self, PyObject *arg) { int is_true = PyObject_IsTrue(arg); if (is_true < 0) { return NULL; } PyThreadState* tstate = PyThreadState_Get(); int old_flag = tstate->profile_interp; _PyThreadState_SetProfileInterp(tstate, is_true); if (old_flag) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject* set_profile_interp_all(PyObject *self, PyObject *arg) { int is_true = PyObject_IsTrue(arg); if (is_true < 0) { return NULL; } g_profile_new_interp_threads = is_true; _PyThreadState_SetProfileInterpAll(is_true); Py_RETURN_NONE; } static PyObject* set_profile_interp_period(PyObject *self, PyObject *arg) { if (!PyLong_Check(arg)) { PyErr_Format(PyExc_TypeError, "Expected int object, got %.200s", Py_TYPE(arg)->tp_name); return NULL; } long val = PyLong_AsLong(arg); if (val == -1 && PyErr_Occurred()) { return NULL; } _PyRuntimeState_SetProfileInterpPeriod(val); Py_RETURN_NONE; } static PyObject* get_and_clear_type_profiles(PyObject *self, PyObject *obj) { return _PyJIT_GetAndClearTypeProfiles(); } static PyObject* clear_type_profiles(PyObject *self, PyObject *obj) { _PyJIT_ClearTypeProfiles(); Py_RETURN_NONE; } static PyObject* get_frame_gen(PyObject *self, PyObject *frame) { if (!PyFrame_Check(frame)) { PyErr_Format(PyExc_TypeError, "Expected frame object, got %.200s", Py_TYPE(frame)->tp_name); return NULL; } PyObject *gen = ((PyFrameObject *)frame)->f_gen; if (!gen) { Py_RETURN_NONE; } Py_INCREF(gen); return gen; } static PyObject* get_coro_awaiter(PyObject *Py_UNUSED(self), PyObject *coro) { if (!PyCoro_CheckExact(coro)) { PyErr_Format(PyExc_TypeError, "Expected coroutine object, got %.200s", Py_TYPE(coro)->tp_name); return NULL; } PyCoroObject *awaiter = ((PyCoroObject *)coro)->cr_awaiter; if (!awaiter) { Py_RETURN_NONE; } Py_INCREF(awaiter); return (PyObject *)awaiter; } static PyObject* has_no_shadowing_instances(PyObject *self, PyObject *type) { if (!PyType_Check(type)) { PyErr_Format(PyExc_TypeError, "Expected type object, got %.200s", Py_TYPE(type)->tp_name); return NULL; } if (PyType_HasFeature((PyTypeObject *) type, Py_TPFLAGS_NO_SHADOWING_INSTANCES)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject* get_call_stack(PyObject *self, PyObject *args) { _PyShadowFrame *shadow_frame = PyThreadState_GET()->shadow_frame; PyObject *stack = PyList_New(0); if (stack == NULL) { return NULL; } while (shadow_frame != NULL) { PyCodeObject *code = _PyShadowFrame_GetCode(shadow_frame); if (PyList_Append(stack, (PyObject *) code) != 0) { Py_DECREF(stack); return NULL; } shadow_frame = shadow_frame->prev; } if (PyList_Reverse(stack) != 0) { Py_DECREF(stack); return NULL; } return stack; } static PyObject* get_entire_call_stack_as_qualnames(PyObject *self, PyObject *Py_UNUSED(args)) { _PyShadowFrame *shadow_frame = PyThreadState_GET()->shadow_frame; _PyShadowFrame *last = NULL; _PyShadowFrame *awaiter_frame = NULL; PyObject *fqname; PyObject *stack = PyList_New(0); int did_fail; if (stack == NULL) { goto err; } while (shadow_frame != NULL) { fqname = _PyShadowFrame_GetFullyQualifiedName(shadow_frame); if (!fqname) { goto err; } did_fail = PyList_Append(stack, fqname); Py_DECREF(fqname); if (did_fail) { goto err; } last = shadow_frame; shadow_frame = shadow_frame->prev; // The awaiter stack (if it exists) should always get the preference awaiter_frame = _PyShadowFrame_GetAwaiterFrame(last); if (awaiter_frame != NULL) { shadow_frame = awaiter_frame; } } if (PyList_Reverse(stack) != 0) { goto err; } return stack; err: Py_XDECREF(stack); return NULL; } static struct PyMethodDef cinder_module_methods[] = { {"setknobs", cinder_setknobs, METH_O, setknobs_doc}, {"getknobs", cinder_getknobs, METH_NOARGS, getknobs_doc}, {"freeze_type", cinder_freeze_type, METH_O, freeze_type_doc}, {"warn_on_inst_dict", cinder_warn_on_inst_dict, METH_O, cinder_warn_on_inst_dict_doc}, {"cinder_set_warn_handler", cinder_set_warn_handler, METH_O, cinder_set_warn_handler_doc}, {"set_warn_handler", cinder_set_warn_handler, METH_O, cinder_set_warn_handler_doc}, {"get_warn_handler", cinder_get_warn_handler, METH_NOARGS, cinder_get_warn_handler_doc}, {"set_immutable_warn_handler", cinder_set_immutable_warn_handler, METH_O, cinder_set_immutable_warn_handler_doc}, {"get_immutable_warn_handler", cinder_get_immutable_warn_handler, METH_NOARGS, cinder_get_immutable_warn_handler_doc}, {"raise_immutable_warning", cinder_raise_immutable_warning, METH_VARARGS, cinder_raise_immutable_warning_doc}, {"flush_immutable_warnings", cinder_flush_immutable_warnings, METH_NOARGS, cinder_flush_immutable_warnings_doc}, {"clear_caches", clear_caches, METH_NOARGS, "Clears caches associated with the JIT. This may have a negative effect " "on performance of existing JIT compiled code."}, {"clear_shadow_cache", clear_shadow_cache, METH_O, ""}, {"strict_module_patch", strict_module_patch, METH_VARARGS, strict_module_patch_doc}, {"strict_module_patch_delete", strict_module_patch_delete, METH_VARARGS, strict_module_patch_delete_doc}, {"strict_module_patch_enabled", strict_module_patch_enabled, METH_O, strict_module_patch_enabled_doc}, {"clear_classloader_caches", clear_classloader_caches, METH_NOARGS, "Clears classloader caches and vtables on all accessible types. " "Will hurt perf; for test isolation where modules and types with " "identical names are dynamically created and destroyed."}, {"_get_qualname", get_qualname_of_code, METH_O, "Returns qualified name stored in code object or None if codeobject was created manually"}, {"_set_qualname", (PyCFunction)set_qualname_of_code, METH_FASTCALL, "Sets the value of qualified name in code object"}, {"set_profile_interp", set_profile_interp, METH_O, "Enable or disable interpreter profiling for this thread. Returns whether or not profiling was enabled before the call."}, {"set_profile_interp_all", set_profile_interp_all, METH_O, "Enable or disable interpreter profiling for all threads, including threads created after this function returns."}, {"set_profile_interp_period", set_profile_interp_period, METH_O, "Set the period, in bytecode instructions, for interpreter profiling."}, {"get_and_clear_type_profiles", get_and_clear_type_profiles, METH_NOARGS, "Get and clear accumulated interpreter type profiles."}, {"clear_type_profiles", clear_type_profiles, METH_NOARGS, "Clear accumulated interpreter type profiles."}, {"_get_frame_gen", get_frame_gen, METH_O, "Get the generator associated with the given frame, or None if one " "doesn't exist."}, {"_get_coro_awaiter", get_coro_awaiter, METH_O, "Get the awaiter of the given coroutine, or None if one is not set."}, {"_has_no_shadowing_instances", has_no_shadowing_instances, METH_O, "Return whether or not the given type has TP_FLAGS_NO_SHADOWING_INSTACES set."}, {"_get_call_stack", get_call_stack, METH_NOARGS, "Return a list that contains the code object for each function on the call" " stack, top-most frame last."}, {"_get_entire_call_stack_as_qualnames", get_entire_call_stack_as_qualnames, METH_NOARGS, "Return the current stack as a list of qualnames."}, {NULL, NULL} /* sentinel */ }; PyDoc_STRVAR(doc_cinder, "Cinder specific methods and types"); static struct PyModuleDef cindermodule = { PyModuleDef_HEAD_INIT, "cinder", doc_cinder, -1, cinder_module_methods, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit_cinder(void) { PyObject *m; /* Create the module and add the functions */ m = PyModule_Create(&cindermodule); if (m == NULL) { return NULL; } PyObject* data_version = PyLong_FromLong(1); if (data_version == NULL) { return NULL; } if (PyObject_SetAttrString( m, "STRUCTURED_DATA_VERSION", data_version) < 0) { return NULL; } if (PyType_Ready(&PyCachedProperty_Type) < 0) { return NULL; } if (PyType_Ready(&PyCachedPropertyWithDescr_Type) < 0) { return NULL; } if (PyType_Ready(&PyStrictModule_Type) < 0) { return NULL; } if (PyType_Ready(&PyAsyncCachedProperty_Type) < 0) { return NULL; } if (PyType_Ready(&PyAsyncCachedClassProperty_Type) < 0) { return NULL; } PyObject *cached_classproperty = PyType_FromSpec(&_PyCachedClassProperty_TypeSpec); if (cached_classproperty == NULL) { return NULL; } #define ADDITEM(NAME, OBJECT) \ if (PyObject_SetAttrString(m, NAME, (PyObject *)OBJECT) < 0) { \ Py_DECREF(cached_classproperty); \ return NULL; \ } ADDITEM("cached_classproperty", cached_classproperty); ADDITEM("cached_property", &PyCachedProperty_Type); ADDITEM("StrictModule", &PyStrictModule_Type); ADDITEM("async_cached_property", &PyAsyncCachedProperty_Type); ADDITEM("async_cached_classproperty", &PyAsyncCachedClassProperty_Type); Py_DECREF(cached_classproperty); #undef ADDITEM return m; }