Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/AST/Pass.impl.hpp	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -28,4 +28,6 @@
 	/* setup the scope for passes that want to run code at exit */ \
 	__attribute__((unused)) ast::__pass::guard_value          guard2( ast::__pass::at_cleanup    (pass, 0) ); \
+	/* begin tracing memory allocation if requested by this pass */ \
+	__pass::beginTrace( pass, 0 ); \
 	/* call the implementation of the previsit of this pass */ \
 	__pass::previsit( pass, node, 0 );
@@ -42,4 +44,6 @@
 	auto __return = __pass::postvisit( pass, node, 0 ); \
 	assertf(__return, "post visit should never return null"); \
+	/* end tracing memory allocation if requested by this pass */ \
+	__pass::endTrace( pass, 0 ); \
 	return __return;
 
Index: src/AST/Pass.proto.hpp
===================================================================
--- src/AST/Pass.proto.hpp	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/AST/Pass.proto.hpp	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -16,4 +16,6 @@
 #pragma once
 // IWYU pragma: private, include "Pass.hpp"
+
+#include "Common/Stats/Heap.h"
 
 namespace ast {
@@ -244,4 +246,20 @@
 	#undef FIELD_PTR
 
+	template< typename pass_t >
+	static inline auto beginTrace(pass_t & pass, int) -> decltype( pass_t::traceId, void() ) {
+		Stats::Heap::stacktrace_push(pass_t::traceId);
+	}
+
+	template< typename pass_t > 
+	static inline auto endTrace(pass_t & pass, int) -> decltype( pass_t::traceId, void() ) {
+		Stats::Heap::stacktrace_pop();
+	}
+
+	template< typename pass_t >
+	static void beginTrace(pass_t &, long) {}
+
+	template< typename pass_t >
+	static void endTrace(pass_t &, long) {}
+
 	// Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement.
 	// All passes which have such functions are assumed desire this behaviour
Index: src/AST/TypeSubstitution.cpp
===================================================================
--- src/AST/TypeSubstitution.cpp	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/AST/TypeSubstitution.cpp	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -18,4 +18,7 @@
 
 namespace ast {
+
+
+size_t TypeSubstitution::Substituter::traceId = Stats::Heap::new_stacktrace_id("TypeSubstitution");
 
 TypeSubstitution::TypeSubstitution() {
Index: src/AST/TypeSubstitution.hpp
===================================================================
--- src/AST/TypeSubstitution.hpp	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/AST/TypeSubstitution.hpp	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -160,4 +160,5 @@
 // definitition must happen after PassVisitor is included so that WithGuards can be used
 struct TypeSubstitution::Substituter : public WithGuards, public WithVisitorRef<Substituter> {
+		static size_t traceId;
 
 		Substituter( const TypeSubstitution & sub, bool freeOnly ) : sub( sub ), freeOnly( freeOnly ) {}
Index: src/Common/Stats/Heap.cc
===================================================================
--- src/Common/Stats/Heap.cc	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/Common/Stats/Heap.cc	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -53,4 +53,28 @@
 		const size_t passes_size = sizeof(passes) / sizeof(passes[0]);
 		size_t       passes_cnt = 1;
+
+		StatBlock    stacktrace_stats[100];
+		size_t       stacktrace_stats_count = 0;
+		bool         stacktrace_stats_enabled = true;
+
+		size_t       trace[1000];
+		const size_t stacktrace_max_depth = sizeof(trace) / sizeof(size_t);
+		size_t       stacktrace_depth;
+
+		size_t new_stacktrace_id(const char * const name) {
+			stacktrace_stats[stacktrace_stats_count].name = name;
+			return stacktrace_stats_count++;
+		}
+
+		void stacktrace_push(size_t id) {
+			++stacktrace_depth;
+			assertf(stacktrace_depth < stacktrace_max_depth, "Stack trace too deep: increase size of array in Heap.cc");
+			trace[stacktrace_depth] = id;
+		}
+
+		void stacktrace_pop() {
+			assertf(stacktrace_depth > 0, "Invalid stack tracing operation: trace is empty");
+			--stacktrace_depth;
+		}
 
 		void newPass( const char * const name ) {
@@ -116,4 +140,13 @@
 			for(size_t i = 0; i < passes_cnt; i++) {
 				print(passes[i], nc, total_mallocs, total_frees, overall_peak);
+			}
+
+			print('-', nct);
+			std::cerr << std::setw(nc) << "Trace";
+			std::cerr << " |       Malloc Count |         Free Count |        Peak Allocs |" << std::endl;
+
+			print('-', nct);
+			for (size_t i = 0; i < stacktrace_stats_count; i++) {
+				print(stacktrace_stats[i], nc, total_mallocs, total_frees, overall_peak);
 			}
 			print('-', nct);
@@ -188,4 +221,8 @@
 						= std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs);
 				}
+
+				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
+					stacktrace_stats[trace[stacktrace_depth]].mallocs++;
+				}
 				return __malloc( size );
 			}
@@ -196,4 +233,7 @@
 					passes[passes_cnt - 1].frees++;
 					passes[passes_cnt - 1].n_allocs--;
+				}
+				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
+					stacktrace_stats[trace[stacktrace_depth]].frees++;
 				}
 				return __free( ptr );
@@ -208,4 +248,7 @@
 						= std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs);
 				}
+				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
+					stacktrace_stats[trace[stacktrace_depth]].mallocs++;
+				}
 				return __calloc( nelem, size );
 			}
@@ -218,4 +261,8 @@
 					passes[passes_cnt - 1].frees++;
 				} // if
+				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
+					stacktrace_stats[trace[stacktrace_depth]].mallocs++;
+					stacktrace_stats[trace[stacktrace_depth]].frees++;
+				}
 				return s;
 			}
Index: src/Common/Stats/Heap.h
===================================================================
--- src/Common/Stats/Heap.h	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/Common/Stats/Heap.h	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -20,4 +20,8 @@
 		void newPass( const char * const name );
 		void print();
+
+		size_t new_stacktrace_id(const char * const name);
+		void stacktrace_push(size_t id);
+		void stacktrace_pop();
 	}
 }
Index: src/ResolvExpr/CandidateFinder.cpp
===================================================================
--- src/ResolvExpr/CandidateFinder.cpp	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -596,4 +596,5 @@
 		const ast::SymbolTable & symtab;
 	public:
+		static size_t traceId;
 		CandidateFinder & selfFinder;
 		CandidateList & candidates;
@@ -1507,4 +1508,5 @@
 	};
 
+	size_t Finder::traceId = Stats::Heap::new_stacktrace_id("Finder");
 	/// Prunes a list of candidates down to those that have the minimum conversion cost for a given
 	/// return type. Skips ambiguous candidates.
Index: src/ResolvExpr/CommonType.cc
===================================================================
--- src/ResolvExpr/CommonType.cc	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/ResolvExpr/CommonType.cc	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -666,4 +666,5 @@
 		const ast::OpenVarSet & open;
 	public:
+		static size_t traceId;
 		ast::ptr< ast::Type > result;
 
@@ -893,4 +894,5 @@
 	};
 
+	size_t CommonType_new::traceId = Stats::Heap::new_stacktrace_id("CommonType_new");
 	namespace {
 		ast::ptr< ast::Type > handleReference(
Index: src/ResolvExpr/ConversionCost.cc
===================================================================
--- src/ResolvExpr/ConversionCost.cc	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/ResolvExpr/ConversionCost.cc	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -795,5 +795,5 @@
 	}
 }
-
+size_t ConversionCost_new::traceId = Stats::Heap::new_stacktrace_id("ConversionCost");
 
 } // namespace ResolvExpr
Index: src/ResolvExpr/ConversionCost.h
===================================================================
--- src/ResolvExpr/ConversionCost.h	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/ResolvExpr/ConversionCost.h	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -88,4 +88,5 @@
 	CostCalculation costCalc;
 public:
+	static size_t traceId;
 	Cost cost;
 
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/ResolvExpr/Resolver.cc	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -1236,4 +1236,5 @@
 
 	public:
+		static size_t traceId;
 		Resolver_new() = default;
 		Resolver_new( const ast::SymbolTable & syms ) { symtab = syms; }
@@ -1266,4 +1267,5 @@
 		const ast::ConstructorInit * previsit( const ast::ConstructorInit * );
 	};
+	size_t Resolver_new::traceId = Stats::Heap::new_stacktrace_id("Resolver");
 
 	void resolve( std::list< ast::ptr< ast::Decl > >& translationUnit ) {
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision 276f105254aa4738569d87456723e9b72f7a5a42)
+++ src/ResolvExpr/Unify.cc	(revision c15085d61a6b626ea93abfe66835758c4493d486)
@@ -702,4 +702,5 @@
 		const ast::SymbolTable & symtab;
 	public:
+		static size_t traceId;
 		bool result;
 
@@ -1135,4 +1136,5 @@
 	};
 
+	size_t Unify_new::traceId = Stats::Heap::new_stacktrace_id("Unify_new");
 	bool unify(
 			const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
