#ifndef __genericprofiler_ah__ #define __genericprofiler_ah__ #include #include #include using namespace std; // GenericProfiler - generic aspect to gather profiling information // (Name and signature of each executed function, how often the function // was executed and how much time was spent executing it.) // // The following definitions need to be specialized: // // pointcut virtual measureFct () = ... ; // Define a match expression for the functions to be profiled // // virtual void (*summary())() { ... } // This function should return a pointer to a static function that // will be called when exiting the program. // If this function is not defined in a derived aspect, a simple // textual summary will be printed. // Refer to the function simple_stat() to learn how to access the // profiling data. // // This aspect uses std::chrono to measure the execution time in nano seconds. namespace Profiler { // Data structure used to gather the profiling information // New Data objects link themselves to existing ones at creation // time. struct Data { static Data *&first () { static Data *data = 0; return data; } struct Data *_next; unsigned _calls; std::chrono::nanoseconds _time; const char *_name; Data (const char* n) : _calls(0), _time(0L), _name (n) { Data *head = first (); first () = this; _next = head; } }; } aspect GenericProfiler { // Do not measure the Profiler's own methods pointcut dontMeasure () = "% Profiler::...::%(...)" || "% GenericProfiler::...::%(...)"; // Pure virtual pointcut: to be specified in a derived aspect pointcut virtual measureFct () = 0; // Execution advice for the functions that are to be measured. // For each JoinPoint the advice code will be transformed into // a new template function instantiation. Therefore a new Data object // will be created whenever a function is executed the first time. // Further executions of the same function will reuse this object, // enabling it to count the number of invocations and the overall // time consumed. advice execution(measureFct() && !dontMeasure()) : around() { static Profiler::Data data (JoinPoint::signature ()); const auto start = std::chrono::high_resolution_clock::now(); tjp->proceed(); const auto end = std::chrono::high_resolution_clock::now(); data._calls++; data._time += end - start; }; // Print a simple summary static void simple_stat () { cout << "Statistics:" << endl; for (Profiler::Data *curr = Profiler::Data::first (); curr; curr = curr->_next) { cout << "---" << endl; cout << "function: " << curr->_name << endl; cout << "no of execs: " << curr->_calls << endl; cout << "total time: " << curr->_time.count() << endl; } } // Return the function that should be used to print the profiling summary // Specialize this function if you prefer a different kind of output. virtual void (*summary())() { return &simple_stat; } // This advice is used to register the function that prints the summary // to be executed when the program exits. advice execution ("% main(...)") : after () { atexit (summary()); } }; #endif