[cfe-dev] [Templight] Templight "v2" with gdb-style debugger

Mikael Persson mikael.s.persson at gmail.com
Tue Mar 11 18:28:21 PDT 2014


Hi all,

I recently picked up the Templight code (see
http://plc.inf.elte.hu/templight/) because I love the idea of having proper
debugging and profiling tools for template meta-programming in C++. As
admitted by the original authors (Zoltán Borók-Nagy, Zoltán Porkoláb
and József Mihalicza), the code was rather crude, but a great first step.

-- Templight "version 2" --  (patch: templight_v2_clang35.diff)

So, I refactored the code to make it a bit more modular and less intrusive
on the Sema class. Here is an outline of the main changes included in the
attached patch (from Clang r202671):

Changes to existing Clang code-base:

 (1) Moved the "ActiveTemplateInstantiation" class out of the Sema class
scope, and into the clang namespace-scope. This was done to make the
templight code independent from the Sema class by allowing a
forward-declaration of ActiveTemplateInstantiation (can't be done if it is
nested in Sema).

 (2) Minor updates to reflect the above change in scope of
the ActiveTemplateInstantiation class.

 (3) Created a "TemplateInstantiationObserver" base-class which bundles
callback functions that are called during the template instantiations (and
when leaving them). This class also supports linked-list-style chaining of
additional observers.

 (4) Added a OwningPtr<TemplateInstantiationObserver> in the Sema class to
hold the chain of template instantiation observers.

 (5) Added callbacks for begin / end of template instantiations. These were
added in all the places where the original Templight code had templight
calls. This replaces the original templight tracing calls.

 (6) Added calls to initialize and finalize the template instantiation
observers in the ParseAST code (see issues below).

 (7) Added front-end options for Templight (mostly the same as in the
original templight patch).

Changes to the Templight implementation: (unless noted here, the behavior
is the same)
N.B.: The output (yaml, xml, txt) is *identical* to the original Templight
code.

 (8) All the templight code was moved out of the Sema class and into its
own cpp-file. This was done mainly to avoid additional pollution in the
Sema class, and to isolate the templight code's implementation dependencies
(llvm support libs, yaml output code, etc.).

 (9) Created the TemplightTracer class (derived from
TemplateInstantiationObserver) to expose the functionality of Templight.
This class is PImpl'd.

 (10) Removed the "TemplightFlag" in favor of having created the tracer or
not.

 (11) Removed the "-templight-capacity" option because I changed the
non-safe-mode to record the instantiation traces up to when the
instantiations come back to the top-level context, at which point, I dump
the traces. This will not affect the profiling because at the top-level
context there is no pending timing or memory to be recorded. With this
change, the number of traces recorded become proportional to the maximum
instantiation depth (which is limited by the compiler), therefore making
the templight-capacity redundant.

 (12) Changed the recording of the traces to be stored in a std::vector, as
opposed to a bare dynamic array.

 (13) Added the "-templight-ignore-system" option to ignore (not traced)
any template instantiation coming from a system-include header file.

 (14) Updated the "creation" code where front-end options are pulled and
used to create the tracer.


-- Templight GDB-style Debugger --  (patch:
templight_v2_clang35_with_debugger.diff)

Here is the really sweet part. When I realized how easy it would be to
create a gdb-style debugger with the new layout that I described above, I
just had to give it a go, and the result is pretty nice, albeit still
crude. I welcome you to try it!

I added the option "-templight-debugger" which turns on an interactive
debugging session during the template instantiations. This is implemented
in the TemplightDebugger class (also PImpl'd, and also deriving
from TemplateInstantiationObserver). The debugger respects the relevant
templight options (memory and ignore-system) and can be used in parallel
with the templight tracer (although timing values will obviously be
useless).

The debugger implements most of the basic gdb commands and reproduces its
behavior as much as possible. You can set breakpoints (by complete name of
template instantiation), you can "step", "next", "run", "kill", etc.. with
the same behavior as gdb. You can print back-traces. And each time a
template instantiation is entered or left, there is a structured print out
similar to gdb, making it usable (I hope) in a GUI front-end similar to the
various GUI front-ends to gdb.

This is really just a first draft of it, it's quite crude. I use C-style
functions for the console input to avoid pulling in the C++
iostream monster (as required in coding standards of LLVM). And, as usual
with interactive programs, it's hard to make sure that it is robust to
people entering random nonsense.

Here are a few issues I'd like some feedback on:

 (15) I feel that the XML output of the templight tracer could easily be
nested, as opposed to having matched "Begin" and "End" blocks.

 (16) I feel a bit uneasy about the place where the tracer / debugger get
created, initialized and finalized. Currently, this is done in the
"createSema" function and in the ParseAST code. It works this way, but it
feels out-of-place. I don't know the Clang code-base well enough (in fact,
barely at all) to know where this should go. Ideally, I would also like to
get rid of the initialize / finalize functions in
the TemplateInstantiationObserver class, but that is impossible as long as
the Sema object and most of Clang's main structures are left to leak
(always-on "-delete-free" option), as RAII is no longer available as a
mechanism for handling the clean up. If anyone that understands Clang's
main structures could weight in on this, I'd be glad to hear suggestions.

 (17) Is there anything in LLVM/Clang that can help in terms of doing
console input? Currently, I use <cstdio> functions and hand-written
"tokenizing".

 (18) I feel like the template debugger should be a separate entity (not an
option to the main clang compiler), but at the same time, it needs the
entire compilation process to be running, and so, if separate, it would
have to be duplicate of clang with only small additions (in terms of code).

 (19) Finally, I'd like to have ideas about what kind of diagnostic
information that could be added to the debugger. Currently, it only prints
out the name of the template instantiation, the location, and total memory
usage. It would be nice to have features similar to GDB's "print" commands,
like being able to print the list of template arguments and their values
within the current context, and things like that. I simply don't have
enough knowledge of Clang's code to know how to tap into that kind of
information.


I know this was a long email, but I hope you had the interest to read up to
here, and I hope to hear back.

Cheers,
Mikael.


-- 
Sven Mikael Persson, M.Sc.(Tech.)
PhD Candidate, McGill University,
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20140311/37840519/attachment.html>
-------------- next part --------------
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td	(revision 202671)
+++ include/clang/Driver/Options.td	(working copy)
@@ -152,6 +152,33 @@
   HelpText<"Emit ARC errors even if the migrator can fix them">,
   Flags<[CC1Option]>;
 
+// BEGIN TEMPLIGHT
+//===----------------------------------------------------------------------===//
+// Templight Options
+//===----------------------------------------------------------------------===//
+  
+def templight : Flag<["-"], "templight">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"For tracing template instantiations">; 
+  
+def templight_stdout : Flag<["-"], "templight-stdout">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"Tracing template instantiations to standard output">;
+  
+def templight_memory : Flag<["-"], "templight-memory">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"For tracing memory usage during template instantiations">;
+  
+def templight_safe_mode : Flag<["-"], "templight-safe-mode">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"For flushing Templight trace immediately instead of store the trace in a buffer">;
+  
+def templight_ignore_system : Flag<["-"], "templight-ignore-system">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"For ignoring any template instantiation that comes from a system header">;
+  
+def templight_output : JoinedOrSeparate<["-"], "templight-output">, Flags<[DriverOption, RenderAsInput, CC1Option]>,
+  HelpText<"Write Templight output to <file>">, MetaVarName<"<file>">;
+  
+def templight_format : JoinedOrSeparate<["-"], "templight-format">, Flags<[DriverOption, RenderAsInput, CC1Option]>,
+  HelpText<"Format of Templight output (yaml/xml/text, default is yaml)">, MetaVarName<"<format>">;
+// END TEMPLIGHT
+
 def _migrate : Flag<["--"], "migrate">, Flags<[DriverOption]>,
   HelpText<"Run the migrator">;
 def ccc_objcmt_migrate : Separate<["-"], "ccc-objcmt-migrate">,
Index: include/clang/Frontend/FrontendOptions.h
===================================================================
--- include/clang/Frontend/FrontendOptions.h	(revision 202671)
+++ include/clang/Frontend/FrontendOptions.h	(working copy)
@@ -145,6 +145,22 @@
   unsigned ASTDumpLookups : 1;             ///< Whether we include lookup table
                                            ///< dumps in AST dumps.
 
+  // BEGIN TEMPLIGHT
+  unsigned Templight : 1;                  ///< For Tracing template
+                                           /// instantiations
+  unsigned TemplightStdout : 1;            ///< Tracing template instantiations
+                                           /// to stdout
+  unsigned TemplightMemory : 1;            ///< For tracing memory usage
+                                           /// during template instantiations
+                                           /// This indicates Templight
+  unsigned TemplightSafeMode : 1;          ///< For flushing the Templight
+                                           /// trace immediately instead of
+                                           /// store it in a buffer
+  unsigned TemplightIgnoreSystem : 1;      ///< For ignoring any template 
+                                           /// instantiation that comes from
+                                           /// a system header.
+  // END TEMPLIGHT
+
   CodeCompleteOptions CodeCompleteOpts;
 
   enum {
@@ -201,6 +217,14 @@
   /// The output file, if any.
   std::string OutputFile;
 
+  // BEGIN TEMPLIGHT
+  /// Output file fo Templight, if any.
+  std::string TemplightOutputFile;
+
+  /// Format of Templight output (yaml/xml/text)
+  std::string TemplightFormat;
+  // END TEMPLIGHT
+
   /// If given, the new suffix for fix-it rewritten files.
   std::string FixItSuffix;
 
Index: include/clang/Sema/ActiveTemplateInst.h
===================================================================
--- include/clang/Sema/ActiveTemplateInst.h	(revision 0)
+++ include/clang/Sema/ActiveTemplateInst.h	(working copy)
@@ -0,0 +1,126 @@
+
+#ifndef LLVM_CLANG_ACTIVE_TEMPLATE_INST_H
+#define LLVM_CLANG_ACTIVE_TEMPLATE_INST_H
+
+#include "clang/Basic/SourceLocation.h"
+
+namespace clang {
+  
+  class Decl;
+  class NamedDecl;
+  class TemplateArgument;
+
+namespace sema {
+  class TemplateDeductionInfo;
+}
+
+
+/// \brief A template instantiation that is currently in progress.
+class ActiveTemplateInstantiation {
+public:
+  /// \brief The kind of template instantiation we are performing
+  enum InstantiationKind {
+    /// We are instantiating a template declaration. The entity is
+    /// the declaration we're instantiating (e.g., a CXXRecordDecl).
+    TemplateInstantiation,
+
+    /// We are instantiating a default argument for a template
+    /// parameter. The Entity is the template, and
+    /// TemplateArgs/NumTemplateArguments provides the template
+    /// arguments as specified.
+    /// FIXME: Use a TemplateArgumentList
+    DefaultTemplateArgumentInstantiation,
+
+    /// We are instantiating a default argument for a function.
+    /// The Entity is the ParmVarDecl, and TemplateArgs/NumTemplateArgs
+    /// provides the template arguments as specified.
+    DefaultFunctionArgumentInstantiation,
+
+    /// We are substituting explicit template arguments provided for
+    /// a function template. The entity is a FunctionTemplateDecl.
+    ExplicitTemplateArgumentSubstitution,
+
+    /// We are substituting template argument determined as part of
+    /// template argument deduction for either a class template
+    /// partial specialization or a function template. The
+    /// Entity is either a ClassTemplatePartialSpecializationDecl or
+    /// a FunctionTemplateDecl.
+    DeducedTemplateArgumentSubstitution,
+
+    /// We are substituting prior template arguments into a new
+    /// template parameter. The template parameter itself is either a
+    /// NonTypeTemplateParmDecl or a TemplateTemplateParmDecl.
+    PriorTemplateArgumentSubstitution,
+
+    /// We are checking the validity of a default template argument that
+    /// has been used when naming a template-id.
+    DefaultTemplateArgumentChecking,
+
+    /// We are instantiating the exception specification for a function
+    /// template which was deferred until it was needed.
+    ExceptionSpecInstantiation,
+    
+    /// Added for Template instantiation observation
+    /// Memoization means we are _not_ instantiating a template because 
+    /// it is already instantiated (but we entered a context where we 
+    /// would have had to if it was not already instantiated).
+    Memoization
+    
+  } Kind;
+
+  /// \brief The point of instantiation within the source code.
+  SourceLocation PointOfInstantiation;
+
+  /// \brief The template (or partial specialization) in which we are
+  /// performing the instantiation, for substitutions of prior template
+  /// arguments.
+  NamedDecl *Template;
+
+  /// \brief The entity that is being instantiated.
+  Decl *Entity;
+
+  /// \brief The list of template arguments we are substituting, if they
+  /// are not part of the entity.
+  const TemplateArgument *TemplateArgs;
+
+  /// \brief The number of template arguments in TemplateArgs.
+  unsigned NumTemplateArgs;
+
+  /// \brief The template deduction info object associated with the
+  /// substitution or checking of explicit or deduced template arguments.
+  sema::TemplateDeductionInfo *DeductionInfo;
+
+  /// \brief The source range that covers the construct that cause
+  /// the instantiation, e.g., the template-id that causes a class
+  /// template instantiation.
+  SourceRange InstantiationRange;
+
+  ActiveTemplateInstantiation()
+    : Kind(TemplateInstantiation), Template(0), Entity(0), TemplateArgs(0),
+      NumTemplateArgs(0), DeductionInfo(0) {}
+
+  /// \brief Determines whether this template is an actual instantiation
+  /// that should be counted toward the maximum instantiation depth.
+  bool isInstantiationRecord() const;
+
+};
+
+
+bool operator==(const ActiveTemplateInstantiation &X,
+                const ActiveTemplateInstantiation &Y);
+
+inline
+bool operator!=(const ActiveTemplateInstantiation &X,
+                const ActiveTemplateInstantiation &Y) {
+  return !(X == Y);
+}
+
+
+
+}
+
+
+#endif
+
+
+
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h	(revision 202671)
+++ include/clang/Sema/Sema.h	(working copy)
@@ -31,6 +31,7 @@
 #include "clang/Basic/TemplateKinds.h"
 #include "clang/Basic/TypeTraits.h"
 #include "clang/Lex/ModuleLoader.h"
+#include "clang/Sema/ActiveTemplateInst.h"
 #include "clang/Sema/AnalysisBasedWarnings.h"
 #include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/ExternalSemaSource.h"
@@ -157,6 +158,7 @@
   class TemplateArgumentList;
   class TemplateArgumentLoc;
   class TemplateDecl;
+  class TemplateInstantiationObserver;
   class TemplateParameterList;
   class TemplatePartialOrderingContext;
   class TemplateTemplateParmDecl;
@@ -5980,120 +5982,6 @@
                                                 bool RelativeToPrimary = false,
                                                const FunctionDecl *Pattern = 0);
 
-  /// \brief A template instantiation that is currently in progress.
-  struct ActiveTemplateInstantiation {
-    /// \brief The kind of template instantiation we are performing
-    enum InstantiationKind {
-      /// We are instantiating a template declaration. The entity is
-      /// the declaration we're instantiating (e.g., a CXXRecordDecl).
-      TemplateInstantiation,
-
-      /// We are instantiating a default argument for a template
-      /// parameter. The Entity is the template, and
-      /// TemplateArgs/NumTemplateArguments provides the template
-      /// arguments as specified.
-      /// FIXME: Use a TemplateArgumentList
-      DefaultTemplateArgumentInstantiation,
-
-      /// We are instantiating a default argument for a function.
-      /// The Entity is the ParmVarDecl, and TemplateArgs/NumTemplateArgs
-      /// provides the template arguments as specified.
-      DefaultFunctionArgumentInstantiation,
-
-      /// We are substituting explicit template arguments provided for
-      /// a function template. The entity is a FunctionTemplateDecl.
-      ExplicitTemplateArgumentSubstitution,
-
-      /// We are substituting template argument determined as part of
-      /// template argument deduction for either a class template
-      /// partial specialization or a function template. The
-      /// Entity is either a ClassTemplatePartialSpecializationDecl or
-      /// a FunctionTemplateDecl.
-      DeducedTemplateArgumentSubstitution,
-
-      /// We are substituting prior template arguments into a new
-      /// template parameter. The template parameter itself is either a
-      /// NonTypeTemplateParmDecl or a TemplateTemplateParmDecl.
-      PriorTemplateArgumentSubstitution,
-
-      /// We are checking the validity of a default template argument that
-      /// has been used when naming a template-id.
-      DefaultTemplateArgumentChecking,
-
-      /// We are instantiating the exception specification for a function
-      /// template which was deferred until it was needed.
-      ExceptionSpecInstantiation
-    } Kind;
-
-    /// \brief The point of instantiation within the source code.
-    SourceLocation PointOfInstantiation;
-
-    /// \brief The template (or partial specialization) in which we are
-    /// performing the instantiation, for substitutions of prior template
-    /// arguments.
-    NamedDecl *Template;
-
-    /// \brief The entity that is being instantiated.
-    Decl *Entity;
-
-    /// \brief The list of template arguments we are substituting, if they
-    /// are not part of the entity.
-    const TemplateArgument *TemplateArgs;
-
-    /// \brief The number of template arguments in TemplateArgs.
-    unsigned NumTemplateArgs;
-
-    /// \brief The template deduction info object associated with the
-    /// substitution or checking of explicit or deduced template arguments.
-    sema::TemplateDeductionInfo *DeductionInfo;
-
-    /// \brief The source range that covers the construct that cause
-    /// the instantiation, e.g., the template-id that causes a class
-    /// template instantiation.
-    SourceRange InstantiationRange;
-
-    ActiveTemplateInstantiation()
-      : Kind(TemplateInstantiation), Template(0), Entity(0), TemplateArgs(0),
-        NumTemplateArgs(0), DeductionInfo(0) {}
-
-    /// \brief Determines whether this template is an actual instantiation
-    /// that should be counted toward the maximum instantiation depth.
-    bool isInstantiationRecord() const;
-
-    friend bool operator==(const ActiveTemplateInstantiation &X,
-                           const ActiveTemplateInstantiation &Y) {
-      if (X.Kind != Y.Kind)
-        return false;
-
-      if (X.Entity != Y.Entity)
-        return false;
-
-      switch (X.Kind) {
-      case TemplateInstantiation:
-      case ExceptionSpecInstantiation:
-        return true;
-
-      case PriorTemplateArgumentSubstitution:
-      case DefaultTemplateArgumentChecking:
-        return X.Template == Y.Template && X.TemplateArgs == Y.TemplateArgs;
-
-      case DefaultTemplateArgumentInstantiation:
-      case ExplicitTemplateArgumentSubstitution:
-      case DeducedTemplateArgumentSubstitution:
-      case DefaultFunctionArgumentInstantiation:
-        return X.TemplateArgs == Y.TemplateArgs;
-
-      }
-
-      llvm_unreachable("Invalid InstantiationKind!");
-    }
-
-    friend bool operator!=(const ActiveTemplateInstantiation &X,
-                           const ActiveTemplateInstantiation &Y) {
-      return !(X == Y);
-    }
-  };
-
   /// \brief List of active template instantiations.
   ///
   /// This vector is treated as a stack. As one template instantiation
@@ -6138,6 +6026,14 @@
   /// to implement it anywhere else.
   ActiveTemplateInstantiation LastTemplateInstantiationErrorContext;
 
+  /// \brief The template instantiation observer to trace or track 
+  /// instantiations (observers can be chained).
+  ///
+  /// This observer is used to print, trace or track template
+  /// instantiations as they are being constructed. For example, 
+  /// the 'templight' option is implemented with one such observer.
+  OwningPtr<TemplateInstantiationObserver> TemplateInstObserverChain;
+
   /// \brief The current index into pack expansion arguments that will be
   /// used for substitution of parameter packs.
   ///
Index: include/clang/Sema/TemplateInstObserver.h
===================================================================
--- include/clang/Sema/TemplateInstObserver.h	(revision 0)
+++ include/clang/Sema/TemplateInstObserver.h	(working copy)
@@ -0,0 +1,84 @@
+
+#ifndef LLVM_CLANG_TEMPLATE_INST_OBSERVER_H
+#define LLVM_CLANG_TEMPLATE_INST_OBSERVER_H
+
+#include "clang/Basic/SourceLocation.h"
+
+#include "llvm/ADT/OwningPtr.h"
+
+namespace clang {
+  
+  class Sema;
+  class ActiveTemplateInstantiation;
+  
+
+class TemplateInstantiationObserver {
+public:
+  
+  /// \brief Called after doing AST-parsing.
+  void initialize(const Sema &TheSema) {
+    this->initializeImpl(TheSema);
+    if ( NextObserver )
+      NextObserver->initialize(TheSema);
+  };
+  
+  /// \brief Called after AST-parsing is completed.
+  void finalize(const Sema &TheSema) {
+    this->finalizeImpl(TheSema);
+    if ( NextObserver )
+      NextObserver->finalize(TheSema);
+  };
+  
+  /// \brief Called when instantiation of a template just began.
+  void atTemplateBegin(const Sema &TheSema, const ActiveTemplateInstantiation &Inst) {
+    this->atTemplateBeginImpl(TheSema, Inst);
+    if ( NextObserver )
+      NextObserver->atTemplateBegin(TheSema, Inst);
+  };
+  
+  /// \brief Called when instantiation of a template is just about to end.
+  void atTemplateEnd(const Sema &TheSema, const ActiveTemplateInstantiation& Inst) {
+    this->atTemplateEndImpl(TheSema, Inst);
+    if ( NextObserver )
+      NextObserver->atTemplateEnd(TheSema, Inst);
+  };
+  
+  virtual ~TemplateInstantiationObserver() { };
+  
+  /// \brief Appends a new observer to the end of this list.
+  /// \note This function uses a tail-call recursion (and is not performance-critical).
+  static void appendNewObserver(OwningPtr<TemplateInstantiationObserver>& CurrentChain,
+                                TemplateInstantiationObserver* NewObserver) {
+    if ( !CurrentChain.isValid() ) {
+      CurrentChain.reset(NewObserver);
+      return;
+    };
+    appendNewObserver(CurrentChain->NextObserver, NewObserver);
+  };
+  
+protected:
+  
+  /// \brief Called after doing AST-parsing.
+  virtual void initializeImpl(const Sema &TheSema) { };
+  /// \brief Called after AST-parsing is completed.
+  virtual void finalizeImpl(const Sema &TheSema) { };
+  
+  /// \brief Called when instantiation of a template just began.
+  virtual void atTemplateBeginImpl(const Sema &TheSema, 
+                                   const ActiveTemplateInstantiation& Inst) { };
+  /// \brief Called when instantiation of a template is just about to end.
+  virtual void atTemplateEndImpl(const Sema &TheSema, 
+                                 const ActiveTemplateInstantiation& Inst) { };
+  
+  OwningPtr<TemplateInstantiationObserver> NextObserver;
+};
+
+
+
+}
+
+
+#endif
+
+
+
Index: include/clang/Sema/TemplightTracer.h
===================================================================
--- include/clang/Sema/TemplightTracer.h	(revision 0)
+++ include/clang/Sema/TemplightTracer.h	(working copy)
@@ -0,0 +1,56 @@
+
+#ifndef LLVM_CLANG_TEMPLIGHT_TRACER_H
+#define LLVM_CLANG_TEMPLIGHT_TRACER_H
+
+#include "clang/Sema/TemplateInstObserver.h"
+
+namespace clang {
+
+
+class TemplightTracer : public TemplateInstantiationObserver {
+public:
+  
+  class TracePrinter; // forward-decl.
+  
+protected:
+  
+  virtual void initializeImpl(const Sema &TheSema);
+  virtual void finalizeImpl(const Sema &TheSema);
+  virtual void atTemplateBeginImpl(const Sema &TheSema, const ActiveTemplateInstantiation& Inst);
+  virtual void atTemplateEndImpl(const Sema &TheSema, const ActiveTemplateInstantiation& Inst);
+  
+private:
+  
+  unsigned MemoryFlag : 1;
+  unsigned SafeModeFlag : 1;
+  unsigned IgnoreSystemFlag : 1;
+  
+  OwningPtr<TracePrinter> TemplateTracePrinter;
+  
+public:
+  
+  /// \brief Sets the format type of the template trace file.
+  /// The argument can be xml/yaml/text
+  TemplightTracer(const Sema &TheSema, 
+                  std::string Output = "", 
+                  const std::string& Format = "yaml",
+                  bool Memory = false, 
+                  bool Safemode = false,
+                  bool IgnoreSystem = false);
+  
+  virtual ~TemplightTracer();
+  
+  bool getMemoryFlag() const { return MemoryFlag; };
+  bool getSafeModeFlag() const { return SafeModeFlag; };
+  
+};
+
+
+
+}
+
+
+#endif
+
+
+
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp	(revision 202671)
+++ lib/Driver/Tools.cpp	(working copy)
@@ -3011,6 +3011,18 @@
     CmdArgs.push_back(A->getValue());
   }
 
+  // BEGIN TEMPLIGHT
+  if (Arg *A = Args.getLastArg(options::OPT_templight_output)) {
+    CmdArgs.push_back("-templight-output");
+    CmdArgs.push_back(A->getValue());
+  }
+
+  if (Arg *A = Args.getLastArg(options::OPT_templight_format)) {
+      CmdArgs.push_back("-templight-format");
+      CmdArgs.push_back(A->getValue());
+  }
+  // END TEMPLIGHT
+
   if (Arg *A = Args.getLastArg(options::OPT_fconstexpr_depth_EQ)) {
     CmdArgs.push_back("-fconstexpr-depth");
     CmdArgs.push_back(A->getValue());
@@ -3159,6 +3171,13 @@
   Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_parseable_fixits);
   Args.AddLastArg(CmdArgs, options::OPT_ftime_report);
   Args.AddLastArg(CmdArgs, options::OPT_ftrapv);
+  // BEGIN TEMPLIGHT
+  Args.AddLastArg(CmdArgs, options::OPT_templight);
+  Args.AddLastArg(CmdArgs, options::OPT_templight_stdout);
+  Args.AddLastArg(CmdArgs, options::OPT_templight_memory);
+  Args.AddLastArg(CmdArgs, options::OPT_templight_safe_mode);
+  Args.AddLastArg(CmdArgs, options::OPT_templight_ignore_system);
+  // END TEMPLIGHT
 
   if (Arg *A = Args.getLastArg(options::OPT_ftrapv_handler_EQ)) {
     CmdArgs.push_back("-ftrapv-handler");
Index: lib/Frontend/CompilerInstance.cpp
===================================================================
--- lib/Frontend/CompilerInstance.cpp	(revision 202671)
+++ lib/Frontend/CompilerInstance.cpp	(working copy)
@@ -30,6 +30,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Sema/Sema.h"
+#include "clang/Sema/TemplightTracer.h"
 #include "clang/Serialization/ASTReader.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Config/config.h"
@@ -434,6 +435,22 @@
                                   CodeCompleteConsumer *CompletionConsumer) {
   TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(),
                          TUKind, CompletionConsumer));
+  // BEGIN TEMPLIGHT
+  // NOTE: The Sema is sufficiently constructed at this point because 
+  // only the Sema's SourceManager is needed by TemplightTracer class.
+  if ( getInvocation().getFrontendOpts().Templight ) {
+    TemplateInstantiationObserver::appendNewObserver(
+      TheSema->TemplateInstObserverChain,
+      new TemplightTracer(*TheSema, 
+        ( getInvocation().getFrontendOpts().TemplightStdout ? 
+          std::string("stdout") : 
+          getInvocation().getFrontendOpts().TemplightOutputFile ),
+        getInvocation().getFrontendOpts().TemplightFormat, 
+        getInvocation().getFrontendOpts().TemplightMemory, 
+        getInvocation().getFrontendOpts().TemplightSafeMode, 
+        getInvocation().getFrontendOpts().TemplightIgnoreSystem));
+  }
+  // END TEMPLIGHT
 }
 
 // Output Files
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp	(revision 202671)
+++ lib/Frontend/CompilerInvocation.cpp	(working copy)
@@ -772,6 +772,22 @@
   Opts.UseGlobalModuleIndex = !Args.hasArg(OPT_fno_modules_global_index);
   Opts.GenerateGlobalModuleIndex = Opts.UseGlobalModuleIndex;
   
+  // BEGIN TEMPLIGHT
+  Opts.Templight = Args.hasArg(OPT_templight);
+  Opts.TemplightStdout = Args.hasArg(OPT_templight_stdout);
+  Opts.TemplightMemory = Args.hasArg(OPT_templight_memory);
+  Opts.TemplightSafeMode = Args.hasArg(OPT_templight_safe_mode);
+  Opts.TemplightIgnoreSystem = Args.hasArg(OPT_templight_ignore_system);
+
+  if (const Arg *A = Args.getLastArg(OPT_templight_output)) {
+    Opts.TemplightOutputFile = A->getValue();
+  }
+
+  if (const Arg *A = Args.getLastArg(OPT_templight_format)) {
+    Opts.TemplightFormat = A->getValue();
+  }
+  // END TEMPLIGHT 
+
   Opts.CodeCompleteOpts.IncludeMacros
     = Args.hasArg(OPT_code_completion_macros);
   Opts.CodeCompleteOpts.IncludeCodePatterns
Index: lib/Parse/ParseAST.cpp
===================================================================
--- lib/Parse/ParseAST.cpp	(revision 202671)
+++ lib/Parse/ParseAST.cpp	(working copy)
@@ -23,6 +23,7 @@
 #include "clang/Sema/ExternalSemaSource.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaConsumer.h"
+#include "clang/Sema/TemplateInstObserver.h"
 #include "llvm/ADT/OwningPtr.h"
 #include "llvm/Support/CrashRecoveryContext.h"
 #include <cstdio>
@@ -106,6 +107,11 @@
   // Also turn on collection of stats inside of the Sema object.
   bool OldCollectStats = PrintStats;
   std::swap(OldCollectStats, S.CollectStats);
+  
+  // Initialize the template instantiation observer chain.
+  // FIXME: See note on "finalize()" below.
+  if (S.TemplateInstObserverChain.isValid())
+    S.TemplateInstObserverChain->initialize(S);
 
   ASTConsumer *Consumer = &S.getASTConsumer();
 
@@ -151,7 +157,16 @@
     Consumer->HandleTopLevelDecl(DeclGroupRef(*I));
   
   Consumer->HandleTranslationUnit(S.getASTContext());
-
+  
+  // Finalize the template instantiation observer chain.
+  // FIXME: This should be done in the Sema class, but because 
+  // Sema does not have a reliable "Finalize" function (it has a 
+  // destructor, but it is not guaranteed to be called ("-disable-free")).
+  // So, for symmetry, do the init here, because the final will appear
+  // later in this function.
+  if (S.TemplateInstObserverChain.isValid())
+    S.TemplateInstObserverChain->finalize(S);
+  
   std::swap(OldCollectStats, S.CollectStats);
   if (PrintStats) {
     llvm::errs() << "\nSTATISTICS:\n";
Index: lib/Sema/ActiveTemplateInst.cpp
===================================================================
--- lib/Sema/ActiveTemplateInst.cpp	(revision 0)
+++ lib/Sema/ActiveTemplateInst.cpp	(working copy)
@@ -0,0 +1,61 @@
+
+#include "clang/Sema/ActiveTemplateInst.h"
+
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang {
+
+bool operator==(const ActiveTemplateInstantiation &X,
+                const ActiveTemplateInstantiation &Y) {
+  if (X.Kind != Y.Kind)
+    return false;
+
+  if (X.Entity != Y.Entity)
+    return false;
+
+  switch (X.Kind) {
+    case ActiveTemplateInstantiation::TemplateInstantiation:
+    case ActiveTemplateInstantiation::ExceptionSpecInstantiation:
+    case ActiveTemplateInstantiation::Memoization:
+      return true;
+    
+    case ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution:
+    case ActiveTemplateInstantiation::DefaultTemplateArgumentChecking:
+      return X.Template == Y.Template && X.TemplateArgs == Y.TemplateArgs;
+    
+    case ActiveTemplateInstantiation::DefaultTemplateArgumentInstantiation:
+    case ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution:
+    case ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution:
+    case ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation:
+      return X.TemplateArgs == Y.TemplateArgs;
+  }
+
+  llvm_unreachable("Invalid InstantiationKind!");
+}
+
+bool ActiveTemplateInstantiation::isInstantiationRecord() const {
+  switch (Kind) {
+    case TemplateInstantiation:
+    case ExceptionSpecInstantiation:
+    case DefaultTemplateArgumentInstantiation:
+    case DefaultFunctionArgumentInstantiation:
+    case ExplicitTemplateArgumentSubstitution:
+    case DeducedTemplateArgumentSubstitution:
+    case PriorTemplateArgumentSubstitution:
+      return true;
+
+    case DefaultTemplateArgumentChecking:
+      return false;
+    
+    // FIXME Should this do a break or not? 
+    // Memoization kind should never occur here, so, it doesn't really matter.
+    case Memoization:
+      break;
+  }
+
+  llvm_unreachable("Invalid InstantiationKind!");
+}
+
+
+}
+
Index: lib/Sema/CMakeLists.txt
===================================================================
--- lib/Sema/CMakeLists.txt	(revision 202671)
+++ lib/Sema/CMakeLists.txt	(working copy)
@@ -3,6 +3,7 @@
   )
 
 add_clang_library(clangSema
+  ActiveTemplateInst.cpp
   AnalysisBasedWarnings.cpp
   AttributeList.cpp
   CodeCompleteConsumer.cpp
@@ -47,6 +48,7 @@
   SemaTemplateInstantiateDecl.cpp
   SemaTemplateVariadic.cpp
   SemaType.cpp
+  TemplightTracer.cpp
   TypeLocBuilder.cpp
 
   LINK_LIBS
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp	(revision 202671)
+++ lib/Sema/Sema.cpp	(working copy)
@@ -36,10 +36,12 @@
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaConsumer.h"
 #include "clang/Sema/TemplateDeduction.h"
+#include "clang/Sema/TemplateInstObserver.h"
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/Support/CrashRecoveryContext.h"
+
 using namespace clang;
 using namespace sema;
 
Index: lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiate.cpp	(revision 202671)
+++ lib/Sema/SemaTemplateInstantiate.cpp	(working copy)
@@ -23,6 +23,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Template.h"
 #include "clang/Sema/TemplateDeduction.h"
+#include "clang/Sema/TemplateInstObserver.h"
 
 using namespace clang;
 using namespace sema;
@@ -182,24 +183,6 @@
   return Result;
 }
 
-bool Sema::ActiveTemplateInstantiation::isInstantiationRecord() const {
-  switch (Kind) {
-  case TemplateInstantiation:
-  case ExceptionSpecInstantiation:
-  case DefaultTemplateArgumentInstantiation:
-  case DefaultFunctionArgumentInstantiation:
-  case ExplicitTemplateArgumentSubstitution:
-  case DeducedTemplateArgumentSubstitution:
-  case PriorTemplateArgumentSubstitution:
-    return true;
-
-  case DefaultTemplateArgumentChecking:
-    return false;
-  }
-
-  llvm_unreachable("Invalid InstantiationKind!");
-}
-
 Sema::InstantiatingTemplate::
 InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
                       Decl *Entity,
@@ -220,6 +203,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -243,6 +228,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -268,6 +255,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -294,6 +283,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
     
     if (!Inst.isInstantiationRecord())
       ++SemaRef.NonInstantiationEntries;
@@ -322,6 +313,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -345,6 +338,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -369,6 +364,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -393,6 +390,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -417,6 +416,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -441,6 +442,8 @@
   Inst.InstantiationRange = InstantiationRange;
   SemaRef.InNonInstantiationSFINAEContext = false;
   SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+  if ( SemaRef.TemplateInstObserverChain.isValid() )
+    SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   
   assert(!Inst.isInstantiationRecord());
   ++SemaRef.NonInstantiationEntries;
@@ -465,6 +468,10 @@
         SemaRef.LookupModulesCache.erase(M);
       SemaRef.ActiveTemplateInstantiationLookupModules.pop_back();
     }
+    
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateEnd(
+        SemaRef, SemaRef.ActiveTemplateInstantiations.back());
 
     SemaRef.ActiveTemplateInstantiations.pop_back();
     Invalid = true;
@@ -677,6 +684,9 @@
         << cast<FunctionDecl>(Active->Entity)
         << Active->InstantiationRange;
       break;
+
+    case ActiveTemplateInstantiation::Memoization:
+      break;
     }
   }
 }
@@ -717,6 +727,9 @@
       // or deduced template arguments, so SFINAE applies.
       assert(Active->DeductionInfo && "Missing deduction info pointer");
       return Active->DeductionInfo;
+
+    case ActiveTemplateInstantiation::Memoization:
+      break;
     }
   }
 
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp	(revision 202671)
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp	(working copy)
@@ -22,6 +22,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/PrettyDeclStackTrace.h"
 #include "clang/Sema/Template.h"
+#include "clang/Sema/TemplateInstObserver.h"
 
 using namespace clang;
 
@@ -3201,7 +3202,7 @@
   // into a template instantiation for this specific function template
   // specialization, which is not a SFINAE context, so that we diagnose any
   // further errors in the declaration itself.
-  typedef Sema::ActiveTemplateInstantiation ActiveInstType;
+  typedef ActiveTemplateInstantiation ActiveInstType;
   ActiveInstType &ActiveInst = SemaRef.ActiveTemplateInstantiations.back();
   if (ActiveInst.Kind == ActiveInstType::ExplicitTemplateArgumentSubstitution ||
       ActiveInst.Kind == ActiveInstType::DeducedTemplateArgumentSubstitution) {
@@ -3210,8 +3211,12 @@
       assert(FunTmpl->getTemplatedDecl() == Tmpl &&
              "Deduction from the wrong function template?");
       (void) FunTmpl;
+      if ( SemaRef.TemplateInstObserverChain.isValid() )
+        SemaRef.TemplateInstObserverChain->atTemplateEnd(SemaRef, ActiveInst);
       ActiveInst.Kind = ActiveInstType::TemplateInstantiation;
       ActiveInst.Entity = New;
+      if ( SemaRef.TemplateInstObserverChain.isValid() )
+        SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, ActiveInst);
     }
   }
 
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp	(revision 202671)
+++ lib/Sema/SemaType.cpp	(working copy)
@@ -31,6 +31,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/Template.h"
+#include "clang/Sema/TemplateInstObserver.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -5093,6 +5094,15 @@
         createImplicitModuleImport(Loc, Owner);
       }
     }
+    else if (Def && TemplateInstObserverChain.isValid()) {
+      ActiveTemplateInstantiation TempInst;
+      TempInst.Kind = ActiveTemplateInstantiation::Memoization;
+      TempInst.Template = Def;
+      TempInst.Entity = Def;
+      TempInst.PointOfInstantiation = Loc;
+      TemplateInstObserverChain->atTemplateBegin(*this, TempInst);
+      TemplateInstObserverChain->atTemplateEnd(*this, TempInst);
+    }
 
     // We lock in the inheritance model once somebody has asked us to ensure
     // that a pointer-to-member type is complete.
Index: lib/Sema/TemplightTracer.cpp
===================================================================
--- lib/Sema/TemplightTracer.cpp	(revision 0)
+++ lib/Sema/TemplightTracer.cpp	(working copy)
@@ -0,0 +1,525 @@
+
+#include "clang/AST/Decl.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/ActiveTemplateInst.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/TemplightTracer.h"
+
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Object/YAML.h"
+
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/Process.h"
+
+#include <string>
+#include <vector>
+
+namespace clang {
+
+namespace {
+
+struct RawTraceEntry {
+  bool IsTemplateBegin;
+  ActiveTemplateInstantiation::InstantiationKind InstantiationKind;
+  Decl *Entity;
+  SourceLocation PointOfInstantiation;
+  double TimeStamp;
+  size_t MemoryUsage;
+  
+  RawTraceEntry() : IsTemplateBegin(true), 
+    InstantiationKind(ActiveTemplateInstantiation::TemplateInstantiation),
+    Entity(0), TimeStamp(0.0), MemoryUsage(0) { };
+};
+
+struct PrintableTraceEntry {
+  bool IsTemplateBegin;
+  std::string InstantiationKind;
+  std::string Name;
+  std::string FileName;
+  int Line;
+  int Column;
+  double TimeStamp;
+  size_t MemoryUsage;
+};
+
+
+const char* const InstantiationKindStrings[] = { 
+  "TemplateInstantiation",
+  "DefaultTemplateArgumentInstantiation",
+  "DefaultFunctionArgumentInstantiation",
+  "ExplicitTemplateArgumentSubstitution",
+  "DeducedTemplateArgumentSubstitution", 
+  "PriorTemplateArgumentSubstitution",
+  "DefaultTemplateArgumentChecking", 
+  "ExceptionSpecInstantiation",
+  "Memoization" };
+
+
+PrintableTraceEntry rawToPrintable(const Sema &TheSema, const RawTraceEntry& Entry) {
+  PrintableTraceEntry Ret;
+  
+  Ret.IsTemplateBegin = Entry.IsTemplateBegin;
+  Ret.InstantiationKind = InstantiationKindStrings[Entry.InstantiationKind];
+  
+  if (Entry.IsTemplateBegin) {
+    NamedDecl *NamedTemplate = dyn_cast<NamedDecl>(Entry.Entity);
+    if (NamedTemplate) {
+      llvm::raw_string_ostream OS(Ret.Name);
+      NamedTemplate->getNameForDiagnostic(OS, TheSema.getLangOpts(), true);
+    }
+    
+    PresumedLoc Loc = TheSema.getSourceManager().getPresumedLoc(Entry.PointOfInstantiation);
+    
+    Ret.FileName = Loc.getFilename();
+    Ret.Line = Loc.getLine();
+    Ret.Column = Loc.getColumn();
+  }
+  
+  
+  Ret.TimeStamp = Entry.TimeStamp;
+  Ret.MemoryUsage = Entry.MemoryUsage;
+  
+  return Ret;
+}
+
+
+
+
+std::string escapeXml(const std::string& Input) {
+  std::string Result;
+  Result.reserve(64);
+
+  unsigned i, pos = 0;
+  for (i = 0; i < Input.length(); ++i) {
+    if (Input[i] == '<' || Input[i] == '>' || Input[i] == '"'
+      || Input[i] == '\'' || Input[i] == '&') {
+      Result.insert(Result.length(), Input, pos, i - pos);
+      pos = i + 1;
+      switch (Input[i]) {
+      case '<':
+        Result += "<";
+        break;
+      case '>':
+        Result += ">";
+        break;
+      case '\'':
+        Result += "'";
+        break;
+      case '"':
+        Result += """;
+        break;
+      case '&':
+        Result += "&";
+        break;
+      default:
+        break;
+      }
+    }
+  }
+  Result.insert(Result.length(), Input, pos, i - pos);
+  return Result;
+}
+
+
+} // unnamed namespace
+
+} // namespace clang
+
+
+
+
+namespace llvm {
+namespace yaml {
+
+template <>
+struct ScalarEnumerationTraits<
+    clang::ActiveTemplateInstantiation::InstantiationKind> {
+  static void enumeration(IO &io,
+    clang::ActiveTemplateInstantiation::InstantiationKind &value) {
+    using namespace clang;
+    io.enumCase(value, "TemplateInstantiation",
+      ActiveTemplateInstantiation::TemplateInstantiation);
+    io.enumCase(value, "DefaultTemplateArgumentInstantiation",
+      ActiveTemplateInstantiation::DefaultTemplateArgumentInstantiation);
+    io.enumCase(value, "DefaultFunctionArgumentInstantiation",
+      ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation);
+    io.enumCase(value, "ExplicitTemplateArgumentSubstitution",
+      ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution);
+    io.enumCase(value, "DeducedTemplateArgumentSubstitution",
+      ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution);
+    io.enumCase(value, "PriorTemplateArgumentSubstitution",
+      ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution);
+    io.enumCase(value, "DefaultTemplateArgumentChecking",
+      ActiveTemplateInstantiation::DefaultTemplateArgumentChecking);
+    io.enumCase(value, "ExceptionSpecInstantiation",
+      ActiveTemplateInstantiation::ExceptionSpecInstantiation);
+    io.enumCase(value, "Memoization",
+      ActiveTemplateInstantiation::Memoization);
+  }
+};
+
+template <>
+struct MappingTraits<clang::PrintableTraceEntry> {
+  static void mapping(IO &io, clang::PrintableTraceEntry &Entry) {
+    io.mapRequired("IsTemplateBegin", Entry.IsTemplateBegin);
+    io.mapRequired("Kind", Entry.InstantiationKind);
+    io.mapOptional("Name", Entry.Name);
+    io.mapOptional("FileName", Entry.FileName);
+    io.mapOptional("Line", Entry.Line);
+    io.mapOptional("Column", Entry.Column);
+    io.mapRequired("TimeStamp", Entry.TimeStamp);
+    io.mapOptional("MemoryUsage", Entry.MemoryUsage);
+  }
+};
+
+} // namespace yaml
+} // namespace llvm
+
+
+
+namespace clang {
+
+class TemplightTracer::TracePrinter {
+protected:
+  virtual void startTraceImpl(const Sema &) = 0;
+  virtual void endTraceImpl(const Sema &) = 0;
+  virtual void printEntryImpl(const Sema &, const PrintableTraceEntry &) = 0;
+  
+public:
+  
+  bool shouldIgnoreEntry(const RawTraceEntry &Entry) const {
+    if ( Entry.InstantiationKind == ActiveTemplateInstantiation::Memoization ) {
+      if ( !LastBeginEntry.IsTemplateBegin
+           && ( LastBeginEntry.InstantiationKind == Entry.InstantiationKind )
+           && ( LastBeginEntry.Entity == Entry.Entity ) ) {
+        return true;
+      }
+    }
+    return false;
+  };
+  
+  void printRawEntry(const Sema &TheSema, const RawTraceEntry &Entry) {
+    if ( shouldIgnoreEntry(Entry) )
+      return;
+    this->printEntryImpl(TheSema, rawToPrintable(TheSema, Entry));
+    TraceOS->flush();
+    if ( Entry.IsTemplateBegin )
+      LastBeginEntry = Entry;
+    if ( !Entry.IsTemplateBegin && 
+         ( Entry.InstantiationKind == ActiveTemplateInstantiation::Memoization ) )
+      LastBeginEntry.IsTemplateBegin = false;
+  };
+  
+  void printCachedEntries(const Sema &TheSema) {
+    for(std::vector<RawTraceEntry>::iterator it = TraceEntries.begin();
+        it != TraceEntries.end(); ++it) {
+      this->printEntryImpl(TheSema, rawToPrintable(TheSema, *it));
+    }
+    TraceEntries.clear();
+  };
+  
+  void cacheEntry(const Sema &TheSema, const RawTraceEntry &Entry) {
+    if ( shouldIgnoreEntry(Entry) )
+      return;
+    TraceEntries.push_back(Entry);
+    if ( Entry.IsTemplateBegin )
+      LastBeginEntry = Entry;
+    if ( !Entry.IsTemplateBegin && 
+         ( Entry.InstantiationKind == ActiveTemplateInstantiation::Memoization ) )
+      LastBeginEntry.IsTemplateBegin = false;
+    if ( !Entry.IsTemplateBegin &&
+         ( Entry.InstantiationKind == TraceEntries.front().InstantiationKind ) &&
+         ( Entry.Entity == TraceEntries.front().Entity ) )
+      printCachedEntries(TheSema);
+  };
+  
+  void startTrace(const Sema &TheSema) {
+    this->startTraceImpl(TheSema);
+  };
+  
+  void endTrace(const Sema &TheSema) {
+    printCachedEntries(TheSema);
+    this->endTraceImpl(TheSema);
+    TraceOS->flush();
+  };
+  
+  TracePrinter(const std::string &Output) : TraceOS(0) {
+    if ( Output == "stdout" ) {
+      TraceOS = &llvm::outs();
+    } else {
+      std::string error;
+      TraceOS = new llvm::raw_fd_ostream(Output.c_str(), error, llvm::sys::fs::F_None);
+      if ( !error.empty() ) {
+        llvm::errs() <<
+          "Error: [Templight] Can not open file to write trace of template instantiations: "
+          << Output << " Error: " << error;
+        TraceOS = 0;
+        return;
+      }
+    }
+  };
+  
+  virtual ~TracePrinter() {
+    if ( TraceOS ) {
+      TraceOS->flush();
+      if ( TraceOS != &llvm::outs() )
+        delete TraceOS;
+    }
+  };
+  
+  std::vector<RawTraceEntry> TraceEntries;
+  RawTraceEntry LastBeginEntry;
+  
+  llvm::raw_ostream* TraceOS;
+  
+  bool isValid() const { return TraceOS; };
+};
+
+
+
+
+namespace {
+
+class YamlPrinter : public TemplightTracer::TracePrinter {
+protected:
+  void startTraceImpl(const Sema &) {
+    Output.reset(new llvm::yaml::Output(*this->TraceOS));
+    Output->beginDocuments();
+    Output->beginFlowSequence();
+  };
+  void endTraceImpl(const Sema &) {
+    Output->endFlowSequence();
+    Output->endDocuments();
+  };
+  void printEntryImpl(const Sema &, const PrintableTraceEntry& Entry) {
+    void *SaveInfo;
+    if ( Output->preflightFlowElement(1, SaveInfo) ) {
+      llvm::yaml::yamlize(*Output, const_cast<PrintableTraceEntry&>(Entry), 
+                          true);
+      Output->postflightFlowElement(SaveInfo);
+    }
+  };
+  
+public:
+  
+  YamlPrinter(const std::string &Output) : 
+              TemplightTracer::TracePrinter(Output) { };
+  
+private:
+  OwningPtr<llvm::yaml::Output> Output;
+};
+
+
+
+class XmlPrinter : public TemplightTracer::TracePrinter {
+protected:
+  void startTraceImpl(const Sema &) {
+    (*this->TraceOS) <<
+      "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
+      "<Trace>\n";
+  };
+  void endTraceImpl(const Sema &) {
+    (*this->TraceOS) << "</Trace>\n";
+  };
+  void printEntryImpl(const Sema &, const PrintableTraceEntry& Entry) {
+    if (Entry.IsTemplateBegin) {
+      std::string EscapedName = escapeXml(Entry.Name);
+      (*this->TraceOS)
+        << llvm::format("<TemplateBegin>\n"
+          "    <Kind>%s</Kind>\n"
+          "    <Context context = \"%s\"/>\n"
+          "    <PointOfInstantiation>%s|%d|%d</PointOfInstantiation>\n",
+          Entry.InstantiationKind.c_str(), EscapedName.c_str(),
+          Entry.FileName.c_str(), Entry.Line, Entry.Column);
+
+      (*this->TraceOS) << llvm::format("    <TimeStamp time = \"%f\"/>\n"
+        "    <MemoryUsage bytes = \"%d\"/>\n"
+        "</TemplateBegin>\n", Entry.TimeStamp, Entry.MemoryUsage);
+    } else {
+      (*this->TraceOS)
+        << llvm::format("<TemplateEnd>\n"
+          "    <Kind>%s</Kind>\n"
+          "    <TimeStamp time = \"%f\"/>\n"
+          "    <MemoryUsage bytes = \"%d\"/>\n"
+          "</TemplateEnd>\n", Entry.InstantiationKind.c_str(),
+          Entry.TimeStamp, Entry.MemoryUsage);
+    }
+  };
+  
+public:
+  
+  XmlPrinter(const std::string &Output) : 
+             TemplightTracer::TracePrinter(Output) { };
+  
+};
+
+
+
+
+class TextPrinter : public TemplightTracer::TracePrinter {
+protected:
+  void startTraceImpl(const Sema &) { };
+  void endTraceImpl(const Sema &) { };
+  void printEntryImpl(const Sema &, const PrintableTraceEntry& Entry) { 
+    if (Entry.IsTemplateBegin) {
+      (*this->TraceOS)
+        << llvm::format(
+          "TemplateBegin\n"
+          "  Kind = %s\n"
+          "  Name = %s\n"
+          "  PointOfInstantiation = %s|%d|%d\n",
+          Entry.InstantiationKind.c_str(), Entry.Name.c_str(),
+          Entry.FileName.c_str(), Entry.Line, Entry.Column);
+
+      (*this->TraceOS) << llvm::format(
+        "  TimeStamp = %f\n"
+        "  MemoryUsage = %d\n"
+        , Entry.TimeStamp, Entry.MemoryUsage);
+    } else {
+      (*this->TraceOS)
+        << llvm::format(
+          "TemplateEnd\n"
+          "  Kind = %s\n"
+          "  TimeStamp = %f\n"
+          "  MemoryUsage = %d\n"
+          , Entry.InstantiationKind.c_str(),
+          Entry.TimeStamp, Entry.MemoryUsage);
+    }
+  };
+  
+public:
+  
+  TextPrinter(const std::string &Output) : 
+              TemplightTracer::TracePrinter(Output) { };
+  
+};
+
+
+} // unnamed namespace
+
+
+
+
+void TemplightTracer::atTemplateBeginImpl(const Sema &TheSema, 
+                          const ActiveTemplateInstantiation& Inst) {
+  if ( !TemplateTracePrinter.isValid() || 
+       ( IgnoreSystemFlag && TheSema.getSourceManager()
+                              .isInSystemHeader(Inst.PointOfInstantiation) ) )
+    return;
+  
+  RawTraceEntry Entry;
+
+  llvm::TimeRecord timeRecord = llvm::TimeRecord::getCurrentTime();
+
+  Entry.IsTemplateBegin = true;
+  Entry.InstantiationKind = Inst.Kind;
+  Entry.Entity = Inst.Entity;
+  Entry.PointOfInstantiation = Inst.PointOfInstantiation;
+  Entry.TimeStamp = timeRecord.getWallTime();
+  Entry.MemoryUsage = (MemoryFlag ? llvm::sys::Process::GetMallocUsage() : 0);
+  
+  if ( SafeModeFlag ) {
+    TemplateTracePrinter->printRawEntry(TheSema, Entry);
+  } else {
+    TemplateTracePrinter->cacheEntry(TheSema, Entry);
+  }
+}
+
+void TemplightTracer::atTemplateEndImpl(const Sema &TheSema, 
+                          const ActiveTemplateInstantiation& Inst) {
+  if ( !TemplateTracePrinter.isValid() || 
+       ( IgnoreSystemFlag && TheSema.getSourceManager()
+                              .isInSystemHeader(Inst.PointOfInstantiation) ) )
+    return;
+  
+  RawTraceEntry Entry;
+
+  llvm::TimeRecord timeRecord = llvm::TimeRecord::getCurrentTime();
+
+  Entry.IsTemplateBegin = false;
+  Entry.InstantiationKind = Inst.Kind;
+  Entry.Entity = Inst.Entity;
+  Entry.TimeStamp = timeRecord.getWallTime();
+  Entry.MemoryUsage = (MemoryFlag ? llvm::sys::Process::GetMallocUsage() : 0);
+
+  if ( SafeModeFlag ) {
+    TemplateTracePrinter->printRawEntry(TheSema, Entry);
+  } else {
+    TemplateTracePrinter->cacheEntry(TheSema, Entry);
+  }
+}
+
+
+TemplightTracer::TemplightTracer(const Sema &TheSema, 
+                                 std::string Output, 
+                                 const std::string& Format,
+                                 bool Memory, bool Safemode, 
+                                 bool IgnoreSystem) :
+                                 MemoryFlag(Memory),
+                                 SafeModeFlag(Safemode),
+                                 IgnoreSystemFlag(IgnoreSystem)  {
+  
+  std::string postfix;
+  if ( Output.empty() ) {
+    // then, derive output name from the input name:
+    FileID fileID = TheSema.getSourceManager().getMainFileID();
+    postfix = (MemoryFlag ? ".memory.trace." : ".trace.");
+    
+    Output =
+      TheSema.getSourceManager().getFileEntryForID(fileID)->getName();
+  }
+  
+  if ( ( Format.empty() ) || ( Format == "yaml" ) ) {
+    TemplateTracePrinter.reset(new YamlPrinter(
+      ( postfix == "" ? Output : (Output + postfix + "yaml") )
+    ));
+    return;
+  }
+  else if ( Format == "xml" ) {
+    TemplateTracePrinter.reset(new XmlPrinter(
+      ( postfix == "" ? Output : (Output + postfix + "xml") )
+    ));
+    return;
+  }
+  else if ( Format == "text" ) {
+    TemplateTracePrinter.reset(new TextPrinter(
+      ( postfix == "" ? Output : (Output + postfix + "txt") )
+    ));
+    return;
+  }
+  else {
+    llvm::errs() << "Error: [Templight] Unrecoginized template trace format:" << Format;
+  }
+  
+  if ( !TemplateTracePrinter.isValid() || !TemplateTracePrinter->isValid() ) {
+    if ( TemplateTracePrinter )
+      TemplateTracePrinter.reset();
+    llvm::errs() << "Note: [Templight] Template trace has been disabled.";
+  }
+  
+}
+
+
+TemplightTracer::~TemplightTracer() { 
+  // must be defined here due to TracePrinter being incomplete in header.
+}
+
+
+void TemplightTracer::initializeImpl(const Sema &TheSema) {
+  if ( TemplateTracePrinter.isValid() )
+    TemplateTracePrinter->startTrace(TheSema);
+}
+
+void TemplightTracer::finalizeImpl(const Sema &TheSema) {
+  if ( TemplateTracePrinter.isValid() )
+    TemplateTracePrinter->endTrace(TheSema);
+}
+
+
+} // namespace clang
+
+
+
+
-------------- next part --------------
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td	(revision 202671)
+++ include/clang/Driver/Options.td	(working copy)
@@ -152,6 +152,36 @@
   HelpText<"Emit ARC errors even if the migrator can fix them">,
   Flags<[CC1Option]>;
 
+// BEGIN TEMPLIGHT
+//===----------------------------------------------------------------------===//
+// Templight Options
+//===----------------------------------------------------------------------===//
+  
+def templight : Flag<["-"], "templight">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"For tracing template instantiations">; 
+  
+def templight_stdout : Flag<["-"], "templight-stdout">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"Tracing template instantiations to standard output">;
+  
+def templight_memory : Flag<["-"], "templight-memory">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"For tracing memory usage during template instantiations">;
+  
+def templight_safe_mode : Flag<["-"], "templight-safe-mode">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"For flushing Templight trace immediately instead of store the trace in a buffer">;
+  
+def templight_ignore_system : Flag<["-"], "templight-ignore-system">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"For ignoring any template instantiation that comes from a system header">;
+  
+def templight_debugger : Flag<["-"], "templight-debugger">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"Start a Templight debugging session (interactive)">;
+  
+def templight_output : JoinedOrSeparate<["-"], "templight-output">, Flags<[DriverOption, RenderAsInput, CC1Option]>,
+  HelpText<"Write Templight output to <file>">, MetaVarName<"<file>">;
+  
+def templight_format : JoinedOrSeparate<["-"], "templight-format">, Flags<[DriverOption, RenderAsInput, CC1Option]>,
+  HelpText<"Format of Templight output (yaml/xml/text, default is yaml)">, MetaVarName<"<format>">;
+// END TEMPLIGHT
+
 def _migrate : Flag<["--"], "migrate">, Flags<[DriverOption]>,
   HelpText<"Run the migrator">;
 def ccc_objcmt_migrate : Separate<["-"], "ccc-objcmt-migrate">,
Index: include/clang/Frontend/FrontendOptions.h
===================================================================
--- include/clang/Frontend/FrontendOptions.h	(revision 202671)
+++ include/clang/Frontend/FrontendOptions.h	(working copy)
@@ -145,6 +145,24 @@
   unsigned ASTDumpLookups : 1;             ///< Whether we include lookup table
                                            ///< dumps in AST dumps.
 
+  // BEGIN TEMPLIGHT
+  unsigned Templight : 1;                  ///< For Tracing template
+                                           /// instantiations
+  unsigned TemplightStdout : 1;            ///< Tracing template instantiations
+                                           /// to stdout
+  unsigned TemplightMemory : 1;            ///< For tracing memory usage
+                                           /// during template instantiations
+                                           /// This indicates Templight
+  unsigned TemplightSafeMode : 1;          ///< For flushing the Templight
+                                           /// trace immediately instead of
+                                           /// store it in a buffer
+  unsigned TemplightIgnoreSystem : 1;      ///< For ignoring any template 
+                                           /// instantiation that comes from
+                                           /// a system header.
+  unsigned TemplightDebugger : 1;          ///< Start a Templight debugging 
+                                           /// session (interactive).
+  // END TEMPLIGHT
+
   CodeCompleteOptions CodeCompleteOpts;
 
   enum {
@@ -201,6 +219,14 @@
   /// The output file, if any.
   std::string OutputFile;
 
+  // BEGIN TEMPLIGHT
+  /// Output file fo Templight, if any.
+  std::string TemplightOutputFile;
+
+  /// Format of Templight output (yaml/xml/text)
+  std::string TemplightFormat;
+  // END TEMPLIGHT
+
   /// If given, the new suffix for fix-it rewritten files.
   std::string FixItSuffix;
 
Index: include/clang/Sema/ActiveTemplateInst.h
===================================================================
--- include/clang/Sema/ActiveTemplateInst.h	(revision 0)
+++ include/clang/Sema/ActiveTemplateInst.h	(working copy)
@@ -0,0 +1,126 @@
+
+#ifndef LLVM_CLANG_ACTIVE_TEMPLATE_INST_H
+#define LLVM_CLANG_ACTIVE_TEMPLATE_INST_H
+
+#include "clang/Basic/SourceLocation.h"
+
+namespace clang {
+  
+  class Decl;
+  class NamedDecl;
+  class TemplateArgument;
+
+namespace sema {
+  class TemplateDeductionInfo;
+}
+
+
+/// \brief A template instantiation that is currently in progress.
+class ActiveTemplateInstantiation {
+public:
+  /// \brief The kind of template instantiation we are performing
+  enum InstantiationKind {
+    /// We are instantiating a template declaration. The entity is
+    /// the declaration we're instantiating (e.g., a CXXRecordDecl).
+    TemplateInstantiation,
+
+    /// We are instantiating a default argument for a template
+    /// parameter. The Entity is the template, and
+    /// TemplateArgs/NumTemplateArguments provides the template
+    /// arguments as specified.
+    /// FIXME: Use a TemplateArgumentList
+    DefaultTemplateArgumentInstantiation,
+
+    /// We are instantiating a default argument for a function.
+    /// The Entity is the ParmVarDecl, and TemplateArgs/NumTemplateArgs
+    /// provides the template arguments as specified.
+    DefaultFunctionArgumentInstantiation,
+
+    /// We are substituting explicit template arguments provided for
+    /// a function template. The entity is a FunctionTemplateDecl.
+    ExplicitTemplateArgumentSubstitution,
+
+    /// We are substituting template argument determined as part of
+    /// template argument deduction for either a class template
+    /// partial specialization or a function template. The
+    /// Entity is either a ClassTemplatePartialSpecializationDecl or
+    /// a FunctionTemplateDecl.
+    DeducedTemplateArgumentSubstitution,
+
+    /// We are substituting prior template arguments into a new
+    /// template parameter. The template parameter itself is either a
+    /// NonTypeTemplateParmDecl or a TemplateTemplateParmDecl.
+    PriorTemplateArgumentSubstitution,
+
+    /// We are checking the validity of a default template argument that
+    /// has been used when naming a template-id.
+    DefaultTemplateArgumentChecking,
+
+    /// We are instantiating the exception specification for a function
+    /// template which was deferred until it was needed.
+    ExceptionSpecInstantiation,
+    
+    /// Added for Template instantiation observation
+    /// Memoization means we are _not_ instantiating a template because 
+    /// it is already instantiated (but we entered a context where we 
+    /// would have had to if it was not already instantiated).
+    Memoization
+    
+  } Kind;
+
+  /// \brief The point of instantiation within the source code.
+  SourceLocation PointOfInstantiation;
+
+  /// \brief The template (or partial specialization) in which we are
+  /// performing the instantiation, for substitutions of prior template
+  /// arguments.
+  NamedDecl *Template;
+
+  /// \brief The entity that is being instantiated.
+  Decl *Entity;
+
+  /// \brief The list of template arguments we are substituting, if they
+  /// are not part of the entity.
+  const TemplateArgument *TemplateArgs;
+
+  /// \brief The number of template arguments in TemplateArgs.
+  unsigned NumTemplateArgs;
+
+  /// \brief The template deduction info object associated with the
+  /// substitution or checking of explicit or deduced template arguments.
+  sema::TemplateDeductionInfo *DeductionInfo;
+
+  /// \brief The source range that covers the construct that cause
+  /// the instantiation, e.g., the template-id that causes a class
+  /// template instantiation.
+  SourceRange InstantiationRange;
+
+  ActiveTemplateInstantiation()
+    : Kind(TemplateInstantiation), Template(0), Entity(0), TemplateArgs(0),
+      NumTemplateArgs(0), DeductionInfo(0) {}
+
+  /// \brief Determines whether this template is an actual instantiation
+  /// that should be counted toward the maximum instantiation depth.
+  bool isInstantiationRecord() const;
+
+};
+
+
+bool operator==(const ActiveTemplateInstantiation &X,
+                const ActiveTemplateInstantiation &Y);
+
+inline
+bool operator!=(const ActiveTemplateInstantiation &X,
+                const ActiveTemplateInstantiation &Y) {
+  return !(X == Y);
+}
+
+
+
+}
+
+
+#endif
+
+
+
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h	(revision 202671)
+++ include/clang/Sema/Sema.h	(working copy)
@@ -31,6 +31,7 @@
 #include "clang/Basic/TemplateKinds.h"
 #include "clang/Basic/TypeTraits.h"
 #include "clang/Lex/ModuleLoader.h"
+#include "clang/Sema/ActiveTemplateInst.h"
 #include "clang/Sema/AnalysisBasedWarnings.h"
 #include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/ExternalSemaSource.h"
@@ -157,6 +158,7 @@
   class TemplateArgumentList;
   class TemplateArgumentLoc;
   class TemplateDecl;
+  class TemplateInstantiationObserver;
   class TemplateParameterList;
   class TemplatePartialOrderingContext;
   class TemplateTemplateParmDecl;
@@ -5980,120 +5982,6 @@
                                                 bool RelativeToPrimary = false,
                                                const FunctionDecl *Pattern = 0);
 
-  /// \brief A template instantiation that is currently in progress.
-  struct ActiveTemplateInstantiation {
-    /// \brief The kind of template instantiation we are performing
-    enum InstantiationKind {
-      /// We are instantiating a template declaration. The entity is
-      /// the declaration we're instantiating (e.g., a CXXRecordDecl).
-      TemplateInstantiation,
-
-      /// We are instantiating a default argument for a template
-      /// parameter. The Entity is the template, and
-      /// TemplateArgs/NumTemplateArguments provides the template
-      /// arguments as specified.
-      /// FIXME: Use a TemplateArgumentList
-      DefaultTemplateArgumentInstantiation,
-
-      /// We are instantiating a default argument for a function.
-      /// The Entity is the ParmVarDecl, and TemplateArgs/NumTemplateArgs
-      /// provides the template arguments as specified.
-      DefaultFunctionArgumentInstantiation,
-
-      /// We are substituting explicit template arguments provided for
-      /// a function template. The entity is a FunctionTemplateDecl.
-      ExplicitTemplateArgumentSubstitution,
-
-      /// We are substituting template argument determined as part of
-      /// template argument deduction for either a class template
-      /// partial specialization or a function template. The
-      /// Entity is either a ClassTemplatePartialSpecializationDecl or
-      /// a FunctionTemplateDecl.
-      DeducedTemplateArgumentSubstitution,
-
-      /// We are substituting prior template arguments into a new
-      /// template parameter. The template parameter itself is either a
-      /// NonTypeTemplateParmDecl or a TemplateTemplateParmDecl.
-      PriorTemplateArgumentSubstitution,
-
-      /// We are checking the validity of a default template argument that
-      /// has been used when naming a template-id.
-      DefaultTemplateArgumentChecking,
-
-      /// We are instantiating the exception specification for a function
-      /// template which was deferred until it was needed.
-      ExceptionSpecInstantiation
-    } Kind;
-
-    /// \brief The point of instantiation within the source code.
-    SourceLocation PointOfInstantiation;
-
-    /// \brief The template (or partial specialization) in which we are
-    /// performing the instantiation, for substitutions of prior template
-    /// arguments.
-    NamedDecl *Template;
-
-    /// \brief The entity that is being instantiated.
-    Decl *Entity;
-
-    /// \brief The list of template arguments we are substituting, if they
-    /// are not part of the entity.
-    const TemplateArgument *TemplateArgs;
-
-    /// \brief The number of template arguments in TemplateArgs.
-    unsigned NumTemplateArgs;
-
-    /// \brief The template deduction info object associated with the
-    /// substitution or checking of explicit or deduced template arguments.
-    sema::TemplateDeductionInfo *DeductionInfo;
-
-    /// \brief The source range that covers the construct that cause
-    /// the instantiation, e.g., the template-id that causes a class
-    /// template instantiation.
-    SourceRange InstantiationRange;
-
-    ActiveTemplateInstantiation()
-      : Kind(TemplateInstantiation), Template(0), Entity(0), TemplateArgs(0),
-        NumTemplateArgs(0), DeductionInfo(0) {}
-
-    /// \brief Determines whether this template is an actual instantiation
-    /// that should be counted toward the maximum instantiation depth.
-    bool isInstantiationRecord() const;
-
-    friend bool operator==(const ActiveTemplateInstantiation &X,
-                           const ActiveTemplateInstantiation &Y) {
-      if (X.Kind != Y.Kind)
-        return false;
-
-      if (X.Entity != Y.Entity)
-        return false;
-
-      switch (X.Kind) {
-      case TemplateInstantiation:
-      case ExceptionSpecInstantiation:
-        return true;
-
-      case PriorTemplateArgumentSubstitution:
-      case DefaultTemplateArgumentChecking:
-        return X.Template == Y.Template && X.TemplateArgs == Y.TemplateArgs;
-
-      case DefaultTemplateArgumentInstantiation:
-      case ExplicitTemplateArgumentSubstitution:
-      case DeducedTemplateArgumentSubstitution:
-      case DefaultFunctionArgumentInstantiation:
-        return X.TemplateArgs == Y.TemplateArgs;
-
-      }
-
-      llvm_unreachable("Invalid InstantiationKind!");
-    }
-
-    friend bool operator!=(const ActiveTemplateInstantiation &X,
-                           const ActiveTemplateInstantiation &Y) {
-      return !(X == Y);
-    }
-  };
-
   /// \brief List of active template instantiations.
   ///
   /// This vector is treated as a stack. As one template instantiation
@@ -6138,6 +6026,14 @@
   /// to implement it anywhere else.
   ActiveTemplateInstantiation LastTemplateInstantiationErrorContext;
 
+  /// \brief The template instantiation observer to trace or track 
+  /// instantiations (observers can be chained).
+  ///
+  /// This observer is used to print, trace or track template
+  /// instantiations as they are being constructed. For example, 
+  /// the 'templight' option is implemented with one such observer.
+  OwningPtr<TemplateInstantiationObserver> TemplateInstObserverChain;
+
   /// \brief The current index into pack expansion arguments that will be
   /// used for substitution of parameter packs.
   ///
Index: include/clang/Sema/TemplateInstObserver.h
===================================================================
--- include/clang/Sema/TemplateInstObserver.h	(revision 0)
+++ include/clang/Sema/TemplateInstObserver.h	(working copy)
@@ -0,0 +1,84 @@
+
+#ifndef LLVM_CLANG_TEMPLATE_INST_OBSERVER_H
+#define LLVM_CLANG_TEMPLATE_INST_OBSERVER_H
+
+#include "clang/Basic/SourceLocation.h"
+
+#include "llvm/ADT/OwningPtr.h"
+
+namespace clang {
+  
+  class Sema;
+  class ActiveTemplateInstantiation;
+  
+
+class TemplateInstantiationObserver {
+public:
+  
+  /// \brief Called after doing AST-parsing.
+  void initialize(const Sema &TheSema) {
+    this->initializeImpl(TheSema);
+    if ( NextObserver )
+      NextObserver->initialize(TheSema);
+  };
+  
+  /// \brief Called after AST-parsing is completed.
+  void finalize(const Sema &TheSema) {
+    this->finalizeImpl(TheSema);
+    if ( NextObserver )
+      NextObserver->finalize(TheSema);
+  };
+  
+  /// \brief Called when instantiation of a template just began.
+  void atTemplateBegin(const Sema &TheSema, const ActiveTemplateInstantiation &Inst) {
+    this->atTemplateBeginImpl(TheSema, Inst);
+    if ( NextObserver )
+      NextObserver->atTemplateBegin(TheSema, Inst);
+  };
+  
+  /// \brief Called when instantiation of a template is just about to end.
+  void atTemplateEnd(const Sema &TheSema, const ActiveTemplateInstantiation& Inst) {
+    this->atTemplateEndImpl(TheSema, Inst);
+    if ( NextObserver )
+      NextObserver->atTemplateEnd(TheSema, Inst);
+  };
+  
+  virtual ~TemplateInstantiationObserver() { };
+  
+  /// \brief Appends a new observer to the end of this list.
+  /// \note This function uses a tail-call recursion (and is not performance-critical).
+  static void appendNewObserver(OwningPtr<TemplateInstantiationObserver>& CurrentChain,
+                                TemplateInstantiationObserver* NewObserver) {
+    if ( !CurrentChain.isValid() ) {
+      CurrentChain.reset(NewObserver);
+      return;
+    };
+    appendNewObserver(CurrentChain->NextObserver, NewObserver);
+  };
+  
+protected:
+  
+  /// \brief Called after doing AST-parsing.
+  virtual void initializeImpl(const Sema &TheSema) { };
+  /// \brief Called after AST-parsing is completed.
+  virtual void finalizeImpl(const Sema &TheSema) { };
+  
+  /// \brief Called when instantiation of a template just began.
+  virtual void atTemplateBeginImpl(const Sema &TheSema, 
+                                   const ActiveTemplateInstantiation& Inst) { };
+  /// \brief Called when instantiation of a template is just about to end.
+  virtual void atTemplateEndImpl(const Sema &TheSema, 
+                                 const ActiveTemplateInstantiation& Inst) { };
+  
+  OwningPtr<TemplateInstantiationObserver> NextObserver;
+};
+
+
+
+}
+
+
+#endif
+
+
+
Index: include/clang/Sema/TemplightTracer.h
===================================================================
--- include/clang/Sema/TemplightTracer.h	(revision 0)
+++ include/clang/Sema/TemplightTracer.h	(working copy)
@@ -0,0 +1,91 @@
+
+#ifndef LLVM_CLANG_TEMPLIGHT_TRACER_H
+#define LLVM_CLANG_TEMPLIGHT_TRACER_H
+
+#include "clang/Sema/TemplateInstObserver.h"
+
+namespace clang {
+
+
+class TemplightTracer : public TemplateInstantiationObserver {
+public:
+  
+  class TracePrinter; // forward-decl.
+  
+protected:
+  
+  virtual void initializeImpl(const Sema &TheSema);
+  virtual void finalizeImpl(const Sema &TheSema);
+  virtual void atTemplateBeginImpl(const Sema &TheSema, const ActiveTemplateInstantiation& Inst);
+  virtual void atTemplateEndImpl(const Sema &TheSema, const ActiveTemplateInstantiation& Inst);
+  
+private:
+  
+  unsigned MemoryFlag : 1;
+  unsigned SafeModeFlag : 1;
+  unsigned IgnoreSystemFlag : 1;
+  
+  OwningPtr<TracePrinter> TemplateTracePrinter;
+  
+public:
+  
+  /// \brief Sets the format type of the template trace file.
+  /// The argument can be xml/yaml/text
+  TemplightTracer(const Sema &TheSema, 
+                  std::string Output = "", 
+                  const std::string& Format = "yaml",
+                  bool Memory = false, 
+                  bool Safemode = false,
+                  bool IgnoreSystem = false);
+  
+  virtual ~TemplightTracer();
+  
+  bool getMemoryFlag() const { return MemoryFlag; };
+  bool getSafeModeFlag() const { return SafeModeFlag; };
+  
+};
+
+
+
+class TemplightDebugger : public TemplateInstantiationObserver {
+public:
+  
+  class InteractiveAgent; // forward-decl.
+  
+protected:
+  
+  virtual void initializeImpl(const Sema &TheSema);
+  virtual void finalizeImpl(const Sema &TheSema);
+  virtual void atTemplateBeginImpl(const Sema &TheSema, const ActiveTemplateInstantiation& Inst);
+  virtual void atTemplateEndImpl(const Sema &TheSema, const ActiveTemplateInstantiation& Inst);
+  
+private:
+  
+  unsigned MemoryFlag : 1;
+  unsigned IgnoreSystemFlag : 1;
+  
+  OwningPtr<InteractiveAgent> Interactor;
+  
+public:
+  
+  /// \brief Construct the templight debugger.
+  TemplightDebugger(const Sema &TheSema, 
+                    bool Memory = false, 
+                    bool IgnoreSystem = false);
+  
+  virtual ~TemplightDebugger();
+  
+  bool getMemoryFlag() const { return MemoryFlag; };
+  
+};
+
+
+
+
+}
+
+
+#endif
+
+
+
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp	(revision 202671)
+++ lib/Driver/Tools.cpp	(working copy)
@@ -3011,6 +3011,18 @@
     CmdArgs.push_back(A->getValue());
   }
 
+  // BEGIN TEMPLIGHT
+  if (Arg *A = Args.getLastArg(options::OPT_templight_output)) {
+    CmdArgs.push_back("-templight-output");
+    CmdArgs.push_back(A->getValue());
+  }
+
+  if (Arg *A = Args.getLastArg(options::OPT_templight_format)) {
+      CmdArgs.push_back("-templight-format");
+      CmdArgs.push_back(A->getValue());
+  }
+  // END TEMPLIGHT
+
   if (Arg *A = Args.getLastArg(options::OPT_fconstexpr_depth_EQ)) {
     CmdArgs.push_back("-fconstexpr-depth");
     CmdArgs.push_back(A->getValue());
@@ -3159,6 +3171,14 @@
   Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_parseable_fixits);
   Args.AddLastArg(CmdArgs, options::OPT_ftime_report);
   Args.AddLastArg(CmdArgs, options::OPT_ftrapv);
+  // BEGIN TEMPLIGHT
+  Args.AddLastArg(CmdArgs, options::OPT_templight);
+  Args.AddLastArg(CmdArgs, options::OPT_templight_stdout);
+  Args.AddLastArg(CmdArgs, options::OPT_templight_memory);
+  Args.AddLastArg(CmdArgs, options::OPT_templight_safe_mode);
+  Args.AddLastArg(CmdArgs, options::OPT_templight_ignore_system);
+  Args.AddLastArg(CmdArgs, options::OPT_templight_debugger);
+  // END TEMPLIGHT
 
   if (Arg *A = Args.getLastArg(options::OPT_ftrapv_handler_EQ)) {
     CmdArgs.push_back("-ftrapv-handler");
Index: lib/Frontend/CompilerInstance.cpp
===================================================================
--- lib/Frontend/CompilerInstance.cpp	(revision 202671)
+++ lib/Frontend/CompilerInstance.cpp	(working copy)
@@ -30,6 +30,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Sema/Sema.h"
+#include "clang/Sema/TemplightTracer.h"
 #include "clang/Serialization/ASTReader.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Config/config.h"
@@ -434,6 +435,29 @@
                                   CodeCompleteConsumer *CompletionConsumer) {
   TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(),
                          TUKind, CompletionConsumer));
+  // BEGIN TEMPLIGHT
+  // NOTE: The Sema is sufficiently constructed at this point because 
+  // only the Sema's SourceManager is needed by TemplightTracer class.
+  if ( getInvocation().getFrontendOpts().Templight ) {
+    TemplateInstantiationObserver::appendNewObserver(
+      TheSema->TemplateInstObserverChain,
+      new TemplightTracer(*TheSema, 
+        ( getInvocation().getFrontendOpts().TemplightStdout ? 
+          std::string("stdout") : 
+          getInvocation().getFrontendOpts().TemplightOutputFile ),
+        getInvocation().getFrontendOpts().TemplightFormat, 
+        getInvocation().getFrontendOpts().TemplightMemory, 
+        getInvocation().getFrontendOpts().TemplightSafeMode, 
+        getInvocation().getFrontendOpts().TemplightIgnoreSystem));
+  }
+  if ( getInvocation().getFrontendOpts().TemplightDebugger ) {
+    TemplateInstantiationObserver::appendNewObserver(
+      TheSema->TemplateInstObserverChain,
+      new TemplightDebugger(*TheSema, 
+        getInvocation().getFrontendOpts().TemplightMemory, 
+        getInvocation().getFrontendOpts().TemplightIgnoreSystem));
+  }
+  // END TEMPLIGHT
 }
 
 // Output Files
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp	(revision 202671)
+++ lib/Frontend/CompilerInvocation.cpp	(working copy)
@@ -772,6 +772,23 @@
   Opts.UseGlobalModuleIndex = !Args.hasArg(OPT_fno_modules_global_index);
   Opts.GenerateGlobalModuleIndex = Opts.UseGlobalModuleIndex;
   
+  // BEGIN TEMPLIGHT
+  Opts.Templight = Args.hasArg(OPT_templight);
+  Opts.TemplightStdout = Args.hasArg(OPT_templight_stdout);
+  Opts.TemplightMemory = Args.hasArg(OPT_templight_memory);
+  Opts.TemplightSafeMode = Args.hasArg(OPT_templight_safe_mode);
+  Opts.TemplightIgnoreSystem = Args.hasArg(OPT_templight_ignore_system);
+  Opts.TemplightDebugger = Args.hasArg(OPT_templight_debugger);
+
+  if (const Arg *A = Args.getLastArg(OPT_templight_output)) {
+    Opts.TemplightOutputFile = A->getValue();
+  }
+
+  if (const Arg *A = Args.getLastArg(OPT_templight_format)) {
+    Opts.TemplightFormat = A->getValue();
+  }
+  // END TEMPLIGHT 
+
   Opts.CodeCompleteOpts.IncludeMacros
     = Args.hasArg(OPT_code_completion_macros);
   Opts.CodeCompleteOpts.IncludeCodePatterns
Index: lib/Parse/ParseAST.cpp
===================================================================
--- lib/Parse/ParseAST.cpp	(revision 202671)
+++ lib/Parse/ParseAST.cpp	(working copy)
@@ -23,6 +23,7 @@
 #include "clang/Sema/ExternalSemaSource.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaConsumer.h"
+#include "clang/Sema/TemplateInstObserver.h"
 #include "llvm/ADT/OwningPtr.h"
 #include "llvm/Support/CrashRecoveryContext.h"
 #include <cstdio>
@@ -106,6 +107,11 @@
   // Also turn on collection of stats inside of the Sema object.
   bool OldCollectStats = PrintStats;
   std::swap(OldCollectStats, S.CollectStats);
+  
+  // Initialize the template instantiation observer chain.
+  // FIXME: See note on "finalize()" below.
+  if (S.TemplateInstObserverChain.isValid())
+    S.TemplateInstObserverChain->initialize(S);
 
   ASTConsumer *Consumer = &S.getASTConsumer();
 
@@ -151,7 +157,16 @@
     Consumer->HandleTopLevelDecl(DeclGroupRef(*I));
   
   Consumer->HandleTranslationUnit(S.getASTContext());
-
+  
+  // Finalize the template instantiation observer chain.
+  // FIXME: This should be done in the Sema class, but because 
+  // Sema does not have a reliable "Finalize" function (it has a 
+  // destructor, but it is not guaranteed to be called ("-disable-free")).
+  // So, for symmetry, do the init here, because the final will appear
+  // later in this function.
+  if (S.TemplateInstObserverChain.isValid())
+    S.TemplateInstObserverChain->finalize(S);
+  
   std::swap(OldCollectStats, S.CollectStats);
   if (PrintStats) {
     llvm::errs() << "\nSTATISTICS:\n";
Index: lib/Sema/ActiveTemplateInst.cpp
===================================================================
--- lib/Sema/ActiveTemplateInst.cpp	(revision 0)
+++ lib/Sema/ActiveTemplateInst.cpp	(working copy)
@@ -0,0 +1,61 @@
+
+#include "clang/Sema/ActiveTemplateInst.h"
+
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang {
+
+bool operator==(const ActiveTemplateInstantiation &X,
+                const ActiveTemplateInstantiation &Y) {
+  if (X.Kind != Y.Kind)
+    return false;
+
+  if (X.Entity != Y.Entity)
+    return false;
+
+  switch (X.Kind) {
+    case ActiveTemplateInstantiation::TemplateInstantiation:
+    case ActiveTemplateInstantiation::ExceptionSpecInstantiation:
+    case ActiveTemplateInstantiation::Memoization:
+      return true;
+    
+    case ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution:
+    case ActiveTemplateInstantiation::DefaultTemplateArgumentChecking:
+      return X.Template == Y.Template && X.TemplateArgs == Y.TemplateArgs;
+    
+    case ActiveTemplateInstantiation::DefaultTemplateArgumentInstantiation:
+    case ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution:
+    case ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution:
+    case ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation:
+      return X.TemplateArgs == Y.TemplateArgs;
+  }
+
+  llvm_unreachable("Invalid InstantiationKind!");
+}
+
+bool ActiveTemplateInstantiation::isInstantiationRecord() const {
+  switch (Kind) {
+    case TemplateInstantiation:
+    case ExceptionSpecInstantiation:
+    case DefaultTemplateArgumentInstantiation:
+    case DefaultFunctionArgumentInstantiation:
+    case ExplicitTemplateArgumentSubstitution:
+    case DeducedTemplateArgumentSubstitution:
+    case PriorTemplateArgumentSubstitution:
+      return true;
+
+    case DefaultTemplateArgumentChecking:
+      return false;
+    
+    // FIXME Should this do a break or not? 
+    // Memoization kind should never occur here, so, it doesn't really matter.
+    case Memoization:
+      break;
+  }
+
+  llvm_unreachable("Invalid InstantiationKind!");
+}
+
+
+}
+
Index: lib/Sema/CMakeLists.txt
===================================================================
--- lib/Sema/CMakeLists.txt	(revision 202671)
+++ lib/Sema/CMakeLists.txt	(working copy)
@@ -3,6 +3,7 @@
   )
 
 add_clang_library(clangSema
+  ActiveTemplateInst.cpp
   AnalysisBasedWarnings.cpp
   AttributeList.cpp
   CodeCompleteConsumer.cpp
@@ -47,6 +48,7 @@
   SemaTemplateInstantiateDecl.cpp
   SemaTemplateVariadic.cpp
   SemaType.cpp
+  TemplightTracer.cpp
   TypeLocBuilder.cpp
 
   LINK_LIBS
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp	(revision 202671)
+++ lib/Sema/Sema.cpp	(working copy)
@@ -36,10 +36,12 @@
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaConsumer.h"
 #include "clang/Sema/TemplateDeduction.h"
+#include "clang/Sema/TemplateInstObserver.h"
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/Support/CrashRecoveryContext.h"
+
 using namespace clang;
 using namespace sema;
 
Index: lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiate.cpp	(revision 202671)
+++ lib/Sema/SemaTemplateInstantiate.cpp	(working copy)
@@ -23,6 +23,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Template.h"
 #include "clang/Sema/TemplateDeduction.h"
+#include "clang/Sema/TemplateInstObserver.h"
 
 using namespace clang;
 using namespace sema;
@@ -182,24 +183,6 @@
   return Result;
 }
 
-bool Sema::ActiveTemplateInstantiation::isInstantiationRecord() const {
-  switch (Kind) {
-  case TemplateInstantiation:
-  case ExceptionSpecInstantiation:
-  case DefaultTemplateArgumentInstantiation:
-  case DefaultFunctionArgumentInstantiation:
-  case ExplicitTemplateArgumentSubstitution:
-  case DeducedTemplateArgumentSubstitution:
-  case PriorTemplateArgumentSubstitution:
-    return true;
-
-  case DefaultTemplateArgumentChecking:
-    return false;
-  }
-
-  llvm_unreachable("Invalid InstantiationKind!");
-}
-
 Sema::InstantiatingTemplate::
 InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
                       Decl *Entity,
@@ -220,6 +203,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -243,6 +228,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -268,6 +255,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -294,6 +283,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
     
     if (!Inst.isInstantiationRecord())
       ++SemaRef.NonInstantiationEntries;
@@ -322,6 +313,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -345,6 +338,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -369,6 +364,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -393,6 +390,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -417,6 +416,8 @@
     Inst.InstantiationRange = InstantiationRange;
     SemaRef.InNonInstantiationSFINAEContext = false;
     SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   }
 }
 
@@ -441,6 +442,8 @@
   Inst.InstantiationRange = InstantiationRange;
   SemaRef.InNonInstantiationSFINAEContext = false;
   SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+  if ( SemaRef.TemplateInstObserverChain.isValid() )
+    SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, Inst);
   
   assert(!Inst.isInstantiationRecord());
   ++SemaRef.NonInstantiationEntries;
@@ -465,6 +468,10 @@
         SemaRef.LookupModulesCache.erase(M);
       SemaRef.ActiveTemplateInstantiationLookupModules.pop_back();
     }
+    
+    if ( SemaRef.TemplateInstObserverChain.isValid() )
+      SemaRef.TemplateInstObserverChain->atTemplateEnd(
+        SemaRef, SemaRef.ActiveTemplateInstantiations.back());
 
     SemaRef.ActiveTemplateInstantiations.pop_back();
     Invalid = true;
@@ -677,6 +684,9 @@
         << cast<FunctionDecl>(Active->Entity)
         << Active->InstantiationRange;
       break;
+
+    case ActiveTemplateInstantiation::Memoization:
+      break;
     }
   }
 }
@@ -717,6 +727,9 @@
       // or deduced template arguments, so SFINAE applies.
       assert(Active->DeductionInfo && "Missing deduction info pointer");
       return Active->DeductionInfo;
+
+    case ActiveTemplateInstantiation::Memoization:
+      break;
     }
   }
 
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp	(revision 202671)
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp	(working copy)
@@ -22,6 +22,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/PrettyDeclStackTrace.h"
 #include "clang/Sema/Template.h"
+#include "clang/Sema/TemplateInstObserver.h"
 
 using namespace clang;
 
@@ -3201,7 +3202,7 @@
   // into a template instantiation for this specific function template
   // specialization, which is not a SFINAE context, so that we diagnose any
   // further errors in the declaration itself.
-  typedef Sema::ActiveTemplateInstantiation ActiveInstType;
+  typedef ActiveTemplateInstantiation ActiveInstType;
   ActiveInstType &ActiveInst = SemaRef.ActiveTemplateInstantiations.back();
   if (ActiveInst.Kind == ActiveInstType::ExplicitTemplateArgumentSubstitution ||
       ActiveInst.Kind == ActiveInstType::DeducedTemplateArgumentSubstitution) {
@@ -3210,8 +3211,12 @@
       assert(FunTmpl->getTemplatedDecl() == Tmpl &&
              "Deduction from the wrong function template?");
       (void) FunTmpl;
+      if ( SemaRef.TemplateInstObserverChain.isValid() )
+        SemaRef.TemplateInstObserverChain->atTemplateEnd(SemaRef, ActiveInst);
       ActiveInst.Kind = ActiveInstType::TemplateInstantiation;
       ActiveInst.Entity = New;
+      if ( SemaRef.TemplateInstObserverChain.isValid() )
+        SemaRef.TemplateInstObserverChain->atTemplateBegin(SemaRef, ActiveInst);
     }
   }
 
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp	(revision 202671)
+++ lib/Sema/SemaType.cpp	(working copy)
@@ -31,6 +31,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/Template.h"
+#include "clang/Sema/TemplateInstObserver.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -5093,6 +5094,15 @@
         createImplicitModuleImport(Loc, Owner);
       }
     }
+    else if (Def && TemplateInstObserverChain.isValid()) {
+      ActiveTemplateInstantiation TempInst;
+      TempInst.Kind = ActiveTemplateInstantiation::Memoization;
+      TempInst.Template = Def;
+      TempInst.Entity = Def;
+      TempInst.PointOfInstantiation = Loc;
+      TemplateInstObserverChain->atTemplateBegin(*this, TempInst);
+      TemplateInstObserverChain->atTemplateEnd(*this, TempInst);
+    }
 
     // We lock in the inheritance model once somebody has asked us to ensure
     // that a pointer-to-member type is complete.
Index: lib/Sema/TemplightTracer.cpp
===================================================================
--- lib/Sema/TemplightTracer.cpp	(revision 0)
+++ lib/Sema/TemplightTracer.cpp	(working copy)
@@ -0,0 +1,869 @@
+
+#include "clang/AST/Decl.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/ActiveTemplateInst.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/TemplightTracer.h"
+
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Object/YAML.h"
+
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/Process.h"
+
+#include <string>
+#include <vector>
+
+#include <algorithm>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+
+
+namespace clang {
+
+namespace {
+
+struct RawTraceEntry {
+  bool IsTemplateBegin;
+  ActiveTemplateInstantiation::InstantiationKind InstantiationKind;
+  Decl *Entity;
+  SourceLocation PointOfInstantiation;
+  double TimeStamp;
+  size_t MemoryUsage;
+  
+  RawTraceEntry() : IsTemplateBegin(true), 
+    InstantiationKind(ActiveTemplateInstantiation::TemplateInstantiation),
+    Entity(0), TimeStamp(0.0), MemoryUsage(0) { };
+};
+
+struct PrintableTraceEntry {
+  bool IsTemplateBegin;
+  std::string InstantiationKind;
+  std::string Name;
+  std::string FileName;
+  int Line;
+  int Column;
+  double TimeStamp;
+  size_t MemoryUsage;
+};
+
+
+const char* const InstantiationKindStrings[] = { 
+  "TemplateInstantiation",
+  "DefaultTemplateArgumentInstantiation",
+  "DefaultFunctionArgumentInstantiation",
+  "ExplicitTemplateArgumentSubstitution",
+  "DeducedTemplateArgumentSubstitution", 
+  "PriorTemplateArgumentSubstitution",
+  "DefaultTemplateArgumentChecking", 
+  "ExceptionSpecInstantiation",
+  "Memoization" };
+
+
+PrintableTraceEntry rawToPrintable(const Sema &TheSema, const RawTraceEntry& Entry) {
+  PrintableTraceEntry Ret;
+  
+  Ret.IsTemplateBegin = Entry.IsTemplateBegin;
+  Ret.InstantiationKind = InstantiationKindStrings[Entry.InstantiationKind];
+  
+  if (Entry.IsTemplateBegin) {
+    NamedDecl *NamedTemplate = dyn_cast<NamedDecl>(Entry.Entity);
+    if (NamedTemplate) {
+      llvm::raw_string_ostream OS(Ret.Name);
+      NamedTemplate->getNameForDiagnostic(OS, TheSema.getLangOpts(), true);
+    }
+    
+    PresumedLoc Loc = TheSema.getSourceManager().getPresumedLoc(Entry.PointOfInstantiation);
+    
+    Ret.FileName = Loc.getFilename();
+    Ret.Line = Loc.getLine();
+    Ret.Column = Loc.getColumn();
+  }
+  
+  
+  Ret.TimeStamp = Entry.TimeStamp;
+  Ret.MemoryUsage = Entry.MemoryUsage;
+  
+  return Ret;
+}
+
+
+
+
+std::string escapeXml(const std::string& Input) {
+  std::string Result;
+  Result.reserve(64);
+
+  unsigned i, pos = 0;
+  for (i = 0; i < Input.length(); ++i) {
+    if (Input[i] == '<' || Input[i] == '>' || Input[i] == '"'
+      || Input[i] == '\'' || Input[i] == '&') {
+      Result.insert(Result.length(), Input, pos, i - pos);
+      pos = i + 1;
+      switch (Input[i]) {
+      case '<':
+        Result += "<";
+        break;
+      case '>':
+        Result += ">";
+        break;
+      case '\'':
+        Result += "'";
+        break;
+      case '"':
+        Result += """;
+        break;
+      case '&':
+        Result += "&";
+        break;
+      default:
+        break;
+      }
+    }
+  }
+  Result.insert(Result.length(), Input, pos, i - pos);
+  return Result;
+}
+
+
+} // unnamed namespace
+
+} // namespace clang
+
+
+
+
+namespace llvm {
+namespace yaml {
+
+template <>
+struct ScalarEnumerationTraits<
+    clang::ActiveTemplateInstantiation::InstantiationKind> {
+  static void enumeration(IO &io,
+    clang::ActiveTemplateInstantiation::InstantiationKind &value) {
+    using namespace clang;
+    io.enumCase(value, "TemplateInstantiation",
+      ActiveTemplateInstantiation::TemplateInstantiation);
+    io.enumCase(value, "DefaultTemplateArgumentInstantiation",
+      ActiveTemplateInstantiation::DefaultTemplateArgumentInstantiation);
+    io.enumCase(value, "DefaultFunctionArgumentInstantiation",
+      ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation);
+    io.enumCase(value, "ExplicitTemplateArgumentSubstitution",
+      ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution);
+    io.enumCase(value, "DeducedTemplateArgumentSubstitution",
+      ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution);
+    io.enumCase(value, "PriorTemplateArgumentSubstitution",
+      ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution);
+    io.enumCase(value, "DefaultTemplateArgumentChecking",
+      ActiveTemplateInstantiation::DefaultTemplateArgumentChecking);
+    io.enumCase(value, "ExceptionSpecInstantiation",
+      ActiveTemplateInstantiation::ExceptionSpecInstantiation);
+    io.enumCase(value, "Memoization",
+      ActiveTemplateInstantiation::Memoization);
+  }
+};
+
+template <>
+struct MappingTraits<clang::PrintableTraceEntry> {
+  static void mapping(IO &io, clang::PrintableTraceEntry &Entry) {
+    io.mapRequired("IsTemplateBegin", Entry.IsTemplateBegin);
+    io.mapRequired("Kind", Entry.InstantiationKind);
+    io.mapOptional("Name", Entry.Name);
+    io.mapOptional("FileName", Entry.FileName);
+    io.mapOptional("Line", Entry.Line);
+    io.mapOptional("Column", Entry.Column);
+    io.mapRequired("TimeStamp", Entry.TimeStamp);
+    io.mapOptional("MemoryUsage", Entry.MemoryUsage);
+  }
+};
+
+} // namespace yaml
+} // namespace llvm
+
+
+
+namespace clang {
+
+class TemplightTracer::TracePrinter {
+protected:
+  virtual void startTraceImpl(const Sema &) = 0;
+  virtual void endTraceImpl(const Sema &) = 0;
+  virtual void printEntryImpl(const Sema &, const PrintableTraceEntry &) = 0;
+  
+public:
+  
+  bool shouldIgnoreEntry(const RawTraceEntry &Entry) const {
+    if ( Entry.InstantiationKind == ActiveTemplateInstantiation::Memoization ) {
+      if ( !LastBeginEntry.IsTemplateBegin
+           && ( LastBeginEntry.InstantiationKind == Entry.InstantiationKind )
+           && ( LastBeginEntry.Entity == Entry.Entity ) ) {
+        return true;
+      }
+    }
+    return false;
+  };
+  
+  void printRawEntry(const Sema &TheSema, const RawTraceEntry &Entry) {
+    if ( shouldIgnoreEntry(Entry) )
+      return;
+    this->printEntryImpl(TheSema, rawToPrintable(TheSema, Entry));
+    TraceOS->flush();
+    if ( Entry.IsTemplateBegin )
+      LastBeginEntry = Entry;
+    if ( !Entry.IsTemplateBegin && 
+         ( Entry.InstantiationKind == ActiveTemplateInstantiation::Memoization ) )
+      LastBeginEntry.IsTemplateBegin = false;
+  };
+  
+  void printCachedEntries(const Sema &TheSema) {
+    for(std::vector<RawTraceEntry>::iterator it = TraceEntries.begin();
+        it != TraceEntries.end(); ++it) {
+      this->printEntryImpl(TheSema, rawToPrintable(TheSema, *it));
+    }
+    TraceEntries.clear();
+  };
+  
+  void cacheEntry(const Sema &TheSema, const RawTraceEntry &Entry) {
+    if ( shouldIgnoreEntry(Entry) )
+      return;
+    TraceEntries.push_back(Entry);
+    if ( Entry.IsTemplateBegin )
+      LastBeginEntry = Entry;
+    if ( !Entry.IsTemplateBegin && 
+         ( Entry.InstantiationKind == ActiveTemplateInstantiation::Memoization ) )
+      LastBeginEntry.IsTemplateBegin = false;
+    if ( !Entry.IsTemplateBegin &&
+         ( Entry.InstantiationKind == TraceEntries.front().InstantiationKind ) &&
+         ( Entry.Entity == TraceEntries.front().Entity ) )
+      printCachedEntries(TheSema);
+  };
+  
+  void startTrace(const Sema &TheSema) {
+    this->startTraceImpl(TheSema);
+  };
+  
+  void endTrace(const Sema &TheSema) {
+    printCachedEntries(TheSema);
+    this->endTraceImpl(TheSema);
+    TraceOS->flush();
+  };
+  
+  TracePrinter(const std::string &Output) : TraceOS(0) {
+    if ( Output == "stdout" ) {
+      TraceOS = &llvm::outs();
+    } else {
+      std::string error;
+      TraceOS = new llvm::raw_fd_ostream(Output.c_str(), error, llvm::sys::fs::F_None);
+      if ( !error.empty() ) {
+        llvm::errs() <<
+          "Error: [Templight] Can not open file to write trace of template instantiations: "
+          << Output << " Error: " << error;
+        TraceOS = 0;
+        return;
+      }
+    }
+  };
+  
+  virtual ~TracePrinter() {
+    if ( TraceOS ) {
+      TraceOS->flush();
+      if ( TraceOS != &llvm::outs() )
+        delete TraceOS;
+    }
+  };
+  
+  std::vector<RawTraceEntry> TraceEntries;
+  RawTraceEntry LastBeginEntry;
+  
+  llvm::raw_ostream* TraceOS;
+  
+  bool isValid() const { return TraceOS; };
+};
+
+
+
+
+namespace {
+
+class YamlPrinter : public TemplightTracer::TracePrinter {
+protected:
+  void startTraceImpl(const Sema &) {
+    Output.reset(new llvm::yaml::Output(*this->TraceOS));
+    Output->beginDocuments();
+    Output->beginFlowSequence();
+  };
+  void endTraceImpl(const Sema &) {
+    Output->endFlowSequence();
+    Output->endDocuments();
+  };
+  void printEntryImpl(const Sema &, const PrintableTraceEntry& Entry) {
+    void *SaveInfo;
+    if ( Output->preflightFlowElement(1, SaveInfo) ) {
+      llvm::yaml::yamlize(*Output, const_cast<PrintableTraceEntry&>(Entry), 
+                          true);
+      Output->postflightFlowElement(SaveInfo);
+    }
+  };
+  
+public:
+  
+  YamlPrinter(const std::string &Output) : 
+              TemplightTracer::TracePrinter(Output) { };
+  
+private:
+  OwningPtr<llvm::yaml::Output> Output;
+};
+
+
+
+class XmlPrinter : public TemplightTracer::TracePrinter {
+protected:
+  void startTraceImpl(const Sema &) {
+    (*this->TraceOS) <<
+      "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
+      "<Trace>\n";
+  };
+  void endTraceImpl(const Sema &) {
+    (*this->TraceOS) << "</Trace>\n";
+  };
+  void printEntryImpl(const Sema &, const PrintableTraceEntry& Entry) {
+    if (Entry.IsTemplateBegin) {
+      std::string EscapedName = escapeXml(Entry.Name);
+      (*this->TraceOS)
+        << llvm::format("<TemplateBegin>\n"
+          "    <Kind>%s</Kind>\n"
+          "    <Context context = \"%s\"/>\n"
+          "    <PointOfInstantiation>%s|%d|%d</PointOfInstantiation>\n",
+          Entry.InstantiationKind.c_str(), EscapedName.c_str(),
+          Entry.FileName.c_str(), Entry.Line, Entry.Column);
+
+      (*this->TraceOS) << llvm::format("    <TimeStamp time = \"%f\"/>\n"
+        "    <MemoryUsage bytes = \"%d\"/>\n"
+        "</TemplateBegin>\n", Entry.TimeStamp, Entry.MemoryUsage);
+    } else {
+      (*this->TraceOS)
+        << llvm::format("<TemplateEnd>\n"
+          "    <Kind>%s</Kind>\n"
+          "    <TimeStamp time = \"%f\"/>\n"
+          "    <MemoryUsage bytes = \"%d\"/>\n"
+          "</TemplateEnd>\n", Entry.InstantiationKind.c_str(),
+          Entry.TimeStamp, Entry.MemoryUsage);
+    }
+  };
+  
+public:
+  
+  XmlPrinter(const std::string &Output) : 
+             TemplightTracer::TracePrinter(Output) { };
+  
+};
+
+
+
+
+class TextPrinter : public TemplightTracer::TracePrinter {
+protected:
+  void startTraceImpl(const Sema &) { };
+  void endTraceImpl(const Sema &) { };
+  void printEntryImpl(const Sema &, const PrintableTraceEntry& Entry) { 
+    if (Entry.IsTemplateBegin) {
+      (*this->TraceOS)
+        << llvm::format(
+          "TemplateBegin\n"
+          "  Kind = %s\n"
+          "  Name = %s\n"
+          "  PointOfInstantiation = %s|%d|%d\n",
+          Entry.InstantiationKind.c_str(), Entry.Name.c_str(),
+          Entry.FileName.c_str(), Entry.Line, Entry.Column);
+
+      (*this->TraceOS) << llvm::format(
+        "  TimeStamp = %f\n"
+        "  MemoryUsage = %d\n"
+        , Entry.TimeStamp, Entry.MemoryUsage);
+    } else {
+      (*this->TraceOS)
+        << llvm::format(
+          "TemplateEnd\n"
+          "  Kind = %s\n"
+          "  TimeStamp = %f\n"
+          "  MemoryUsage = %d\n"
+          , Entry.InstantiationKind.c_str(),
+          Entry.TimeStamp, Entry.MemoryUsage);
+    }
+  };
+  
+public:
+  
+  TextPrinter(const std::string &Output) : 
+              TemplightTracer::TracePrinter(Output) { };
+  
+};
+
+
+} // unnamed namespace
+
+
+
+
+void TemplightTracer::atTemplateBeginImpl(const Sema &TheSema, 
+                          const ActiveTemplateInstantiation& Inst) {
+  if ( !TemplateTracePrinter.isValid() || 
+       ( IgnoreSystemFlag && TheSema.getSourceManager()
+                              .isInSystemHeader(Inst.PointOfInstantiation) ) )
+    return;
+  
+  RawTraceEntry Entry;
+
+  llvm::TimeRecord timeRecord = llvm::TimeRecord::getCurrentTime();
+
+  Entry.IsTemplateBegin = true;
+  Entry.InstantiationKind = Inst.Kind;
+  Entry.Entity = Inst.Entity;
+  Entry.PointOfInstantiation = Inst.PointOfInstantiation;
+  Entry.TimeStamp = timeRecord.getWallTime();
+  Entry.MemoryUsage = (MemoryFlag ? llvm::sys::Process::GetMallocUsage() : 0);
+  
+  if ( SafeModeFlag ) {
+    TemplateTracePrinter->printRawEntry(TheSema, Entry);
+  } else {
+    TemplateTracePrinter->cacheEntry(TheSema, Entry);
+  }
+}
+
+void TemplightTracer::atTemplateEndImpl(const Sema &TheSema, 
+                          const ActiveTemplateInstantiation& Inst) {
+  if ( !TemplateTracePrinter.isValid() || 
+       ( IgnoreSystemFlag && TheSema.getSourceManager()
+                              .isInSystemHeader(Inst.PointOfInstantiation) ) )
+    return;
+  
+  RawTraceEntry Entry;
+
+  llvm::TimeRecord timeRecord = llvm::TimeRecord::getCurrentTime();
+
+  Entry.IsTemplateBegin = false;
+  Entry.InstantiationKind = Inst.Kind;
+  Entry.Entity = Inst.Entity;
+  Entry.TimeStamp = timeRecord.getWallTime();
+  Entry.MemoryUsage = (MemoryFlag ? llvm::sys::Process::GetMallocUsage() : 0);
+
+  if ( SafeModeFlag ) {
+    TemplateTracePrinter->printRawEntry(TheSema, Entry);
+  } else {
+    TemplateTracePrinter->cacheEntry(TheSema, Entry);
+  }
+}
+
+
+TemplightTracer::TemplightTracer(const Sema &TheSema, 
+                                 std::string Output, 
+                                 const std::string& Format,
+                                 bool Memory, bool Safemode, 
+                                 bool IgnoreSystem) :
+                                 MemoryFlag(Memory),
+                                 SafeModeFlag(Safemode),
+                                 IgnoreSystemFlag(IgnoreSystem)  {
+  
+  std::string postfix;
+  if ( Output.empty() ) {
+    // then, derive output name from the input name:
+    FileID fileID = TheSema.getSourceManager().getMainFileID();
+    postfix = (MemoryFlag ? ".memory.trace." : ".trace.");
+    
+    Output =
+      TheSema.getSourceManager().getFileEntryForID(fileID)->getName();
+  }
+  
+  if ( ( Format.empty() ) || ( Format == "yaml" ) ) {
+    TemplateTracePrinter.reset(new YamlPrinter(
+      ( postfix == "" ? Output : (Output + postfix + "yaml") )
+    ));
+    return;
+  }
+  else if ( Format == "xml" ) {
+    TemplateTracePrinter.reset(new XmlPrinter(
+      ( postfix == "" ? Output : (Output + postfix + "xml") )
+    ));
+    return;
+  }
+  else if ( Format == "text" ) {
+    TemplateTracePrinter.reset(new TextPrinter(
+      ( postfix == "" ? Output : (Output + postfix + "txt") )
+    ));
+    return;
+  }
+  else {
+    llvm::errs() << "Error: [Templight] Unrecoginized template trace format:" << Format;
+  }
+  
+  if ( !TemplateTracePrinter.isValid() || !TemplateTracePrinter->isValid() ) {
+    if ( TemplateTracePrinter )
+      TemplateTracePrinter.reset();
+    llvm::errs() << "Note: [Templight] Template trace has been disabled.";
+  }
+  
+}
+
+
+TemplightTracer::~TemplightTracer() { 
+  // must be defined here due to TracePrinter being incomplete in header.
+}
+
+
+void TemplightTracer::initializeImpl(const Sema &TheSema) {
+  if ( TemplateTracePrinter.isValid() )
+    TemplateTracePrinter->startTrace(TheSema);
+}
+
+void TemplightTracer::finalizeImpl(const Sema &TheSema) {
+  if ( TemplateTracePrinter.isValid() )
+    TemplateTracePrinter->endTrace(TheSema);
+}
+
+
+
+
+
+
+
+class TemplightDebugger::InteractiveAgent {
+public:
+  
+  void printEntryImpl(const Sema &, const PrintableTraceEntry& Entry) { 
+    if (Entry.IsTemplateBegin) {
+      llvm::outs()
+        << "Entering " << Entry.InstantiationKind << " of " << Entry.Name << '\n'
+        << "  at " << Entry.FileName << '|' << Entry.Line << '|' << Entry.Column 
+        << " (Memory usage: " << Entry.MemoryUsage << ")\n";
+    } else {
+      llvm::outs()
+        << "Leaving  " << Entry.InstantiationKind << " of " << Entry.Name
+        << " (Memory usage: " << Entry.MemoryUsage << ")\n";
+    }
+  };
+  
+  bool shouldIgnoreEntry(const Sema &TheSema, const RawTraceEntry &Entry) const {
+    if ( ignoreAll )
+      return true;
+    if ( ignoreUntilBreakpoint ) {
+      std::string EntryName = "";
+      NamedDecl *NamedTemplate = dyn_cast<NamedDecl>(Entry.Entity);
+      if ( NamedTemplate ) {
+        llvm::raw_string_ostream OS(EntryName);
+        NamedTemplate->getNameForDiagnostic(OS, TheSema.getLangOpts(), true);
+      }
+      std::vector< std::string >::const_iterator it = std::find(
+        Breakpoints.begin(), Breakpoints.end(), EntryName);
+      if ( it != Breakpoints.end() )
+        return false;
+      else
+        return true;
+    }
+    if ( ignoreUntilLastEnds ) {
+      if ( ( EntriesStack.size() > 0 )
+           && ( EntriesStack.back().InstantiationKind == Entry.InstantiationKind )
+           && ( EntriesStack.back().Entity == Entry.Entity ) )
+        return false;
+      else
+        return true;
+    }
+    if ( Entry.InstantiationKind == ActiveTemplateInstantiation::Memoization ) {
+      if ( !LastBeginEntry.IsTemplateBegin
+           && ( LastBeginEntry.InstantiationKind == Entry.InstantiationKind )
+           && ( LastBeginEntry.Entity == Entry.Entity ) ) {
+        return true;
+      }
+    }
+    return false;
+  };
+  
+  static void getLineFromStdIn(std::string& s) {
+    char c = 0;
+    s = "";
+    int i = std::getchar();
+    if ( i == EOF ) {
+      std::clearerr(stdin);
+      return;
+    };
+    c = char(i);
+    while ( c != '\n' ) {
+      s.push_back(c);
+      i = std::getchar();
+      if ( i == EOF ) {
+        std::clearerr(stdin);
+        return;
+      };
+      c = char(i);
+    };
+  };
+  
+  static void flushStdIn() {
+    std::string s;
+    getLineFromStdIn(s);
+  };
+  
+  static void getTokenizeCommand(const std::string& s, std::string& com, std::string& arg) {
+    com = "";
+    unsigned i = 0;
+    while( ( i < s.size() ) && ( std::isspace(s[i]) ) )
+      ++i;
+    while( ( i < s.size() ) && ( !std::isspace(s[i]) ) ) {
+      com += s[i];
+      ++i;
+    };
+    while( ( i < s.size() ) && ( std::isspace(s[i]) ) )
+      ++i;
+    unsigned e = s.size();
+    while( ( e > i ) && ( std::isspace(s[e-1]) ) )
+      --e;
+    arg = s.substr(i, e - i);
+  };
+  
+  void processInputs(const Sema &TheSema) {
+    std::string user_in;
+    while(true) {
+      llvm::outs() << "(tdb) ";
+      getLineFromStdIn(user_in);
+      if ( user_in == "" )
+        user_in = LastUserCommand;
+      
+      LastUserCommand = user_in;
+      
+      std::string user_command, com_arg;
+      getTokenizeCommand(user_in, user_command, com_arg);
+      
+      if ( ( user_command == "i" ) ||
+           ( user_command == "info" ) ) {
+        if ( ( com_arg == "f" ) ||
+             ( com_arg == "frame" ) ) {
+          if ( EntriesStack.size() > 0 )
+            printEntryImpl(TheSema, rawToPrintable(TheSema, EntriesStack.back()));
+          continue;
+        }
+        else if ( ( com_arg == "b" ) ||
+                  ( com_arg == "break" ) ) {
+          for(unsigned i = 0; i < Breakpoints.size(); ++i)
+            if ( Breakpoints[i] != "-" )
+              llvm::outs() << "Breakpoint " << i 
+                           << " for " << Breakpoints[i] << '\n';
+          continue;
+        }
+        else if ( ( com_arg == "s" ) ||
+                  ( com_arg == "stack" ) ) {
+          user_command = "bt";
+        }
+        else {
+          llvm::outs() << "Invalid input!\n";
+          continue;
+        }
+        
+      }
+      
+      if ( ( user_command == "r" ) ||
+           ( user_command == "c" ) ||
+           ( user_command == "run" ) ||
+           ( user_command == "continue" ) ) {
+        ignoreAll = false;
+        ignoreUntilLastEnds = false;
+        ignoreUntilBreakpoint = true;
+        return;
+      }
+      else if ( ( user_command == "k" ) ||
+                ( user_command == "q" ) ||
+                ( user_command == "kill" ) ||
+                ( user_command == "quit" ) ) {
+        ignoreAll = true;
+        ignoreUntilLastEnds = false;
+        ignoreUntilBreakpoint = false;
+        return;
+      }
+      else if ( ( user_command == "n" ) ||
+                ( user_command == "next" ) ) {
+        ignoreAll = false;
+        ignoreUntilLastEnds = (EntriesStack.size() > 0);
+        ignoreUntilBreakpoint = false;
+        return;
+      }
+      else if ( ( user_command == "s" ) ||
+                ( user_command == "step" ) ) {
+        ignoreAll = false;
+        ignoreUntilLastEnds = false;
+        ignoreUntilBreakpoint = false;
+        return;
+      }
+      else if ( ( user_command == "b" ) ||
+                ( user_command == "break" ) ) {
+        std::vector< std::string >::iterator it = std::find(
+          Breakpoints.begin(), Breakpoints.end(), com_arg);
+        if ( it == Breakpoints.end() ) {
+          it = std::find(Breakpoints.begin(), Breakpoints.end(), "-");
+          if ( it == Breakpoints.end() )
+            it = Breakpoints.insert(Breakpoints.end(), com_arg);
+          else
+            *it = com_arg;
+        }
+        llvm::outs() << "Breakpoint " << (it - Breakpoints.begin()) 
+                     << " for " << com_arg << '\n';
+        continue;
+      }
+      else if ( ( user_command == "d" ) ||
+                ( user_command == "delete" ) ) {
+        int ind = 0;
+        if ( std::sscanf(com_arg.c_str(), "%d", &ind) < 1 ) {
+          llvm::outs() << "Invalid input!\n";
+          continue;
+        }
+        llvm::outs() << "Deleted breakpoint " << ind 
+                     << " for " << Breakpoints[ind] << '\n';
+        Breakpoints[ind] = "-";
+        continue;
+      }
+      else if ( ( user_command == "bt" ) ||
+                ( user_command == "backtrace" ) ||
+                ( user_command == "where" ) ) {
+        for(std::vector<RawTraceEntry>::reverse_iterator it = EntriesStack.rbegin();
+            it != EntriesStack.rend(); ++it) {
+          PrintableTraceEntry PEntry = rawToPrintable(TheSema, *it);
+          llvm::outs()
+            << PEntry.InstantiationKind << " of " << PEntry.Name 
+            << " at " << PEntry.FileName << '|' 
+            << PEntry.Line << '|' << PEntry.Column << '\n';
+        }
+        continue;
+      }
+      else {
+        llvm::outs() << "Invalid input!\n";
+        continue; 
+      }
+      
+    };
+  };
+  
+  void printRawEntry(const Sema &TheSema, const RawTraceEntry &Entry) {
+    if ( shouldIgnoreEntry(TheSema, Entry) )
+      return;
+    
+    if ( Entry.IsTemplateBegin ) {
+      EntriesStack.push_back(Entry);
+      LastBeginEntry = Entry;
+    };
+    
+    printEntryImpl(TheSema, rawToPrintable(TheSema, Entry));
+    
+    if ( !Entry.IsTemplateBegin && 
+         ( Entry.InstantiationKind == ActiveTemplateInstantiation::Memoization ) )
+      LastBeginEntry.IsTemplateBegin = false;
+    
+    if ( !Entry.IsTemplateBegin && ( EntriesStack.size() > 0 ) &&
+         ( Entry.InstantiationKind == EntriesStack.back().InstantiationKind ) &&
+         ( Entry.Entity == EntriesStack.back().Entity ) ) {
+      EntriesStack.pop_back();
+//       LastBeginEntry = EntriesStack.back();
+    };
+    
+    processInputs(TheSema);
+  };
+  
+  void startTrace(const Sema &TheSema) {
+    llvm::outs() << "Welcome to the Templight debugger!\n"
+                 << "Begin by entering 'run' after setting breakpoints.\n";
+    processInputs(TheSema);
+  };
+  
+  void endTrace(const Sema &TheSema) {
+    llvm::outs() << "Templight debugging session has ended. Goodbye!\n";
+  };
+  
+  InteractiveAgent() : ignoreAll(false), ignoreUntilLastEnds(false), 
+                       ignoreUntilBreakpoint(false) { };
+  
+  ~InteractiveAgent() { };
+  
+  std::vector<RawTraceEntry> EntriesStack;
+  RawTraceEntry LastBeginEntry;
+  
+  std::vector< std::string > Breakpoints;
+  
+  std::string LastUserCommand;
+  
+  unsigned ignoreAll : 1;
+  unsigned ignoreUntilLastEnds : 1;
+  unsigned ignoreUntilBreakpoint : 1;
+};
+
+
+
+
+
+
+void TemplightDebugger::initializeImpl(const Sema &TheSema) {
+  Interactor->startTrace(TheSema);
+}
+
+void TemplightDebugger::finalizeImpl(const Sema &TheSema) {
+  Interactor->endTrace(TheSema);
+}
+
+void TemplightDebugger::atTemplateBeginImpl(const Sema &TheSema, 
+                          const ActiveTemplateInstantiation& Inst) {
+  if ( IgnoreSystemFlag && TheSema.getSourceManager()
+                            .isInSystemHeader(Inst.PointOfInstantiation) )
+    return;
+  
+  RawTraceEntry Entry;
+
+  llvm::TimeRecord timeRecord = llvm::TimeRecord::getCurrentTime();
+
+  Entry.IsTemplateBegin = true;
+  Entry.InstantiationKind = Inst.Kind;
+  Entry.Entity = Inst.Entity;
+  Entry.PointOfInstantiation = Inst.PointOfInstantiation;
+  Entry.TimeStamp = timeRecord.getWallTime();
+  Entry.MemoryUsage = (MemoryFlag ? llvm::sys::Process::GetMallocUsage() : 0);
+  
+  Interactor->printRawEntry(TheSema, Entry);
+}
+
+void TemplightDebugger::atTemplateEndImpl(const Sema &TheSema, 
+                          const ActiveTemplateInstantiation& Inst) {
+  if ( IgnoreSystemFlag && TheSema.getSourceManager()
+                            .isInSystemHeader(Inst.PointOfInstantiation) )
+    return;
+  
+  RawTraceEntry Entry;
+
+  llvm::TimeRecord timeRecord = llvm::TimeRecord::getCurrentTime();
+
+  Entry.IsTemplateBegin = false;
+  Entry.InstantiationKind = Inst.Kind;
+  Entry.Entity = Inst.Entity;
+  Entry.TimeStamp = timeRecord.getWallTime();
+  Entry.MemoryUsage = (MemoryFlag ? llvm::sys::Process::GetMallocUsage() : 0);
+
+  Interactor->printRawEntry(TheSema, Entry);
+}
+
+
+TemplightDebugger::TemplightDebugger(const Sema &TheSema, 
+                                     bool Memory, bool IgnoreSystem) :
+                                     MemoryFlag(Memory),
+                                     IgnoreSystemFlag(IgnoreSystem) {
+  Interactor.reset(new InteractiveAgent());
+}
+
+
+TemplightDebugger::~TemplightDebugger() { 
+  // must be defined here due to TracePrinter being incomplete in header.
+}
+
+
+
+
+} // namespace clang
+
+
+
+


More information about the cfe-dev mailing list