gc-graph
author Benjamin Smedberg <benjamin@smedbergs.us>
Sun, 20 Apr 2008 21:43:09 -0400
changeset 42 53c5098cb79e0223016b6901194a89d913f235fb
parent 6 b0246d10657e45b3622cad612f53a2b6d5efce1d
permissions -rw-r--r--
Updates and imports

diff --git a/MMgc/GC.cpp b/MMgc/GC.cpp
--- a/MMgc/GC.cpp
+++ b/MMgc/GC.cpp
@@ -113,6 +113,11 @@ namespace MMgc
 namespace MMgc
 {
 
+	static const char kGraphvizRootStyle[] = "style=\"filled\", color=\"#ccddbb\"";
+	static const char kGraphvizStackStyle[] = "style=\"filled\", color=\"#bbddcc\"";
+	static const char kGraphvizObjectStyle[] = "style=\"filled\", color=\"#b3cc99\"";
+	static const char kGraphvizConservativeEdge[] = "color=\"#808080\"";
+
 #ifdef MMGC_DRC
 
 	// how many objects trigger a reap, should be high
@@ -257,6 +262,8 @@ namespace MMgc
 		  finalizedValue(true),
 		  // Expand, don't collect, until we hit this threshold
 		  collectThreshold(256)
+		, m_gvFile(NULL)
+		  
 	{		
 		// sanity check for all our types
 		GCAssert (sizeof(int8) == 1);
@@ -325,10 +332,15 @@ namespace MMgc
 		// keep GC::Size honest
 		GCAssert(offsetof(GCLargeAlloc::LargeBlock, usableSize) == offsetof(GCAlloc::GCBlock, size));
 
+		const char *gvFile = getenv("MMGC_GRAPHVIZ_LOG");
+		if (gvFile)
+			StartGraphing(gvFile);
 	}
 
 	GC::~GC()
 	{
+		StopGraphing();
+
 		// Force all objects to be destroyed
 		destroying = true;
 		ClearMarks();
@@ -381,6 +393,12 @@ namespace MMgc
 
 	void GC::Collect()
 	{
+		static int cnum = 0;
+
+		if (m_gvFile)
+			fprintf(m_gvFile, "subgraph GC%i {\n", ++cnum);
+			
+
 		// invoke precollect callback
 		bool vetoed = false;
 		GCCallback *cb = m_callbacks;
@@ -434,6 +452,8 @@ namespace MMgc
 #ifdef DEBUGGER
 		StopGCActivity();
 #endif
+		if (m_gvFile)
+			fprintf(m_gvFile, "}\n");
 	}
 
 	void GC::Trace(const void *stackStart/*=NULL*/, size_t stackSize/*=0*/)
@@ -455,6 +475,15 @@ namespace MMgc
 			GCRoot *r = m_roots;
 			while(r) {
 				GCWorkItem item = r->GetWorkItem();
+
+				if (m_gvFile) {
+					fprintf(m_gvFile,
+						"    P%p [label=\"root at %p\", %s];\n",
+						item.ptr,
+						item.ptr,
+						(const char*) kGraphvizRootStyle);
+				}
+
 				MarkItem(item, work);
 				r = r->next;
 			}
@@ -1034,6 +1063,14 @@ bail:
 		// this is where we will clear to when CleanStack is called
 		if(rememberedStackTop == 0 || rememberedStackTop > item.ptr) {
 			rememberedStackTop = item.ptr;
+		}
+
+		if (m_gvFile) {
+			fprintf(m_gvFile,
+				"    P%p [label=\"stack at %p\", %s];\n",
+				item.ptr,
+				item.ptr,
+				(const char*) kGraphvizStackStyle);
 		}
 
 		PushWorkItem(work, item);
@@ -2176,6 +2213,8 @@ bail:
 		// set the mark bits on this guy
 		if(wi.IsGCItem())
 		{
+			GraphNode(wi.ptr, kGraphvizObjectStyle);
+
 			int b = SetMark(wi.ptr);
 			(void)b;
 #ifdef _DEBUG
@@ -2250,6 +2289,8 @@ bail:
 				//if(GCAlloc::IsWhite(block, itemNum)) 
 				if((bits2 & ((GCAlloc::kMark|GCAlloc::kQueued)<<shift)) == 0)
 				{
+					GraphEdge(wi.ptr, p, item, kGraphvizConservativeEdge);
+
 					if(block->alloc->ContainsPointers())
 					{
 						// try custom marking
@@ -2324,6 +2365,8 @@ bail:
 				GCLargeAlloc::LargeBlock *b = GCLargeAlloc::GetBlockHeader(item);
 				if((b->flags & (GCLargeAlloc::kQueuedFlag|GCLargeAlloc::kMarkFlag)) == 0) 
 				{
+					GraphEdge(wi.ptr, p, item, kGraphvizConservativeEdge);
+
 					if (m_edgeCallbacks)
 						FireFoundEdgeTo(item);
 
@@ -2931,5 +2974,35 @@ bail:
 	}
 #endif
 
-
+	void GC::StartGraphing(const char *filename)
+	{
+		StopGraphing();
+		m_gvFile = fopen(filename, "w");
+		fprintf(m_gvFile, "digraph GC {\n    node [shape=box];\n");
+	}
+
+	void GC::StopGraphing()
+	{
+		if (m_gvFile) {
+			fprintf(m_gvFile, ";\n");
+
+			fclose(m_gvFile);
+			m_gvFile = NULL;
+		}
+	}
+
+	void GC::GraphNode(const void *obj, const char *style)
+	{
+		if (m_gvFile)
+			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);
+	}
 }
diff --git a/MMgc/GC.h b/MMgc/GC.h
--- a/MMgc/GC.h
+++ b/MMgc/GC.h
@@ -1139,6 +1139,18 @@ public:
 		void WhosPointingAtMe(void* me, int recurseDepth=0, int currentDepth=0);
     	void ProbeForMatch(const void *mem, size_t size, uintptr value, int recurseDepth, int currentDepth);
 #endif
+
+	public:
+		void StartGraphing(const char *filename);
+		void StopGraphing();
+		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;
 	};
 
 	// helper class to wipe out vtable pointer of members for DRC
diff --git a/MMgc/GCTypes.h b/MMgc/GCTypes.h
--- a/MMgc/GCTypes.h
+++ b/MMgc/GCTypes.h
@@ -42,6 +42,8 @@
 #ifdef _MAC
 #include <stdint.h>
 #endif
+
+#include <stdio.h>
 
 #if defined(HAVE_VISIBILITY_ATTRIBUTE)
 #define MMGC_VISIBILITY_DEFAULT __attribute__ ((visibility ("default")))