Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:01:40

0001 //
0002 // Created by Nathan Brei on 10/25/21.
0003 //
0004 
0005 #pragma once
0006 
0007 #include <vector>
0008 #include <string>
0009 #include <cassert>
0010 #include <sys/time.h>
0011 #include <chrono>
0012 
0013 
0014 // Note on tracking how/where Insert() objects came from.
0015 //
0016 // The JEvent class has a JCallGraphRecorder member object. This object has a member called
0017 // m_insert_dataorigin_type that is used to indicate how new inserts should be classified. This
0018 // value is copied into the m_insert_origin member of the JFactory during the call to JEvent::Insert. This
0019 // is set to ORIGIN_FROM_SOURCE in JEventSource::DoNext just before GetEvent is called and reset after.
0020 // This should capture all Insert calls made by the source.
0021 //
0022 // The default value for m_insert_dataorigin_type is ORIGIN_FROM_FACTORY which will usually be
0023 // correct. Note that the factory's m_insert_origin member is initialized to ORIGIN_NOT_AVAILABLE,
0024 // but this will be overwritten iff the factory has objects inserted by an Insert() call.
0025 //
0026 // WARNING: The above described mechanism could fail if a more complex subevent task scheme is implemented
0027 // that results in multiple threads inserting into the same JEvent at the same time. It would actually
0028 // need to be one thread inserting from a factory while another inserts from a source. Multiple source
0029 // scenarios and multiple factory scenarios would be OK, but a mixture could result in incorrect
0030 // values for the origin type being recorded.
0031 
0032 class JCallGraphRecorder {
0033 public:
0034     enum JDataSource {
0035         DATA_NOT_AVAILABLE = 1,
0036         DATA_FROM_CACHE,
0037         DATA_FROM_SOURCE,
0038         DATA_FROM_FACTORY
0039     };
0040 
0041     enum JDataOrigin {
0042         ORIGIN_NOT_AVAILABLE = 1,
0043         ORIGIN_FROM_FACTORY,
0044         ORIGIN_FROM_SOURCE
0045     };
0046 
0047     struct JCallGraphNode {
0048         std::string caller_name;
0049         std::string caller_tag;
0050         std::string callee_name;
0051         std::string callee_tag;
0052         std::chrono::steady_clock::time_point start_time;
0053         std::chrono::steady_clock::time_point end_time;
0054         JDataSource data_source = DATA_NOT_AVAILABLE;
0055     JCallGraphNode() {}
0056     JCallGraphNode(std::string caller_name, std::string caller_tag, std::string callee_name, std::string callee_tag) 
0057     : caller_name(caller_name), caller_tag(caller_tag), callee_name(callee_name), callee_tag(callee_tag) {}
0058     };
0059 
0060     struct JCallStackFrame {
0061         std::string factory_name;
0062         std::string factory_tag;
0063         std::chrono::steady_clock::time_point start_time;
0064     };
0065 
0066     struct JErrorCallStack {
0067         std::string factory_name;
0068         std::string tag;
0069         const char* filename;
0070         int line = 0;
0071     };
0072 
0073 private:
0074     bool m_enabled = false;
0075     std::vector<JCallStackFrame> m_call_stack;
0076     std::vector<JErrorCallStack> m_error_call_stack;
0077     std::vector<JCallGraphNode> m_call_graph;
0078     JDataOrigin m_insert_dataorigin_type = ORIGIN_FROM_FACTORY; // See note at top of file
0079 
0080 public:
0081     inline bool IsEnabled() const { return m_enabled; }
0082     inline void SetEnabled(bool recordingEnabled=true){ m_enabled = recordingEnabled; }
0083     inline void StartFactoryCall(const std::string& callee_name, const std::string& callee_tag);
0084     inline JDataOrigin SetInsertDataOrigin(JDataOrigin origin){ auto previous = m_insert_dataorigin_type; m_insert_dataorigin_type = origin; return previous; }
0085     inline JDataOrigin GetInsertDataOrigin(){ return m_insert_dataorigin_type; }
0086     inline void FinishFactoryCall(JDataSource data_source=JDataSource::DATA_FROM_FACTORY);
0087     inline std::vector<JCallGraphNode> GetCallGraph() {return m_call_graph;} ///< Get the current factory call stack
0088     inline void AddToCallGraph(const JCallGraphNode &cs) {if(m_enabled) m_call_graph.push_back(cs);} ///< Add specified item to call stack record but only if record_call_stack is true
0089     inline void AddToErrorCallStack(const JErrorCallStack &cs) {if (m_enabled) m_error_call_stack.push_back(cs);} ///< Add layer to the factory call stack
0090     inline std::vector<JErrorCallStack> GetErrorCallStack(){return m_error_call_stack;} ///< Get the current factory error call stack
0091     void PrintErrorCallStack() const; ///< Print the current factory call stack
0092     void Reset();
0093     std::vector<std::pair<std::string, std::string>> TopologicalSort() const;
0094 };
0095 
0096 
0097 
0098 void JCallGraphRecorder::StartFactoryCall(const std::string& callee_name, const std::string& callee_tag) {
0099 
0100     /// This is used to fill initial info into a call_stack_t stucture
0101     /// for recording the call stack. It should be matched with a call
0102     /// to CallStackEnd. It is normally called from the Get() method
0103     /// above, but may also be used by external actors to manipulate
0104     /// the call stack (presumably for good and not evil).
0105 
0106     if (!m_enabled) return;
0107     JCallStackFrame frame;
0108     frame.factory_name = callee_name;
0109     frame.factory_tag = callee_tag;
0110     frame.start_time = std::chrono::steady_clock::now();
0111     m_call_stack.push_back(frame);
0112 }
0113 
0114 
0115 void JCallGraphRecorder::FinishFactoryCall(JCallGraphRecorder::JDataSource data_source) {
0116 
0117     /// Complete a call stack entry. This should be matched
0118     /// with a previous call to CallStackStart which was
0119     /// used to fill the cs structure.
0120 
0121     if (!m_enabled) return;
0122     assert(!m_call_stack.empty());
0123 
0124     JCallStackFrame& callee_frame = m_call_stack.back();
0125 
0126     JCallGraphNode node;
0127     node.callee_name = callee_frame.factory_name;
0128     node.callee_tag = callee_frame.factory_tag;
0129     node.start_time = callee_frame.start_time;
0130     node.end_time = std::chrono::steady_clock::now();
0131     node.data_source = data_source;
0132 
0133     m_call_stack.pop_back();
0134 
0135     if (!m_call_stack.empty()) {
0136         JCallStackFrame& caller_frame = m_call_stack.back();
0137         node.caller_name = caller_frame.factory_name;
0138         node.caller_tag = caller_frame.factory_tag;
0139         m_call_graph.push_back(node);
0140     }
0141 }
0142 
0143