// $Id: dynatracer.h,v 1.12 2018/01/03 21:32:54 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

/* Authors: Bob Giansiracusa, Mary Kuhner, Jon Yamato, and Joseph Felsenstein */


// The name of this file should be changed to "dynameter.h", but CVS makes that *so* difficult ...
#ifndef DYNATRACER_H
#define DYNATRACER_H

//------------------------------------------------------------------------------------
// Must include this file first, before testing DYNAMETER_LEVEL for definedness,
// because "local_build.h" defines that symbol.  It also defines some other compile-time
// symbols used to control expansion of some debugging macros defined in this file.

#include <iostream>
#include "local_build.h"

//------------------------------------------------------------------------------------

#ifdef DYNAMETER_LEVEL
#include <fstream>                      // Required to define std::ofstream.
#include "dynacount.h"                  // Required to define DYNAMETER_ARRAYSIZE.

//------------------------------------------------------------------------------------

// Used in experimental code - not used in current distribution.
// #define MEMORYTRACE
// #define MEMORYPRINT

//------------------------------------------------------------------------------------
// Definition of experimental class for testing overloading of new and delete.

#ifdef MEMORYTRACE                      // Under construction - not running in current distribution.

class S
{
  private:
    int i[100];
  public:
    S() { puts("S::S()"); }
    ~S() { puts("S::~S()"); }
};

#endif // MEMORYTRACE - End of experimental code - not running in current distribution.

//------------------------------------------------------------------------------------
// Experimental test of overloading global and class new.

#ifdef MEMORYTRACE                      // Under construction - not running in current distribution.

#define NEW new(__FILE__, __LINE__)
#define DELETE printf("delete: %s at %u\n", __FILE__, __LINE__) ; delete

extern void * operator new (size_t size, char * file, unsigned int line);
extern void * operator new [] (size_t size, char * file, unsigned int line);

#endif // MEMORYTRACE - End of experimental code - not running in current distribution.

//------------------------------------------------------------------------------------
// Experimental test of overloading global and class delete.

#ifdef MEMORYTRACE                      // Under construction - not running in current distribution.

extern void operator delete (void * ptr);
extern void operator delete [] (void * ptr);

extern void operator delete (void * ptr, size_t size);
extern void operator delete [] (void * ptr, size_t size);

#endif // MEMORYTRACE - End of experimental code - not running in current distribution.

//------------------------------------------------------------------------------------
// Class which defines accumulation of non-dynamic tracing/metering information (total calls/timing, etc).

class GlobalDynameter
{
    friend class LocalDynameter;

    // Utility sorting functions.
    friend bool NumSelfCalls(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);
    friend bool MaxSinceLastBefore(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);
    friend bool CalleeRuntimePerCall(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);
    friend bool CalleeRuntimeAggregate(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);
    friend bool SelfRuntimePerCall(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);
    friend bool SelfRuntimeAggregate(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);
    friend bool TotalRuntimePerCall(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);
    friend bool TotalRuntimeAggregate(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);

  private:
    const char * const m_srcFunctionName;            // Function name decorated with class and argument signature.
    const char * const m_srcFileName;                // Source file name.
    const unsigned int m_ordinalCount;               // This function's order of first call (relative to all others).
    unsigned long long int m_calleeRuntime;          // Accum runtime of functions called by this one (callees).
    unsigned long long int m_selfRuntime;            // Accum runtime of this function (not including callees).
    unsigned long long int m_maxSinceLastBeforeCall; // Max ticks since last metering tool access before current call.
    unsigned long long int m_selfNumberOfCalls;      // Num traced calls to this function (not necessarily printed).

    // Constructor for GlobalDynameter called by LocalDynameter constructor to store per-function results.
    GlobalDynameter(const char * const srcFunctionName,
                    const char * const srcFileName,
                    const unsigned int ordinalCount,
                    const unsigned long long int calleeRuntime,
                    const unsigned long long int selfRuntime,
                    const unsigned long long int maxSinceLastBeforeCall);

    // Default constructor - should never get called.
    GlobalDynameter();

}; // class GlobalDynameter

//------------------------------------------------------------------------------------
// Class which defines dynamic tracing/metering activity (per-call information, dynamic call chain, etc).

class LocalDynameter
{
  private:
    const unsigned long long int m_fcnEntryTime; // Clock count at initial function entry.
    const char * const m_srcFunctionName;        // Function name decorated with class and argument signature.
    const char * const m_srcFileName;            // Source file name.
#if (DYNAMETER_LEVEL >= 3u)
    bool m_tracePrintoutOK;                 // Flag indicating state of trace printout suppression.
    unsigned int m_traceLevel;              // "Local" value (per class object - ie, at given fcn level).
#endif // (DYNAMETER_LEVEL >= 3u)
    unsigned long long int m_calleeRuntime; // Accumulated runtime of functions called by this one (callees).
    GlobalDynameter * m_globalDynameterPtr; // Pointer to GlobalDynameter object in GlobalDynameterArray.
    LocalDynameter * m_parentMeterPtr;      // Pointer to my parent (or NULL).
    unsigned int m_srcLineNumber;           // Source line number where constructor called.

    // "Global" pointer to LocalDynameter object of function currently on top of run-time stack.
    static LocalDynameter * s_currentMeterPtr;

    // Accumulates max time from previous trace recording to current call, over all calls to all functions.
    static unsigned long long int s_maxSinceLastBeforeCall;

    // Total number of function calls traced so far (all functions).
    static unsigned long long int s_totalNumberOfCalls;

    static std::ofstream s_traceOut;    // File stream for tracing/metering output.

    // Array of pointers to GlobalDynameter objects holding trace data.
    static GlobalDynameter * s_GlobalDynameterArray[DYNAMETER_ARRAYSIZE];

    // Number of GlobalDynameter objects populating above array - ie, number of functions traced so far this run.
    // This is used as an ordinal count for GlobalDynameter objects (gives order of each function's first call).
    static unsigned int s_GlobalDynameterCount;

#if (DYNAMETER_LEVEL >= 3u)

    // User-settable limit to depth of tracing based on number of calls to same function.
    static unsigned int s_traceCountLimit;

    // Flag/counter to control whether (and how) ellipsis indication is printed when dynamic tracing is suppressed.
    static unsigned int s_ellipsisCounter;

    // Count of total number of elided tracing printouts.
    static unsigned int s_printoutsElided;

    // Utility function.  Prints marks indicating level of function-call nesting.
    void print_indentation(std::ofstream & stream);

    // Utility function.  Prints message indicating number of elided tracing printouts.
    void print_ellipsis(std::ofstream & stream);

#endif // (DYNAMETER_LEVEL >= 3u)

    // Utility function.  Prints summary statistics on all traced functions.
    void TablePrinter(const std::string heading, const std::string filename);

  public:
    // Set at first call and used as zero-point of elapsed time for run.
    static unsigned long long int s_startClock;

    // Set at first call and updated at end of each call, irrespective of trace level at call.
    static unsigned long long int s_lastClock;

    // Constructor/Printer.
    // Note: If macro __PRETTY_FUNCTION__ (which includes signature and class name in string) is unavailable,
    // use more generally available macro __FUNCTION__ instead (no signature or class name in string).
    LocalDynameter(const char * const srcFunctionName,     // Supplied by __PRETTY_FUNCTION__ macro
                   const char * const srcFileName,         // Supplied by __FILE__ macro
                   const unsigned int srcLineNumber,       // Supplied by __LINE__ macro
                   const unsigned int globalDynameterIdx); // Supplied by DYNACOUNTER_START + __COUNTER__

    // Destructor/Printer.
    ~LocalDynameter();

}; // class LocalDynameter

//------------------------------------------------------------------------------------
// Utility functions for sorting.
// All comparison functions yield sorts in decreasing (non-increasing) order.

// Utility function.  Sorts by number of self calls.
extern bool NumSelfCalls(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);

// Utility function.  Sorts by max time since last trace probe before current call.
extern bool MaxSinceLastBefore(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);

// Utility function.  Sorts by callee runtime per call (total over all callees).
extern bool CalleeRuntimePerCall(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);

// Utility function.  Sorts by callee runtime aggregate (summed over all calls).
extern bool CalleeRuntimeAggregate(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);

// Utility function.  Sorts by self runtime per call (excluding all callees).
extern bool SelfRuntimePerCall(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);

// Utility function.  Sorts by self runtime aggregate (excluding callees, summed over all calls).
extern bool SelfRuntimeAggregate(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);

// Utility function.  Sorts by total runtime per call (sum of self plus callee runtime).
extern bool TotalRuntimePerCall(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);

// Utility function.  Sorts by total runtime aggregate (sum of self plus callee, summed over all calls).
extern bool TotalRuntimeAggregate(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr);

//------------------------------------------------------------------------------------

#endif  // DYNAMETER_LEVEL

//------------------------------------------------------------------------------------
// External function declarations.

#ifdef DYNAMETER_LEVEL

// Timer utility function (CPU cycle counter).
// This function is used by the Dynameter constructors (in expansion of the StartDynameter() macro)
// and corresponding destructors.  It is also called in a few other places to "reset" the tracing/metering
// clock (ie, after human interaction finishes).
//
extern unsigned long long int rdtsc();

#endif

//------------------------------------------------------------------------------------
// Macros which invoke tracing/metering functionality.
//
// Note: If predefined macro __PRETTY_FUNCTION__ (which includes signature and class name in string) is unavailable,
// use predefined (and more generally available) macro __FUNCTION__ instead (no signature or class name in string).
//
// Note that they expand into other than ordinary function calls (ie, declarations) if DYNAMETER_LEVEL is defined,
// which is why inline function definitions are insufficient.  If DYNAMETER_LEVEL is NOT defined, they expand into
// whitespace (and therefore are invisible in non-tracing/metering-mode compilations).

#ifdef DYNAMETER_LEVEL

#define StartDynameter()                                         \
    LocalDynameter traceObj(__PRETTY_FUNCTION__,                 \
                            __FILE__,                            \
                            __LINE__,                            \
                            DYNACOUNTER_START + __COUNTER__)

#else // DYNAMETER_LEVEL

#define StartDynameter()

#endif // DYNAMETER_LEVEL

//------------------------------------------------------------------------------------
// Extensions to ASSERT macro with equivalent functionality but extra argument printout capability.
// These are independent of the Dynameter tool and are useful for debugging anywhere.

#ifndef NDEBUG                          // In Debug mode, these all take effect.

#define DebugAssert(condition, msg)                                     \
    if(!(condition))                                                    \
    {                                                                   \
        std::cerr << std::endl                                          \
                  << "DebugAssert: " << msg << std::endl                \
                  << "Condition:   " << #condition << std::endl         \
                  << "In function: " << __PRETTY_FUNCTION__ << std::endl \
                  << "In filename: " << __FILE__ << " (line " << __LINE__ << ")" << std::endl << std::endl; \
        assert(condition);                                              \
    }

#define DebugAssert2(condition, arg1, arg2)                             \
    if(!(condition))                                                    \
    {                                                                   \
        std::cerr << std::endl                                          \
                  << "DebugAssert2: " << #condition << std::endl        \
                  << #arg1 << " : " << (arg1) << std::endl              \
                  << #arg2 << " : " << (arg2) << std::endl              \
                  << "In function:  " << __PRETTY_FUNCTION__ << std::endl \
                  << "In filename:  " << __FILE__ << " (line " << __LINE__ << ")" << std::endl << std::endl; \
        assert(condition);                                              \
    }

#define DebugAssert3(condition, arg1, arg2, arg3)                       \
    if(!(condition))                                                    \
    {                                                                   \
        std::cerr << std::endl                                          \
                  << "DebugAssert3: " << #condition << std::endl        \
                  << #arg1 << " : " << (arg1) << std::endl              \
                  << #arg2 << " : " << (arg2) << std::endl              \
                  << #arg3 << " : " << (arg3) << std::endl              \
                  << "In function:  " << __PRETTY_FUNCTION__ << std::endl \
                  << "In filename:  " << __FILE__ << " (line " << __LINE__ << ")" << std::endl << std::endl; \
        assert(condition);                                              \
    }

#define DebugAssert4(condition, arg1, arg2, arg3, arg4)                 \
    if(!(condition))                                                    \
    {                                                                   \
        std::cerr << std::endl                                          \
                  << "DebugAssert4: " << #condition << std::endl        \
                  << #arg1 << " : " << (arg1) << std::endl              \
                  << #arg2 << " : " << (arg2) << std::endl              \
                  << #arg3 << " : " << (arg3) << std::endl              \
                  << #arg4 << " : " << (arg4) << std::endl              \
                  << "In function:  " << __PRETTY_FUNCTION__ << std::endl \
                  << "In filename:  " << __FILE__ << " (line " << __LINE__ << ")" << std::endl << std::endl; \
        assert(condition);                                              \
    }

#define DebugPrint1(arg1)                                               \
    if(true)                                                            \
    {                                                                   \
        std::cerr << std::endl                                          \
                  << #arg1 << " : " << (arg1) << std::endl              \
                  << "In function:  " << __PRETTY_FUNCTION__ << std::endl \
                  << "In filename:  " << __FILE__ << " (line " << __LINE__ << ")" << std::endl << std::endl; \
    }

#define DebugPrint2(arg1, arg2)                                         \
    if(true)                                                            \
    {                                                                   \
        std::cerr << std::endl                                          \
                  << #arg1 << " : " << (arg1) << std::endl              \
                  << #arg2 << " : " << (arg2) << std::endl              \
                  << "In function:  " << __PRETTY_FUNCTION__ << std::endl \
                  << "In filename:  " << __FILE__ << " (line " << __LINE__ << ")" << std::endl << std::endl; \
    }

#define DebugPrint3(arg1, arg2, arg3)                                   \
    if(true)                                                            \
    {                                                                   \
        std::cerr << std::endl                                          \
                  << #arg1 << " : " << (arg1) << std::endl              \
                  << #arg2 << " : " << (arg2) << std::endl              \
                  << #arg3 << " : " << (arg3) << std::endl              \
                  << "In function:  " << __PRETTY_FUNCTION__ << std::endl \
                  << "In filename:  " << __FILE__ << " (line " << __LINE__ << ")" << std::endl << std::endl; \
    }

#else  // In non-debug mode, they are all defined as NO-OPs.

#define DebugAssert(condition, msg)
#define DebugAssert2(condition, arg1, arg2)
#define DebugAssert3(condition, arg1, arg2, arg3)
#define DebugAssert4(condition, arg1, arg2, arg3, arg4)
#define DebugPrint1(arg1)
#define DebugPrint2(arg1, arg2)
#define DebugPrint3(arg1, arg2, arg3)

#endif // NDEBUG

//------------------------------------------------------------------------------------

#endif // DYNATRACER_H

//____________________________________________________________________________________
