summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2014-08-12 00:30:13 +0100
committerMaxime Coste <frrrwww@gmail.com>2014-08-12 19:18:10 +0100
commitd356ae241986282cf33a31874da1d3139960b63d (patch)
tree651bb910fa69fec45c9ffc09a47f6096ed492809 /src
parent1b54b65bb59b46718d7485e690835faf695b6410 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile2
-rw-r--r--src/buffer.hh1
-rw-r--r--src/buffer_manager.hh1
-rw-r--r--src/client.hh7
-rw-r--r--src/input_handler.hh1
-rw-r--r--src/safe_ptr.cc60
-rw-r--r--src/safe_ptr.hh167
-rw-r--r--src/user_interface.hh2
-rw-r--r--src/utils.hh86
-rw-r--r--src/window.hh1
10 files changed, 237 insertions, 91 deletions
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 <unordered_map>
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 "string.hh"
-#include "utils.hh"
#include "display_buffer.hh"
-#include "input_handler.hh"
#include "env_vars.hh"
+#include "input_handler.hh"
+#include "safe_ptr.hh"
+#include "string.hh"
+#include "utils.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 <string.h>
+
+#if defined(__linux__)
+# include <execinfo.h>
+#elif defined(__CYGWIN__)
+# include <windows.h>
+#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 "<not implemented>";
+ #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 <vector>
+#include <algorithm>
+#endif
+
+namespace Kakoune
+{
+
+// *** safe_ptr: objects that assert nobody references them when they die ***
+
+template<typename T>
+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<Callstack> 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<typename T>
T* Singleton<T>::ms_instance = nullptr;
-// *** safe_ptr: objects that assert nobody references them when they die ***
-
-template<typename T>
-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<typename Container>
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
{