mmgc-graphviz
author jorendorff@mozilla.com
Wed, 28 Nov 2007 10:09:48 -0600
changeset 7 66abaa75a3727ffeba67074a9210006ecdcb1e64
child 9 01b3ffa4acb6f282cd673c1ac70c925355b4b93d
permissions -rw-r--r--
First check-in, pretty horrible

diff --git a/MMgc/GC.cpp b/MMgc/GC.cpp
--- a/MMgc/GC.cpp
+++ b/MMgc/GC.cpp
@@ -280,6 +280,9 @@ namespace MMgc
 #else
 		  disableThreadCheck(false)
 #endif
+#ifdef MMGC_GRAPHVIZ
+		  , m_gvFile(NULL)
+#endif
 	{
 		// sanity check for all our types
 		GCAssert (sizeof(int8) == 1);
@@ -352,6 +355,10 @@ namespace MMgc
 
 	GC::~GC()
 	{
+#ifdef MMGC_GRAPHVIZ
+		StopGraphing();
+#endif
+
 		// Force all objects to be destroyed
 		destroying = true;
 		ClearMarks();
@@ -545,7 +552,19 @@ namespace MMgc
 				cb->enterExclusiveGCNoLock();
 		}
 
+#ifdef MMGC_GRAPHVIZ
+		if (m_gvFile) {
+			fprintf(m_gvFile, "digraph C%d {\n", sweeps);
+			fprintf(m_gvFile, "    node [shape=box];\n");
+		}
+#endif
+
 		CollectImpl();
+
+#ifdef MMGC_GRAPHVIZ
+		if (m_gvFile)
+			fprintf(m_gvFile, "}\n");
+#endif
 
 #ifdef MMGC_THREADSAFE
 		m_lock.Acquire();
@@ -619,6 +638,46 @@ namespace MMgc
 #endif
 	}
 
+#define GRAPHVIZ_ROOT_STYLE "style=\"filled\", color=\"#ccddbb\""
+#define GRAPHVIZ_OBJECT_STYLE "style=\"filled\", color=\"#b3cc99\""
+#define GRAPHVIZ_CONSERVATIVE_EDGE_STYLE "color=\"#808080\""
+
+#ifdef MMGC_GRAPHVIZ
+	std::string Quote(const std::string &s)
+	{
+		// XXX BOGUS FIXME - should at least escape quotes and newlines
+		return '"' + s + '"';
+	}
+
+	void GC::GraphNode(const void *obj, const char *style)
+	{
+		if (m_gvFile != NULL && obj != NULL) {
+			if (IsGCMemory(obj)) {
+				std::string label;
+				if (IsFinalized(obj)) {
+					label = ((const GCFinalizedObject *) obj)->GetRepr();
+				} else {
+					label = typeid(*(const GCObject *) obj).name();
+				}
+				label = Quote(label);
+				fprintf(m_gvFile, "    P%p [label=%s, %s];\n",
+						obj, label.c_str(), style);
+			} else {
+				fprintf(m_gvFile, "    P%p [%s];\n", obj, style);
+			}
+		}
+	}
+
+	void GC::GraphEdge(const void *fromObj,
+					   const void *fromField,
+					   const void *toObj,
+					   const char *style)
+	{
+		if (m_gvFile)
+			fprintf(m_gvFile, "    P%p -> P%p [%s];\n", fromObj, toObj, style);
+	}
+#endif
+
 	void GC::Trace(const void *stackStart/*=NULL*/, size_t stackSize/*=0*/)
 	{
 		ASSERT_EXCLUSIVE_GC();
@@ -638,6 +697,16 @@ namespace MMgc
 			GCRoot *r = m_roots;
 			while(r) {
 				GCWorkItem item = r->GetWorkItem();
+#ifdef MMGC_GRAPHVIZ
+				if (m_gvFile) {
+					fprintf(m_gvFile,
+							"    P%p [label=\"root at %p\", %s];\n",
+							item.ptr,
+							item.ptr,
+							GRAPHVIZ_ROOT_STYLE);
+				}
+#endif
+
 				MarkItem(item, work);
 				r = r->next;
 			}
@@ -2248,7 +2317,6 @@ bail:
 #undef ALLOCA_AND_FILL_WITH_SPACES
 #endif
 
-
 	void GC::StartIncrementalMark()
 	{
 		GCAssert(!marking);
@@ -2406,6 +2474,11 @@ bail:
 
 	void GC::MarkItem(GCWorkItem &wi, GCStack<GCWorkItem> &work)
 	{
+#ifdef MMGC_GRAPHVIZ
+		if (wi.IsGCItem())
+			GraphNode(wi.ptr, GRAPHVIZ_OBJECT_STYLE);
+#endif
+
 		size_t size = wi.GetSize();
 		uintptr *p = (uintptr*) wi.ptr;
 
@@ -2486,6 +2559,9 @@ bail:
 				{
 					if(block->alloc->ContainsPointers())
 					{
+#ifdef MMGC_GRAPHVIZ
+						GraphEdge(wi.ptr, p, item, GRAPHVIZ_CONSERVATIVE_EDGE_STYLE);
+#endif
 						// try custom marking
 						if ((bits2 & (GCAlloc::kFinalize<<shift)) != 0
 							&& ((GCFinalizedObject *) GetUserPointer(item))->CustomMark())
@@ -2545,6 +2621,9 @@ bail:
 						size_t usize = b->usableSize;
 						if((b->flags & GCLargeAlloc::kContainsPointers) != 0) 
 						{
+#ifdef MMGC_GRAPHVIZ
+							GraphEdge(wi.ptr, p, item, GRAPHVIZ_CONSERVATIVE_EDGE_STYLE);
+#endif
 							// try custom marking
 							if ((b->flags & GCLargeAlloc::kFinalizeFlag) != 0
 								&& ((GCFinalizedObject *) GetUserPointer(item))->CustomMark())
@@ -3169,5 +3248,4 @@ bail:
 	}
 #endif
 
-
 }
diff --git a/MMgc/GC.h b/MMgc/GC.h
--- a/MMgc/GC.h
+++ b/MMgc/GC.h
@@ -38,6 +38,10 @@
 
 #ifndef __GC__
 #define __GC__
+
+#ifdef MMGC_GRAPHVIZ
+#include <typeinfo>
+#endif
 
 #if defined MMGC_IA32
 
@@ -1515,8 +1519,8 @@ private:
 #endif
 #endif
 
-public:
 #ifdef MEMORY_INFO
+	public:
 		void DumpBackPointerChain(void *o);
 
 		// debugging routine that records who marked who, can be used to
@@ -1526,6 +1530,7 @@ public:
 		static void WriteBackPointer(const void *item, const void *container, size_t itemSize);
 #endif
 #ifdef _DEBUG
+	public:
 		// Dump a list of objects that have pointers to the given location.
 		void WhosPointingAtMe(void* me, int recurseDepth=0, int currentDepth=0);
 
@@ -1539,8 +1544,9 @@ public:
 #ifdef MMGC_THREADSAFE
 	public:
 		/**
-		 * True if marking or sweeping is happening.  This implies that no
-		 * threads in requests are running right now.
+		 * True if marking or sweeping is happening.  In an MMGC_THREADSAFE
+		 * build, this implies that no threads in requests are running right
+		 * now.
 		 *
 		 * Contrast Collecting().
 		 *
@@ -1664,6 +1670,31 @@ public:
 		GCThread *m_exclusiveGCThread;
 
 		/**
+		 * This is notified whenever m_exclusiveGCThread becomes NULL.
+		 *
+		 * @access Requires(m_lock)
+		 */
+		GCCondition m_condDone;
+
+		/**
+		 * The number of threads currently in active requests.
+		 *
+		 * @access Requires(m_lock)
+		 */
+		int m_requestCount;
+
+		/**
+		 * This is notified whenever m_requestCount becomes zero.
+		 *
+		 * At most one thread is ever waiting on this condition at a time; if
+		 * a thread is waiting on it, that thread is `m_exclusiveGCThread` and
+		 * `m_gcRunning` is false.
+		 *
+		 * @access Requires(m_lock)
+		 */
+		GCCondition m_condNoRequests;
+
+		/**
 		 * True if a thread is doing GC-related work, having already ensured
 		 * that no threads are in active requests.
 		 *
@@ -1674,31 +1705,31 @@ public:
 		 * @access ReadWrite(request || exclusiveGC || m_lock, exclusiveGC && m_lock)
 		 */
 		bool m_gcRunning;
-
-		/**
-		 * This is notified whenever m_exclusiveGCThread becomes NULL.
-		 *
-		 * @access Requires(m_lock)
-		 */
-		GCCondition m_condDone;
-
-		/**
-		 * The number of threads currently in active requests.
-		 *
-		 * @access Requires(m_lock)
-		 */
-		int m_requestCount;
-
-		/**
-		 * This is notified whenever m_requestCount becomes zero.
-		 *
-		 * At most one thread is ever waiting on this condition at a time; if
-		 * a thread is waiting on it, that thread is `m_exclusiveGCThread` and
-		 * `m_gcRunning` is false.
-		 *
-		 * @access Requires(m_lock)
-		 */
-		GCCondition m_condNoRequests;
+#endif
+
+#ifdef MMGC_GRAPHVIZ
+	public:
+		void StartGraphing(const char *filename)
+		{
+			StopGraphing();
+			m_gvFile = fopen(filename, "w");
+		}
+
+		void StopGraphing()
+		{
+			if (m_gvFile)
+				fclose(m_gvFile);
+		}
+
+		void GraphNode(const void *obj, const char *style);
+
+		void GraphEdge(const void *fromObj,
+					   const void *fromField,
+					   const void *toObj,
+					   const char *style);
+
+	private:
+		FILE *m_gvFile;
 #endif
 	};
 
diff --git a/MMgc/GCObject.cpp b/MMgc/GCObject.cpp
--- a/MMgc/GCObject.cpp
+++ b/MMgc/GCObject.cpp
@@ -60,6 +60,19 @@ namespace MMgc
 		GC::GetGC(gcObject)->Free(gcObject);
 	}		
 
+	std::string GCFinalizedObject::GetRepr() const
+	{
+		std::string s;
+		char buf[60];
+		size_t n;
+
+		n = snprintf(buf, 60, "%s at %p",
+							typeid(*this).name(),
+							dynamic_cast<void *>(this));
+		s.assign(buf, n);
+		return s;
+	}
+
 	void* GCFinalizedObjectOptIn::operator new(size_t size, GC *gc, size_t extra)
 	{
 		return gc->Alloc(size + extra, GC::kContainsPointers|GC::kZero, 4);
diff --git a/MMgc/GCObject.h b/MMgc/GCObject.h
--- a/MMgc/GCObject.h
+++ b/MMgc/GCObject.h
@@ -136,6 +136,8 @@ namespace MMgc
 		 */
 		virtual bool CustomMark() { return false; }
 
+		virtual std::string GetRepr() const;
+
 		static void *operator new(size_t size, GC *gc, size_t extra = 0);
 		static void operator delete (void *gcObject);
 	};
diff --git a/MMgc/macbuild.h b/MMgc/macbuild.h
--- a/MMgc/macbuild.h
+++ b/MMgc/macbuild.h
@@ -66,6 +66,7 @@
  */
 #ifdef DEBUG
 #define MEMORY_INFO
+#define MMGC_GRAPHVIZ
 #endif
 
 /**