diff --git a/Cargo.lock b/Cargo.lock index 40baabbaa..2b6783e22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955f37ac58af2416bac687c8ab66a4ccba282229bd7422a28d2281a5e66a6116" +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -117,6 +126,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.0" @@ -830,6 +854,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + [[package]] name = "glob" version = "0.3.0" @@ -1300,6 +1330,15 @@ dependencies = [ "syn", ] +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.13.0" @@ -1720,6 +1759,12 @@ dependencies = [ "syn-ext", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1980,6 +2025,7 @@ dependencies = [ "ahash", "ascii", "atty", + "backtrace", "bitflags", "bstr", "caseless", diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 25a9cd072..6fb794428 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -93,6 +93,7 @@ flame = { version = "0.2", optional = true } flamer = { version = "0.4", optional = true } ascii = "1.0.0" +backtrace = "0.3.66" [target.'cfg(unix)'.dependencies] exitcode = "1.1.2" diff --git a/vm/src/object/core.rs b/vm/src/object/core.rs index 19055df25..f6055a3f0 100644 --- a/vm/src/object/core.rs +++ b/vm/src/object/core.rs @@ -31,6 +31,7 @@ use std::{ any::TypeId, borrow::Borrow, cell::UnsafeCell, + collections::HashMap, fmt, marker::PhantomData, mem::ManuallyDrop, @@ -38,6 +39,11 @@ use std::{ ptr::{self, NonNull}, }; +use once_cell::sync::Lazy; + +pub static ID2TYPE: Lazy>> = + Lazy::new(|| PyMutex::new(HashMap::new())); + // so, PyObjectRef is basically equivalent to `PyRc>`, except it's // only one pointer in width rather than 2. We do that by manually creating a vtable, and putting // a &'static reference to it inside the `PyRc` rather than adjacent to it, like trait objects do. @@ -109,6 +115,7 @@ impl PyObjVTable { #[repr(C)] struct PyInner { ref_count: RefCount, + is_drop: PyMutex, // TODO: move typeid into vtable once TypeId::of is const typeid: TypeId, vtable: &'static PyObjVTable, @@ -433,6 +440,7 @@ impl PyInner { let member_count = typ.slots.member_count; Box::new(PyInner { ref_count: RefCount::new(), + is_drop: PyMutex::new(false), typeid: TypeId::of::(), vtable: PyObjVTable::of::(), typ: PyAtomicRef::from(typ), @@ -849,7 +857,17 @@ impl<'a, T: PyObjectPayload> From<&'a Py> for &'a PyObject { impl Drop for PyObjectRef { #[inline] fn drop(&mut self) { + if *self.0.is_drop.lock() { + error!( + "Double drop on PyObjectRef, typeid={:?}, type={:?} + backtrace={:?}", + self.0.typeid, + ID2TYPE.lock().get(&self.0.typeid), + backtrace::Backtrace::new() + ); + } if self.0.ref_count.dec() { + *self.0.is_drop.lock() = true; unsafe { PyObject::drop_slow(self.ptr) } } } @@ -953,7 +971,23 @@ impl fmt::Debug for PyRef { impl Drop for PyRef { #[inline] fn drop(&mut self) { + if *self.as_object().0.is_drop.lock() { + error!( + "Double drop on PyRef, type={:?} + backtrace={:?}", + std::any::type_name::(), + backtrace::Backtrace::new() + ); + } + + let tid = TypeId::of::(); + ID2TYPE + .lock() + .entry(tid) + .or_insert_with(|| std::any::type_name::().to_string()); + if self.0.ref_count.dec() { + *self.0.is_drop.lock() = true; unsafe { PyObject::drop_slow(self.ptr.cast::()) } } } @@ -1136,6 +1170,7 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) { let type_type_ptr = Box::into_raw(Box::new(partially_init!( PyInner:: { ref_count: RefCount::new(), + is_drop: PyMutex::new(false), typeid: TypeId::of::(), vtable: PyObjVTable::of::(), dict: None, @@ -1148,6 +1183,7 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) { let object_type_ptr = Box::into_raw(Box::new(partially_init!( PyInner:: { ref_count: RefCount::new(), + is_drop: PyMutex::new(false), typeid: TypeId::of::(), vtable: PyObjVTable::of::(), dict: None,