From d356ae241986282cf33a31874da1d3139960b63d Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 12 Aug 2014 00:30:13 +0100 Subject: [PATCH] Make safe_ptr able to track callstacks The code stays disabled, as the performance penalty is quite high, but can be enabled to help debugging safe pointers. --- src/Makefile | 2 +- src/buffer.hh | 1 + src/buffer_manager.hh | 1 + src/client.hh | 7 +- src/input_handler.hh | 1 + src/safe_ptr.cc | 60 +++++++++++++++ src/safe_ptr.hh | 167 ++++++++++++++++++++++++++++++++++++++++++ src/user_interface.hh | 2 +- src/utils.hh | 86 ---------------------- src/window.hh | 1 + 10 files changed, 237 insertions(+), 91 deletions(-) create mode 100644 src/safe_ptr.cc create mode 100644 src/safe_ptr.hh diff --git a/src/Makefile b/src/Makefile index f9e23de1..6511b9b4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,7 +9,7 @@ bindir := $(DESTDIR)$(PREFIX)/bin sharedir := $(DESTDIR)$(PREFIX)/share/kak docdir := $(DESTDIR)$(PREFIX)/share/doc/kak -CXXFLAGS += -std=gnu++11 -g -Wall -Wno-reorder -Wno-sign-compare -pedantic +CXXFLAGS += -std=gnu++11 -g -Wall -Wno-reorder -Wno-sign-compare -pedantic -rdynamic os := $(shell uname) diff --git a/src/buffer.hh b/src/buffer.hh index c43e1ec0..e9feff66 100644 --- a/src/buffer.hh +++ b/src/buffer.hh @@ -5,6 +5,7 @@ #include "hook_manager.hh" #include "option_manager.hh" #include "keymap_manager.hh" +#include "safe_ptr.hh" #include "string.hh" #include "value.hh" diff --git a/src/buffer_manager.hh b/src/buffer_manager.hh index a1001ffd..cd7029ea 100644 --- a/src/buffer_manager.hh +++ b/src/buffer_manager.hh @@ -3,6 +3,7 @@ #include "completion.hh" #include "utils.hh" +#include "safe_ptr.hh" #include diff --git a/src/client.hh b/src/client.hh index 00ebe46d..3f8ee4b6 100644 --- a/src/client.hh +++ b/src/client.hh @@ -1,11 +1,12 @@ #ifndef client_hh_INCLUDED #define client_hh_INCLUDED +#include "display_buffer.hh" +#include "env_vars.hh" +#include "input_handler.hh" +#include "safe_ptr.hh" #include "string.hh" #include "utils.hh" -#include "display_buffer.hh" -#include "input_handler.hh" -#include "env_vars.hh" namespace Kakoune { diff --git a/src/input_handler.hh b/src/input_handler.hh index 642b4243..dd027910 100644 --- a/src/input_handler.hh +++ b/src/input_handler.hh @@ -8,6 +8,7 @@ #include "keys.hh" #include "string.hh" #include "utils.hh" +#include "safe_ptr.hh" namespace Kakoune { diff --git a/src/safe_ptr.cc b/src/safe_ptr.cc new file mode 100644 index 00000000..c4d5f485 --- /dev/null +++ b/src/safe_ptr.cc @@ -0,0 +1,60 @@ +#include "safe_ptr.hh" + +#ifdef SAFE_PTR_TRACK_CALLSTACKS + +#include + +#if defined(__linux__) +# include +#elif defined(__CYGWIN__) +# include +#endif + + +namespace Kakoune +{ + + +SafeCountable::Backtrace::Backtrace() +{ + #if defined(__linux__) + num_frames = backtrace(stackframes, max_frames); + #elif defined(__CYGWIN__) + num_frames = CaptureStackBackTrace(0, max_frames, stackframes, nullptr); + #endif +} + +const char* SafeCountable::Backtrace::desc() const +{ + #if defined(__linux__) + char** symbols = backtrace_symbols(stackframes, num_frames); + int size = 0; + for (int i = 0; i < num_frames; ++i) + size += strlen(symbols[i]) + 1; + + char* res = (char*)malloc(size+1); + res[0] = 0; + for (int i = 0; i < num_frames; ++i) + { + strcat(res, symbols[i]); + strcat(res, "\n"); + } + free(symbols); + return res; + #elif defined(__CYGWIN__) + char* res = (char*)malloc(num_frames * 20); + res[0] = 0; + for (int i = 0; i < num_frames; ++i) + { + char addr[20]; + snprintf(addr, 20, "0x%p", stackframes[i]); + strcat(res, addr); + } + return res; + #else + return ""; + #endif +} + +} +#endif diff --git a/src/safe_ptr.hh b/src/safe_ptr.hh new file mode 100644 index 00000000..6a53a02e --- /dev/null +++ b/src/safe_ptr.hh @@ -0,0 +1,167 @@ +#ifndef safe_ptr_hh_INCLUDED +#define safe_ptr_hh_INCLUDED + +// #define SAFE_PTR_TRACK_CALLSTACKS + +#include "assert.hh" + +#ifdef SAFE_PTR_TRACK_CALLSTACKS + +#include +#include +#endif + +namespace Kakoune +{ + +// *** safe_ptr: objects that assert nobody references them when they die *** + +template +class safe_ptr +{ +public: + safe_ptr() : m_ptr(nullptr) {} + explicit safe_ptr(T* ptr) : m_ptr(ptr) + { + #ifdef KAK_DEBUG + if (m_ptr) + m_ptr->inc_safe_count(this); + #endif + } + safe_ptr(const safe_ptr& other) : safe_ptr(other.m_ptr) {} + safe_ptr(safe_ptr&& other) : m_ptr(other.m_ptr) + { + other.m_ptr = nullptr; + #ifdef KAK_DEBUG + if (m_ptr) + m_ptr->safe_ptr_moved(&other, this); + #endif + } + ~safe_ptr() + { + #ifdef KAK_DEBUG + if (m_ptr) + m_ptr->dec_safe_count(this); + #endif + } + + safe_ptr& operator=(const safe_ptr& other) + { + #ifdef KAK_DEBUG + if (m_ptr != other.m_ptr) + { + if (m_ptr) + m_ptr->dec_safe_count(this); + if (other.m_ptr) + other.m_ptr->inc_safe_count(this); + } + #endif + m_ptr = other.m_ptr; + return *this; + } + + safe_ptr& operator=(safe_ptr&& other) + { + #ifdef KAK_DEBUG + if (m_ptr) + m_ptr->dec_safe_count(this); + if (other.m_ptr) + other.m_ptr->safe_ptr_moved(&other, this); + #endif + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + return *this; + } + + void reset(T* ptr = nullptr) + { + *this = safe_ptr(ptr); + } + + bool operator== (const safe_ptr& other) const { return m_ptr == other.m_ptr; } + bool operator!= (const safe_ptr& other) const { return m_ptr != other.m_ptr; } + bool operator== (T* ptr) const { return m_ptr == ptr; } + bool operator!= (T* ptr) const { return m_ptr != ptr; } + + T& operator* () const { return *m_ptr; } + T* operator-> () const { return m_ptr; } + + T* get() const { return m_ptr; } + + explicit operator bool() const { return m_ptr; } + +private: + T* m_ptr; +}; + +class SafeCountable +{ +public: +#ifdef KAK_DEBUG + SafeCountable() : m_count(0) {} + ~SafeCountable() + { + kak_assert(m_count == 0); + #ifdef SAFE_PTR_TRACK_CALLSTACKS + kak_assert(m_callstacks.empty()); + #endif + } + + void inc_safe_count(void* ptr) const + { + ++m_count; + #ifdef SAFE_PTR_TRACK_CALLSTACKS + m_callstacks.emplace_back(ptr); + #endif + } + void dec_safe_count(void* ptr) const + { + --m_count; + kak_assert(m_count >= 0); + #ifdef SAFE_PTR_TRACK_CALLSTACKS + auto it = std::find_if(m_callstacks.begin(), m_callstacks.end(), + [=](const Callstack& cs) { return cs.ptr == ptr; }); + kak_assert(it != m_callstacks.end()); + m_callstacks.erase(it); + #endif + } + + void safe_ptr_moved(void* from, void* to) const + { + #ifdef SAFE_PTR_TRACK_CALLSTACKS + auto it = std::find_if(m_callstacks.begin(), m_callstacks.end(), + [=](const Callstack& cs) { return cs.ptr == from; }); + kak_assert(it != m_callstacks.end()); + it->ptr = to; + #endif + } + +private: + #ifdef SAFE_PTR_TRACK_CALLSTACKS + struct Backtrace + { + static constexpr int max_frames = 16; + void* stackframes[max_frames]; + int num_frames = 0; + + Backtrace(); + const char* desc() const; + }; + + struct Callstack + { + Callstack(void* p) : ptr(p) {} + void* ptr; + Backtrace bt; + }; + + mutable std::vector m_callstacks; + #endif + mutable int m_count; +#endif +}; + +} + +#endif // safe_ptr_hh_INCLUDED + diff --git a/src/user_interface.hh b/src/user_interface.hh index fdd03994..631309ad 100644 --- a/src/user_interface.hh +++ b/src/user_interface.hh @@ -4,7 +4,7 @@ #include "color.hh" #include "keys.hh" #include "memoryview.hh" -#include "utils.hh" +#include "safe_ptr.hh" namespace Kakoune { diff --git a/src/utils.hh b/src/utils.hh index 7d999fc6..e4d1bd08 100644 --- a/src/utils.hh +++ b/src/utils.hh @@ -53,92 +53,6 @@ private: template T* Singleton::ms_instance = nullptr; -// *** safe_ptr: objects that assert nobody references them when they die *** - -template -class safe_ptr -{ -public: - safe_ptr() : m_ptr(nullptr) {} - explicit safe_ptr(T* ptr) : m_ptr(ptr) - { - #ifdef KAK_DEBUG - if (m_ptr) - m_ptr->inc_safe_count(); - #endif - } - safe_ptr(const safe_ptr& other) : safe_ptr(other.m_ptr) {} - safe_ptr(safe_ptr&& other) : m_ptr(other.m_ptr) { other.m_ptr = nullptr; } - ~safe_ptr() - { - #ifdef KAK_DEBUG - if (m_ptr) - m_ptr->dec_safe_count(); - #endif - } - - safe_ptr& operator=(const safe_ptr& other) - { - #ifdef KAK_DEBUG - if (m_ptr != other.m_ptr) - { - if (m_ptr) - m_ptr->dec_safe_count(); - if (other.m_ptr) - other.m_ptr->inc_safe_count(); - } - #endif - m_ptr = other.m_ptr; - return *this; - } - - safe_ptr& operator=(safe_ptr&& other) - { - #ifdef KAK_DEBUG - if (m_ptr) - m_ptr->dec_safe_count(); - #endif - m_ptr = other.m_ptr; - other.m_ptr = nullptr; - return *this; - } - - void reset(T* ptr = nullptr) - { - *this = safe_ptr(ptr); - } - - bool operator== (const safe_ptr& other) const { return m_ptr == other.m_ptr; } - bool operator!= (const safe_ptr& other) const { return m_ptr != other.m_ptr; } - bool operator== (T* ptr) const { return m_ptr == ptr; } - bool operator!= (T* ptr) const { return m_ptr != ptr; } - - T& operator* () const { return *m_ptr; } - T* operator-> () const { return m_ptr; } - - T* get() const { return m_ptr; } - - explicit operator bool() const { return m_ptr; } - -private: - T* m_ptr; -}; - -class SafeCountable -{ -public: -#ifdef KAK_DEBUG - SafeCountable() : m_count(0) {} - ~SafeCountable() { kak_assert(m_count == 0); } - - void inc_safe_count() const { ++m_count; } - void dec_safe_count() const { --m_count; kak_assert(m_count >= 0); } - -private: - mutable int m_count; -#endif -}; - // *** Containers helpers *** template diff --git a/src/window.hh b/src/window.hh index 74d4fa37..8552c304 100644 --- a/src/window.hh +++ b/src/window.hh @@ -8,6 +8,7 @@ #include "hook_manager.hh" #include "option_manager.hh" #include "keymap_manager.hh" +#include "safe_ptr.hh" namespace Kakoune {