Zakero's C++ Header Libraries
A collection of reusable C++ libraries
Zakero_Profiler.h
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright 2010-2019 Andrew Moore
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7  */
8 
9 #ifndef zakero_Profiler_h
10 #define zakero_Profiler_h
11 
244 /******************************************************************************
245  * Includes
246  */
247 
248 #include <ctime>
249 #include <fstream>
250 #include <iostream>
251 #include <map>
252 #include <mutex>
253 #include <thread>
254 
255 // C++20
256 #include <experimental/source_location>
257 
258 // Zakero
259 #include "Zakero_Base.h"
260 
261 
262 /******************************************************************************
263  * Macros
264  */
265 
266 // {{{ Macros
267 
268 #ifdef ZAKERO_PROFILER_ENABLE
269 
301 #define ZAKERO_PROFILER_INIT(output_) \
302  zakero::Profiler::init(output_, {});
303 
345 #define ZAKERO_PROFILER_INIT_METADATA(output_, meta_data_) \
346  zakero::Profiler::init(output_, meta_data_);
347 
393 #define ZAKERO_PROFILER_ACTIVATE \
394  zakero::Profiler::activate();
395 
403 #define ZAKERO_PROFILER_DEACTIVATE \
404  zakero::Profiler::deactivate();
405 
420 #define ZAKERO_PROFILER_UNIQUE(name_) ZAKERO_CONCAT(name_, __LINE__)
421 
461 #define ZAKERO_PROFILER_DURATION(category_, name_) \
462  zakero::Profiler::Duration ZAKERO_PROFILER_UNIQUE(zakero_profiler_duration_) \
463  ( category_ \
464  , name_ \
465  , std::experimental::source_location::current() \
466  ); \
467 
468 
480 #define ZAKERO_PROFILER_INSTANT(category_, name_) \
481  { \
482  zakero::Profiler::Instant ZAKERO_PROFILER_UNIQUE(zakero_profiler_instant_) \
483  ( category_ \
484  , name_ \
485  , std::experimental::source_location::current() \
486  ); \
487  } \
488 
489 #else
490 
491 #define ZAKERO_PROFILER_INIT(output_)
492 #define ZAKERO_PROFILER_INIT_METADATA(output_, meta_data_)
493 #define ZAKERO_PROFILER_ACTIVATE
494 #define ZAKERO_PROFILER_DEACTIVATE
495 #define ZAKERO_PROFILER_DURATION(category_, name_)
496 #define ZAKERO_PROFILER_INSTANT(category_, name_)
497 
498 #endif
499 
500 // }}}
501 
502 namespace zakero
503 {
504  // {{{ Declaration
505 
506  class Profiler
507  {
508  public:
509  using MetaData = std::map<std::string, std::string>;
510 
511  struct Data
512  {
513  std::string category;
514  std::string name;
515  std::experimental::source_location location;
516  std::chrono::microseconds::rep time_stamp;
517  std::thread::id thread_id;
518  pid_t process_id;
519  char phase;
520 
521  Data(const char, const std::string&, const std::string&, const std::experimental::source_location&) noexcept;
522  };
523 
524  struct Duration
525  : public zakero::Profiler::Data
526  {
527  bool was_active;
528 
529  Duration(const std::string&, const std::string&, const std::experimental::source_location&) noexcept;
530  ~Duration() noexcept;
531  };
532 
533  struct Instant
534  : public zakero::Profiler::Data
535  {
536  Instant(const std::string&, const std::string&, const std::experimental::source_location&) noexcept;
537  };
538 
539  Profiler() noexcept;
540  ~Profiler() noexcept;
541 
542  static void init(std::ostream&, zakero::Profiler::MetaData) noexcept;
543  static void init(std::string, zakero::Profiler::MetaData) noexcept;
544 
545  static void activate() noexcept;
546  static void deactivate() noexcept;
547 
548  static void report(const zakero::Profiler::Data&) noexcept;
549 
550  private:
551  std::mutex mutex;
552  std::ostream* stream;
553  std::ofstream file_output;
554  bool is_active;
555  };
556 
557  // }}}
558 }
559 
560 // {{{ Implementation
561 
562 #ifdef ZAKERO_PROFILER_IMPLEMENTATION
563 
564 // {{{ Macros
565 // {{{ Macros : Doxygen
566 
567 #ifdef ZAKERO__DOXYGEN_DEFINE_DOCS
568 
569 // Only used for generating Doxygen documentation
570 
582 #define ZAKERO_PROFILER_IMPLEMENTATION
583 
594 #define ZAKERO_PROFILER_ENABLE
595 
596 #endif // ZAKERO__DOXYGEN_DEFINE_DOCS
597 
598 // }}}
599 
609 #ifdef __linux__
610 #include <sys/types.h>
611 #include <unistd.h>
612 #define ZAKERO_PROFILER__PID getpid()
613 #else
614 #define ZAKERO_PROFILER__PID -1
615 #endif
616 
617 // }}}
618 
619 namespace zakero
620 {
621 // {{{ Anonymous Namespace
622 
623 namespace
624 {
625  zakero::Profiler zakero_profiler;
626 
627  // std::format performance testing
628  //uint64_t count = 0;
629  //uint64_t total = 0;
630 }
631 
632 // }}}
633 // {{{ Documentation
634 
661 // }}}
662 
669 Profiler::Profiler() noexcept
670  : mutex()
671  , stream(nullptr)
672  , file_output()
673  , is_active(false)
674 {
675 }
676 
677 
683 Profiler::~Profiler() noexcept
684 {
685  std::lock_guard<std::mutex> lock(zakero_profiler.mutex);
686 
687  if(stream != nullptr)
688  {
689  (*stream) << "]}" << std::endl;
690 
691  if(file_output.is_open())
692  {
693  file_output.close();
694  }
695  }
696 
697  // std::format performance testing
698  //std::cout << "Avg: " << (total / count) << "\n";
699 }
700 
701 
713 void zakero::Profiler::init(std::string file_name
714  , zakero::Profiler::MetaData meta_data
715  ) noexcept
716 {
717  if(zakero_profiler.stream != nullptr)
718  {
719  return;
720  }
721 
722  zakero_profiler.file_output.open(file_name);
723 
724  init(zakero_profiler.file_output, meta_data);
725 }
726 
727 
738 void zakero::Profiler::init(std::ostream& output_stream
739  , zakero::Profiler::MetaData meta_data
740  ) noexcept
741 {
742  std::lock_guard<std::mutex> lock(zakero_profiler.mutex);
743 
744  if(zakero_profiler.stream != nullptr)
745  {
746  return;
747  }
748 
749  zakero_profiler.stream = &output_stream;
750  activate();
751 
752  if(meta_data.contains("date") == false)
753  {
754  std::time_t time = std::chrono::system_clock::to_time_t(
755  std::chrono::system_clock::now()
756  );
757 
758  char buf[128];
759  std::strftime(buf, sizeof(buf), "%F %T", std::localtime(&time));
760 
761  meta_data["date"] = buf;
762  }
763 
764  if(meta_data.contains("traceEvents"))
765  {
766  meta_data.erase("traceEvents");
767  }
768 
769  meta_data["displayTimeUnit"] = "ms";
770 
771  (*zakero_profiler.stream) << "{";
772 
773  for(const auto& iter : meta_data)
774  {
775  (*zakero_profiler.stream) << "\"" << iter.first << "\":\"" << iter.second << "\",";
776  }
777 
778  (*zakero_profiler.stream) << "\"traceEvents\":[{}\n";
779 }
780 
781 
788 void zakero::Profiler::activate() noexcept
789 {
790  zakero_profiler.is_active = true;
791 }
792 
793 
803 void zakero::Profiler::deactivate() noexcept
804 {
805  zakero_profiler.is_active = false;
806 }
807 
808 
814 void Profiler::report(const zakero::Profiler::Data& data
815  ) noexcept
816 {
817  std::lock_guard<std::mutex> lock(zakero_profiler.mutex);
818 
819  // std::format performance testing
820  //const auto t1 = std::chrono::steady_clock::now();
821 
822  (*zakero_profiler.stream)
823  << ",{\"ph\":\"" << data.phase << "\""
824  << ",\"ts\":" << data.time_stamp
825  << ",\"pid\":" << data.process_id
826  << ",\"tid\":" << data.thread_id
827  << ",\"cat\":\"" << data.category << "\""
828  << ",\"name\":\"" << data.name << "\""
829  << ",\"args\":"
830  << "{\"file_name\":\"" << data.location.file_name() << "\""
831  << ",\"function_name\":\"" << data.location.function_name() << "\""
832  << "}"
833  << "}\n"
834  ;
835  /* C++20 std::format ready
836  (*zakero_profiler.stream) << std::format(
837  ",{{\"ph\":\"{}\""
838  ",\"ts\":{}"
839  ",\"cat\":\"{}\""
840  ",\"name\":\"{}\""
841  ",\"pid\":{}"
842  ",\"tid\":{}"
843  ",\"args\": {{\"file_name\":\"{}\",\"function_name\":\"{}\"}}"
844  "}}\n"
845  , data.phase
846  , nano
847  , data.category
848  , data.name
849  , data.process_id
850  , data.thread_id
851  , data.location.file_name()
852  , data.location.function_name()
853  );
854 
855  // std::format performance testing
856  const auto t2 = std::chrono::steady_clock::now();
857  const auto delta = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
858  std::cout << (uint64_t)delta << "\n";
859  total += delta;
860  count++;
861  */
862 }
863 
864 
880 Profiler::Data::Data(const char phase
881  , const std::string& category
882  , const std::string& name
883  , const std::experimental::source_location& location
884  ) noexcept
885  : category(category)
886  , name(name)
887  , location(location)
888  , time_stamp(ZAKERO_STEADY_TIME_NOW(microseconds))
889  , thread_id(std::this_thread::get_id())
890  , process_id(ZAKERO_PROFILER__PID)
891  , phase(phase)
892 {
893 }
894 
895 
914 Profiler::Duration::Duration(const std::string& category
915  , const std::string& name
916  , const std::experimental::source_location& location
917  ) noexcept
918  : zakero::Profiler::Data('B', category, name, location)
919  , was_active(zakero_profiler.is_active)
920 {
921 }
922 
923 
929 Profiler::Duration::~Duration() noexcept
930 {
931  if(was_active || zakero_profiler.is_active)
932  {
933  zakero::Profiler::report(*this);
934 
935  phase = 'E';
936  time_stamp = ZAKERO_STEADY_TIME_NOW(microseconds);
937  zakero::Profiler::report(*this);
938  }
939 }
940 
941 
960 Profiler::Instant::Instant(const std::string& category
961  , const std::string& name
962  , const std::experimental::source_location& location
963  ) noexcept
964  : zakero::Profiler::Data('I', category, name, location)
965 {
966  if(zakero_profiler.is_active)
967  {
968  zakero::Profiler::report(*this);
969  }
970 }
971 
972 } // zakero
973 
974 #endif // ZAKERO_PROFILER_IMPLEMENTATION
975 
976 // }}}
977 
978 #endif // zakero_Profiler_h
Zakero_Base.h
Zakero Base.
ZAKERO_STEADY_TIME_NOW
#define ZAKERO_STEADY_TIME_NOW(unit_)
Get the current time.
Definition: Zakero_Base.h:121