(svn r10391) -Add: debug support - structured dump of variables/classes/structures (with anti-recursion repository) into string
authorKUDr
Fri, 29 Jun 2007 23:30:14 +0000
changeset 7614 a70f01c9eb57
parent 7613 0940d162ab76
child 7615 42bf748ad323
(svn r10391) -Add: debug support - structured dump of variables/classes/structures (with anti-recursion repository) into string
projects/openttd.vcproj
projects/openttd_vs80.vcproj
source.list
src/misc/dbg_helpers.cpp
src/misc/dbg_helpers.h
--- a/projects/openttd.vcproj	Fri Jun 29 22:39:50 2007 +0000
+++ b/projects/openttd.vcproj	Fri Jun 29 23:30:14 2007 +0000
@@ -1182,6 +1182,12 @@
 				RelativePath=".\..\src\misc\crc32.hpp">
 			</File>
 			<File
+				RelativePath=".\..\src\misc\dbg_helpers.cpp">
+			</File>
+			<File
+				RelativePath=".\..\src\misc\dbg_helpers.h">
+			</File>
+			<File
 				RelativePath=".\..\src\misc\fixedsizearray.hpp">
 			</File>
 			<File
--- a/projects/openttd_vs80.vcproj	Fri Jun 29 22:39:50 2007 +0000
+++ b/projects/openttd_vs80.vcproj	Fri Jun 29 23:30:14 2007 +0000
@@ -1792,6 +1792,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\misc\dbg_helpers.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\misc\dbg_helpers.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\misc\fixedsizearray.hpp"
 				>
 			</File>
--- a/source.list	Fri Jun 29 22:39:50 2007 +0000
+++ b/source.list	Fri Jun 29 23:30:14 2007 +0000
@@ -369,6 +369,8 @@
 misc/blob.hpp
 misc/countedptr.hpp
 misc/crc32.hpp
+misc/dbg_helpers.cpp
+misc/dbg_helpers.h
 misc/fixedsizearray.hpp
 misc/hashtable.hpp
 misc/str.hpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/misc/dbg_helpers.cpp	Fri Jun 29 23:30:14 2007 +0000
@@ -0,0 +1,170 @@
+/* $Id:$ */
+
+/** @file dbg_helpers.cpp */
+#include "../stdafx.h"
+#include "../direction.h"
+#include "../rail.h"
+#include "../rail_map.h"
+#include "dbg_helpers.h"
+
+/** Trackdir & TrackdirBits short names. */
+static const char* trackdir_names[] = {
+	"NE", "SE", "UE", "LE", "LS", "RS", "rne", "rse",
+	"SW", "NW", "UW", "LW", "LN", "RN", "rsw", "rnw",
+};
+
+/** Return name of given Trackdir. */
+CStrA ValueStr(Trackdir td)
+{
+	CStrA out;
+	out.Format("%d (%s)", td, ItemAtT(td, trackdir_names, "UNK", INVALID_TRACKDIR, "INV"));
+	return out.Transfer();
+}
+
+/** Return composed name of given TrackdirBits. */
+CStrA ValueStr(TrackdirBits td_bits)
+{
+	CStrA out;
+	out.Format("%d (%s)", td_bits, ComposeNameT(td_bits, trackdir_names, "UNK", INVALID_TRACKDIR_BIT, "INV").Data());
+	return out.Transfer();
+}
+
+
+/** DiagDirection short names. */
+static const char* diagdir_names[] = {
+	"NE", "SE", "SW", "NW",
+};
+
+/** Return name of given DiagDirection. */
+CStrA ValueStr(DiagDirection dd)
+{
+	CStrA out;
+	out.Format("%d (%s)", dd, ItemAtT(dd, diagdir_names, "UNK", INVALID_DIAGDIR, "INV"));
+	return out.Transfer();
+}
+
+
+/** SignalType short names. */
+static const char* signal_type_names[] = {
+	"NORMAL", "ENTRY", "EXIT", "COMBO",
+};
+
+/** Return name of given SignalType. */
+CStrA ValueStr(SignalType t)
+{
+	CStrA out;
+	out.Format("%d (%s)", t, ItemAtT(t, signal_type_names, "UNK"));
+	return out.Transfer();
+}
+
+
+/** Translate TileIndex into string. */
+CStrA TileStr(TileIndex tile)
+{
+	CStrA out;
+	out.Format("0x%04X (%d, %d)", tile, TileX(tile), TileY(tile));
+	return out.Transfer();
+}
+
+/** Keep track of the last assigned type_id. Used for anti-recursion. */
+/*static*/ size_t& DumpTarget::LastTypeId()
+{
+	static size_t last_type_id = 0;
+	return last_type_id;
+}
+
+/** Return structured name of the current class/structure. */
+CStrA DumpTarget::GetCurrentStructName()
+{
+	CStrA out;
+	if (!m_cur_struct.empty()) {
+		// we are inside some named struct, return its name
+		out = m_cur_struct.top();
+	}
+	return out.Transfer();
+}
+
+/**
+ * Find the given instance in our anti-recursion repository.
+ * Return true and set name when object was found.
+ */
+bool DumpTarget::FindKnownName(size_t type_id, const void *ptr, CStrA &name)
+{
+	KNOWN_NAMES::const_iterator it = m_known_names.find(KnownStructKey(type_id, ptr));
+	if (it != m_known_names.end()) {
+		/* we have found it */
+		name = (*it).second;
+		return true;
+	}
+	return false;
+}
+
+/** Write some leading spaces into the output. */
+void DumpTarget::WriteIndent()
+{
+	int num_spaces = 2 * m_indent;
+	memset(m_out.GrowSizeNC(num_spaces), ' ', num_spaces);
+}
+
+/** Write a line with indent at the beginning and <LF> at the end. */
+void DumpTarget::WriteLine(const char *format, ...)
+{
+	WriteIndent();
+	va_list args;
+	va_start(args, format);
+	m_out.AddFormatL(format, args);
+	va_end(args);
+	m_out.AppendStr("\n");
+}
+
+/** Write 'name = value' with indent and new-line. */
+void DumpTarget::WriteValue(const char *name, const char *value_str)
+{
+	WriteIndent();
+	m_out.AddFormat("%s = %s\n", name, value_str);
+}
+
+/** Write name & TileIndex to the output. */
+void DumpTarget::WriteTile(const char *name, TileIndex tile)
+{
+	WriteIndent();
+	m_out.AddFormat("%s = %s\n", name, TileStr(tile).Data());
+}
+
+/**
+ * Open new structure (one level deeper than the current one) 'name = {<LF>'.
+ */
+void DumpTarget::BeginStruct(size_t type_id, const char *name, const void *ptr)
+{
+	/* make composite name */
+	CStrA cur_name = GetCurrentStructName().Transfer();
+	if (cur_name.Size() > 0) {
+		/* add name delimiter (we use structured names) */
+		cur_name.AppendStr(".");
+	}
+	cur_name.AppendStr(name);
+
+	/* put the name onto stack (as current struct name) */
+	m_cur_struct.push(cur_name);
+
+	/* put it also to the map of known structures */
+	m_known_names.insert(KNOWN_NAMES::value_type(KnownStructKey(type_id, ptr), cur_name));
+
+	WriteIndent();
+	m_out.AddFormat("%s = {\n", name);
+	m_indent++;
+}
+
+/**
+ * Close structure '}<LF>'.
+ */
+void DumpTarget::EndStruct()
+{
+	m_indent--;
+	WriteIndent();
+	m_out.AddFormat("}\n");
+
+	/* remove current struct name from the stack */
+	m_cur_struct.pop();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/misc/dbg_helpers.h	Fri Jun 29 23:30:14 2007 +0000
@@ -0,0 +1,166 @@
+/* $Id:$ */
+
+/** @file dbg_helpers.h */
+
+#ifndef DBG_HELPERS
+#define DBG_HELPERS
+
+#include <new>
+#include <map>
+#include <stack>
+
+#include "blob.hpp"
+#include "str.hpp"
+
+/** Helper template class that provides C array length and item type */
+template <typename T> struct ArrayT;
+
+/** Helper template class that provides C array length and item type */
+template <typename T, size_t N> struct ArrayT<T[N]> {
+	static const size_t length = N;
+	typedef T item_t;
+};
+
+
+/**
+* Helper template function that returns item of array at given index
+* or t_unk when index is out of bounds.
+*/
+template <typename E, typename T>
+inline typename ArrayT<T>::item_t ItemAtT(E idx, T &t, typename ArrayT<T>::item_t t_unk)
+{
+	if ((size_t)idx >= ArrayT<T>::length) {
+		return t_unk;
+	}
+	return t[idx];
+}
+
+/**
+* Helper template function that returns item of array at given index
+* or t_inv when index == idx_inv
+* or t_unk when index is out of bounds.
+*/
+template <typename E, typename T>
+inline typename ArrayT<T>::item_t ItemAtT(E idx, T &t, typename ArrayT<T>::item_t t_unk, E idx_inv, typename ArrayT<T>::item_t t_inv)
+{
+	if ((size_t)idx < ArrayT<T>::length) {
+		return t[idx];
+	}
+	if (idx == idx_inv) {
+		return t_inv;
+	}
+	return t_unk;
+}
+
+/**
+* Helper template function that returns compound bitfield name that is
+* concatenation of names of each set bit in the given value
+* or t_inv when index == idx_inv
+* or t_unk when index is out of bounds.
+*/
+template <typename E, typename T>
+inline CStrA ComposeNameT(E value, T &t, const char* t_unk, E val_inv, const char* name_inv)
+{
+	CStrA out;
+	if (value == val_inv) {
+		out = name_inv;
+	} else if (value == 0) {
+		out = "<none>";
+	} else {
+		for (int i = 0; i < ArrayT<T>::length; i++) {
+			if ((value & (1 << i)) == 0) continue;
+			out.AddFormat("%s%s", (out.Size() > 0 ? "+" : ""), t[i]);
+			value &= ~(E)(1 << i);
+		}
+		if (value != 0) out.AddFormat("%s%s", (out.Size() > 0 ? "+" : ""), t_unk);
+	}
+	return out.Transfer();
+}
+
+CStrA ValueStr(Trackdir td);
+CStrA ValueStr(TrackdirBits td_bits);
+CStrA ValueStr(DiagDirection dd);
+CStrA ValueStr(SignalType t);
+
+/** Class that represents the dump-into-string target. */
+struct DumpTarget {
+
+	/** Used as a key into map of known object instances. */
+	struct KnownStructKey {
+		size_t      m_type_id;
+		const void *m_ptr;
+
+		KnownStructKey(size_t type_id, const void *ptr)
+			: m_type_id(type_id)
+			, m_ptr(ptr)
+		{}
+
+		KnownStructKey(const KnownStructKey &src)
+		{
+			m_type_id = src.m_type_id;
+			m_ptr = src.m_ptr;
+		}
+
+		bool operator < (const KnownStructKey &other) const
+		{
+			if ((size_t)m_ptr < (size_t)other.m_ptr) return true;
+			if ((size_t)m_ptr > (size_t)other.m_ptr) return false;
+			if (m_type_id < other.m_type_id) return true;
+			return false;
+		}
+	};
+
+	typedef std::map<KnownStructKey, CStrA> KNOWN_NAMES;
+
+	CStrA              m_out;         ///< the output string
+	int                m_indent;      ///< current indent/nesting level
+	std::stack<CStrA>  m_cur_struct;  ///< here we will track the current structure name
+	KNOWN_NAMES        m_known_names; ///< map of known object instances and their structured names
+
+	DumpTarget()
+		: m_indent(0)
+	{}
+
+	static size_t& LastTypeId();
+	CStrA GetCurrentStructName();
+	bool FindKnownName(size_t type_id, const void* ptr, CStrA &name);
+
+	void WriteIndent();
+
+	void WriteLine(const char *format, ...);
+	void WriteValue(const char *name, const char *value_str);
+	void WriteTile(const char *name, TileIndex t);
+
+	/** Dump given enum value (as a number and as named value) */
+	template <typename E> void WriteEnumT(const char *name, E e)
+	{
+		WriteValue(name, ValueStr(e).Data());
+	}
+
+	void BeginStruct(size_t type_id, const char *name, const void *ptr);
+	void EndStruct();
+
+	/** Dump nested object (or only its name if this instance is already known). */
+	template <typename S> void WriteStructT(const char *name, const S *s)
+	{
+		static size_t type_id = ++LastTypeId();
+
+		if (s == NULL) {
+			/* No need to dump NULL struct. */
+			WriteLine("%s = <null>", name);
+			return;
+		}
+		CStrA known_as;
+		if (FindKnownName(type_id, s, known_as)) {
+			/* We already know this one, no need to dump it. */
+			WriteLine("%s = known_as.%s", name, known_as.Data());
+		} else {
+			/* Still unknown, dump it */
+			BeginStruct(type_id, name, s);
+			s->Dump(*this);
+			EndStruct();
+		}
+	}
+};
+
+#endif /* DBG_HELPERS */