The Sparta Modeling Framework
Loading...
Searching...
No Matches
Collectable.hpp
Go to the documentation of this file.
1// <Collectable.hpp> -*- C++ -*-
2
10#pragma once
11
12#include <algorithm>
13#include <sstream>
14#include <functional>
15#include <type_traits>
16#include <iomanip>
17
19#include "sparta/pipeViewer/transaction_structures.hpp"
23#include "sparta/pairs/SpartaKeyPairs.hpp"
24#include "sparta/utils/Utils.hpp"
26
27#include "boost/numeric/conversion/converter.hpp"
28
29namespace sparta{
30 namespace collection
31 {
32
48 template<typename DataT, SchedulingPhase collection_phase = SchedulingPhase::Collection, typename = void>
50 {
51 public:
52 static constexpr uint64_t BAD_DISPLAY_ID = 0x1000;
53
62 const std::string& name,
63 const std::string& group,
64 uint32_t index,
65 uint64_t parentid = 0,
66 const std::string & desc = "Collectable <manual, no desc>") :
67 CollectableTreeNode(sparta::notNull(parent), name, group, index, desc),
68 event_set_(this),
69 ev_close_record_(&event_set_, name + "_pipeline_collectable_close_event",
71 {
72 sparta_assert(getNodeUID() < ~(decltype(transaction_t::location_ID))0,
73 "Tree has at least " << getNodeUID() << " in it. Because pipeouts "
74 "use these locations and only have 32b location IDs, the pipeline "
75 "collection system imposes a limit of " << ~(decltype(transaction_t::location_ID))0
76 << " nodes in the tree. This is expected to be an unattainable number "
77 "without some kind of runaway allocation bug. If you are sure you "
78 << " nodes in the tree. This is expected to be an unattainable number "
79 "without some kind of runaway allocation bug. If you are sure you "
80 "need more nodes than this, contact the SPARTA team. In the meantime, "
81 "it is probably safe to comment out this assertion if you do not "
82 "plan to use the pipeline collection functionality");
83
84 argos_record_.location_ID = boost::numeric::converter<decltype(argos_record_.location_ID),
85 decltype(getNodeUID())>::convert(getNodeUID());
86 argos_record_.control_Process_ID = 0; // what is this for?
87 argos_record_.parent_ID = parentid;
88 argos_record_.flags = is_Annotation;
89 argos_record_.time_Start = 0;
90 argos_record_.time_End = 1;
91
92
93 }
94
104 const std::string& name,
105 const DataT * collected_object,
106 uint64_t parentid = 0,
107 const std::string & desc = "Collectable <no desc>") :
108 Collectable(parent, name,
111 parentid,
112 desc)
113 {
114 collected_object_ = collected_object;
115
116 // Get an initial value, if available
117 if(collected_object) {
118 std::ostringstream ss;
119 ss << *collected_object;
120 prev_annot_ = ss.str();
121 }
122 }
123
132 const std::string& name,
133 uint64_t parentid = 0,
134 const std::string & desc = "Collectable <manual, no desc>") :
135 Collectable(parent, name, nullptr, parentid, desc)
136 {
137 // Can't auto collect without setting collected_object_
139 }
140
142 virtual ~Collectable() {}
143
148 void initialize(const DataT & val) {
149 std::ostringstream ss;
150 ss << val;
151 prev_annot_ = ss.str();
152 }
153
156 void collect(const DataT & val)
157 {
159 {
160 std::ostringstream ss;
161 ss << val;
162 if((ss.str() != prev_annot_) && !record_closed_)
163 {
164 // Close the old record (if there is one)
165 closeRecord();
166 }
167
168 // Remember the new string for a new record and start
169 // a new record if not empty.
170 prev_annot_ = ss.str();
171 if(!prev_annot_.empty() && record_closed_) {
172 startNewRecord_();
173 record_closed_ = false;
174 }
175 }
176 }
177
188 void collectWithDuration(const DataT & val, sparta::Clock::Cycle duration)
189 {
191 {
192 if(duration != 0) {
193 ev_close_record_.preparePayload(false)->schedule(duration);
194 }
195 collect(val);
196 }
197 }
198
202 void collect() override final {
203 // If pointer has become nullified, close the record
204 if(nullptr == collected_object_) {
205 closeRecord();
206 return;
207 }
208 collect(*collected_object_);
209 }
210
216 void collectWithDuration(sparta::Clock::Cycle duration) {
217 // If pointer has become nullified, close the record
218 if(nullptr == collected_object_) {
219 closeRecord();
220 return;
221 }
222 collectWithDuration(*collected_object_, duration);
223 }
224
227 void restartRecord() override final {
228 // Do not get a new ID when we're doing a heartbeat
229 if(!record_closed_) {
230 argos_record_.flags |= CONTINUE_FLAG;
231 writeRecord_();
232 argos_record_.flags &= ~CONTINUE_FLAG;
233 startNewRecord_();
234 }
235 }
236
239 void closeRecord(const bool & simulation_ending = false) override final
240 {
242 {
243 if(!record_closed_ && writeRecord_(simulation_ending)) {
244 prev_annot_.clear();
245 }
246 record_closed_ = true;
247 }
248 }
249
253 auto_collect_ = false;
254 }
255
256 protected:
257
261 return event_set_;
262 }
263
264 private:
265
267 bool writeRecord_(bool simulation_ending = false)
268 {
269 sparta_assert(pipeline_col_,
270 "Must startCollecting_ on this Collectable "
271 << getLocation() << " before a record can be written");
272
273 // This record hasn't changed, don't write it
274 if(SPARTA_EXPECT_FALSE(argos_record_.time_Start == pipeline_col_->getScheduler()->getCurrentTick())) {
275 return false;
276 }
277
278 // Set a new transaction ID since we're starting anew
279 argos_record_.transaction_ID = pipeline_col_->getUniqueTransactionId();
280
281 // Notice that we make the length +1 in order to null
282 // terminate the data.
283 argos_record_.length = static_cast<uint16_t>(prev_annot_.size());
284
285 // We should just change the transaction structure to take
286 // an std::string instead of char* in the future.
287 argos_record_.annt = prev_annot_;
288
289 // Capture the end time
290 argos_record_.time_End =
291 pipeline_col_->getScheduler()->getCurrentTick() + (simulation_ending ? 1 : 0);;
292
293 // If this assert fires, then a user is trying to collect
294 // a record multiple times in their cycle window or
295 // something else really bad has happened.
296 sparta_assert(argos_record_.time_Start < argos_record_.time_End);
297
298 // Write the old record to the file
299 pipeline_col_->writeRecord(argos_record_);
300
301 return true;
302 }
303
305 void startNewRecord_() {
306 // Set up the new start time for the new record
307 argos_record_.time_Start = pipeline_col_->getScheduler()->getCurrentTick();
308 }
309
312 void setCollecting_(bool collect, Collector * collector) override final
313 {
314 pipeline_col_ = dynamic_cast<PipelineCollector *>(collector);
315 sparta_assert(pipeline_col_ != nullptr,
316 "Collectables can only added to PipelineCollectors... for now");
317
318 if(collect && !prev_annot_.empty()) {
319 // Set the start time for this transaction to be
320 // moment collection is enabled.
321 startNewRecord_();
322 }
323
324 // If the collected object is null, this Collectable
325 // object is to be explicitly collected
326 if(collected_object_ && auto_collect_) {
327 if(collect) {
328 // Add this Collectable to the PipelineCollector's
329 // list of objects requiring collection
330 pipeline_col_->addToAutoCollection(this, collection_phase);
331 }
332 else {
333 // Remove this Collectable from the
334 // PipelineCollector's list of objects requiring
335 // collection
336 pipeline_col_->removeFromAutoCollection(this);
337 }
338 }
339
340 if(!collect && !record_closed_) {
341 // Force the record to be written
342 closeRecord();
343 }
344 }
345
346 // The annotation object to be collected
347 const DataT * collected_object_ = nullptr;
348
349 // The live transaction record
350 annotation_t argos_record_;
351
352 // Store the result of the previous annotation, the
353 // annotation_t struct holds a pointer to this
354 std::string prev_annot_;
355
356 // Ze Collec-tor
357 PipelineCollector * pipeline_col_ = nullptr;
358
359 // For those folks that want a value to automatically
360 // disappear in the future
361 sparta::EventSet event_set_;
363
364 // Is this collectable currently closed?
365 bool record_closed_ = true;
366
367 // Should we auto-collect?
368 bool auto_collect_ = true;
369 };
370
379 public:
380
381 // We need to make Collectable a friend of this class, as it will directly call
382 // the static private function from within itself.
383 template<typename DataT, SchedulingPhase collection_phase, typename>
384 friend class Collectable;
385 private:
386
387 // Any point this funtion is invoked, it releases a new 64bit integer which is unique.
388 // So, we need to limit the access of this function.
389 static uint64_t getUniquePairID_() {
390 static uint64_t id = 0;
391 return ++id;
392 }
393 };
394
439 template<typename DataT, SchedulingPhase collection_phase>
440 class Collectable<DataT, collection_phase,
441 MetaStruct::enable_if_t<
442 std::is_base_of<sparta::PairDefinition<MetaStruct::remove_any_pointer_t<DataT>>,
443 typename MetaStruct::remove_any_pointer_t<DataT>::SpartaPairDefinitionType>::value>> :
444 public sparta::PairCollector<typename MetaStruct::remove_any_pointer_t<DataT>::SpartaPairDefinitionType>, public CollectableTreeNode
445 {
446 // Aliasing the actual Datatype of the collectable being collected as Data_t
448
449 // Aliasing the Datatype of the Pair Definition of the collectable being collected as PairDef_t
450 typedef typename Data_t::SpartaPairDefinitionType PairDef_t;
451
452 // Making a bunch of APIs of PairCollector class being available to us with the using directive
453 using PairCollector<PairDef_t>::getNameStrings;
454 using PairCollector<PairDef_t>::getDataVector;
455 using PairCollector<PairDef_t>::getStringVector;
456 using PairCollector<PairDef_t>::getFormatVector;
457 using PairCollector<PairDef_t>::getSizeOfVector;
458 using PairCollector<PairDef_t>::getPEventLogVector;
459 using PairCollector<PairDef_t>::getArgosFormatGuide;
460 using PairCollector<PairDef_t>::collect_;
461 using PairCollector<PairDef_t>::isCollecting;
462
463 public:
464
473 const std::string& name,
474 const std::string& group,
475 uint32_t index,
476 uint64_t parentid = 0,
477 const std::string & desc = "Collectable <manual, no desc>") :
478 sparta::PairCollector<PairDef_t>(),
479 CollectableTreeNode(sparta::notNull(parent), name, group, index, desc),
480 argos_record_(pair_t(0, 1, parentid, 0, BAD_DISPLAY_ID,
481 boost::numeric::converter<decltype(argos_record_.location_ID),
482 decltype(getNodeUID())>::convert(getNodeUID()),
483 is_Pair, 0)),
484 event_set_(this),
485 ev_close_record_(&event_set_, name + "_pipeline_collectable_close_event",
487 static constexpr auto MAX_UID = std::numeric_limits<decltype(argos_record_.location_ID)>::max();
488 sparta_assert(getNodeUID() < MAX_UID,
489 "Tree has at least " << getNodeUID() << " in it. Because pipeouts "
490 "use these locations and only have 32b location IDs, the pipeline "
491 "collection system imposes a limit of " << MAX_UID
492 << " nodes in the tree. This is expected to be an unattainable number "
493 "without some kind of runaway allocation bug. If you are sure you "
494 "need more nodes than this, contact the SPARTA team. In the meantime, "
495 "it is probably safe to comment out this assertion if you do not "
496 "plan to use the pipeline collection functionality");
497 }
498
509 const std::string& name,
510 const DataT * collected_object,
511 uint64_t parentid = 0,
512 const std::string & desc = "Collectable <no desc>") :
513 Collectable(parent, name,
516 parentid,
517 desc)
518 {
519 collected_object_ = collected_object;
520
521 // Get an initial value, if available
522 if(collected_object) {
523 initialize(*collected_object);
524 }
525 }
526
535 const std::string& name,
536 uint64_t parentid = 0,
537 const std::string & desc = "Collectable <manual, no desc>") :
538 Collectable(parent, name, nullptr, parentid, desc)
539 {
540 // Can't auto collect without setting collected_object_
542 }
543
545 virtual ~Collectable() {}
546
551 template<typename T>
552 MetaStruct::enable_if_t<!MetaStruct::is_any_pointer<T>::value, void>
553 initialize(const T & val) {
555 collect(val);
556 }
557 }
558
563 template<typename T>
564 MetaStruct::enable_if_t<MetaStruct::is_any_pointer<T>::value, void>
565 initialize(const T & val){
567 collect(*val);
568 }
569 }
570
574 template<typename T>
575 MetaStruct::enable_if_t<!MetaStruct::is_any_pointer<T>::value, void>
576 collect(const T & val)
577 {
578 collect_(val);
579
580 // if the value pairs are not the same as the
581 // previous value pairs and the previous record is still open
582 if(!isSameRecord() && !record_closed_)
583 {
584 //Close the old record (if there is one)
585 closeRecord();
586 }
587
588 // Remember the new value pairs for a new record and start
589 // a new record if not empty.
590 updateLastRecord_();
591
592 // If the new Value pairs are not empty and current record is closed, we start a new record.
593 if(record_closed_ && hasData_()){
594 startNewRecord_();
595 record_closed_ = false;
596 }
597 }
598
602 template <typename T>
603 MetaStruct::enable_if_t<MetaStruct::is_any_pointer<T>::value, void>
604 collect(const T & val){
605 // If pointer has become nullified, close the record
606 if(nullptr == val) {
607 closeRecord();
608 return;
609 }
610 collect(*val);
611 }
612
623 template<typename T>
624 MetaStruct::enable_if_t<!MetaStruct::is_any_pointer<T>::value, void>
625 collectWithDuration(const T & val, sparta::Clock::Cycle duration){
627 {
628 if(duration != 0) {
629 ev_close_record_.preparePayload(false)->schedule(duration);
630 }
631 collect(val);
632 }
633 }
634
645 template<typename T>
646 MetaStruct::enable_if_t<MetaStruct::is_any_pointer<T>::value, void>
647 collectWithDuration(const T & val, sparta::Clock::Cycle duration){
648 // If pointer has become nullified, close the record
649 if(nullptr == val) {
650 closeRecord();
651 return;
652 }
653 collectWithDuration(*val, duration);
654 }
655
659 void collect() override final {
660 // If pointer has become nullified, close the record
661 if(nullptr == collected_object_) {
662 closeRecord();
663 return;
664 }
665 collect(*collected_object_);
666 }
667
673 void collectWithDuration(sparta::Clock::Cycle duration) {
674 // If pointer has become nullified, close the record
675 if(nullptr == collected_object_) {
676 closeRecord();
677 return;
678 }
679 collectWithDuration(*collected_object_, duration);
680 }
681
684 void restartRecord() override final {
685
686 // Do not get a new ID when we're doing a heartbeat
687 if(!record_closed_) {
688 argos_record_.flags |= CONTINUE_FLAG;
689 writeRecord_();
690 argos_record_.flags &= ~CONTINUE_FLAG;
691 startNewRecord_();
692 }
693 }
694
697 void closeRecord(const bool & simulation_ending = false) override final
698 {
700 {
701 if(!record_closed_ && writeRecord_(simulation_ending)) {
702
703 // Clear the previous vector containing the Name Value pairs.
704 argos_record_.valueVector.clear();
705 argos_record_.stringVector.clear();
706 }
707 record_closed_ = true;
708 }
709 }
710
714 auto_collect_ = false;
715 }
716
718 // if any of the old Values have changed or not.
719 bool isSameRecord() const {
720 return (argos_record_.valueVector == getDataVector()) && (argos_record_.stringVector == getStringVector());
721 }
722
725 const std::string & dumpNameValuePairs(const DataT & val) {
726 collect_(val);
727 log_string_.clear();
728 std::ostringstream ss;
729 for(const auto & pairs : getPEventLogVector()){
730 ss << pairs.first << "(" << pairs.second << ") ";
731 }
732 log_string_ = ss.str();
733 return log_string_;
734 }
735
736 protected:
737
741 return event_set_;
742 }
743
745 // and this is a pure virtual function in base
747 // else Collectable will also become Abstract.
748 virtual void generateCollectionString_() override {}
749
750 private:
751
761 inline static uint64_t getUniquePairID_() {
762 static uint64_t id = 0;
763 if(id){ return id; }
764 id = sparta::collection::UniquePairIDGenerator::getUniquePairID_();
765 return id;
766 }
767
769 // we collected from the last collection
771 // this cycle of Collection.
772 void updateLastRecord_(){
773 // These values will change every time the transaction changes
774 argos_record_.valueVector = getDataVector();
775 argos_record_.stringVector = getStringVector();
776 argos_record_.sizeOfVector = getSizeOfVector();
777
778 if (has_display_id_field_) {
779 argos_record_.display_ID = argos_record_.valueVector[0].first & 0x0fff;
780 }
781 }
782
784 bool hasData_() const {
785 return !(argos_record_.valueVector.empty() && argos_record_.stringVector.empty());
786 }
787
789 // for writing to the Transaction Database File.
790 bool writeRecord_(bool simulation_ending = false)
791 {
792 sparta_assert(pipeline_col_,
793 "Must startCollecting_ on this Collectable "
794 << getLocation() << " before a record can be written");
795
796 // This record hasn't changed, don't write it
797 if(SPARTA_EXPECT_FALSE(argos_record_.time_Start == pipeline_col_->getScheduler()->getCurrentTick())) {
798 return false;
799 }
800
801 // Set a new transaction ID since we're starting anew
802 argos_record_.transaction_ID = pipeline_col_->getUniqueTransactionId();
803
804 argos_record_.pairId = getUniquePairID_();
805
806 // Capture the end time
807 argos_record_.time_End =
808 pipeline_col_->getScheduler()->getCurrentTick() + (simulation_ending ? 1 : 0);
809
810 // If this assert fires, then a user is trying to collect
811 // a record multiple times in their cycle window or
812 // something else really bad has happened.
813 sparta_assert(argos_record_.time_Start < argos_record_.time_End);
814
815 // Write the old record to the file
816 pipeline_col_->writeRecord(argos_record_);
817 return true;
818 }
819
821 void startNewRecord_() {
822
823 // Set up the new start time for the new record
824 argos_record_.time_Start = pipeline_col_->getScheduler()->getCurrentTick();
825 }
826
828 void setCollecting_(bool collect, Collector * collector) override final
829 {
830 pipeline_col_ = dynamic_cast<PipelineCollector *>(collector);
831 sparta_assert(pipeline_col_ != nullptr,
832 "Collectables can only added to PipelineCollectors... for now");
833
834 // These fields only need to be set once - they define the format of the collectable
835 // and remain constant for the duration of the simulation
836 argos_record_.nameVector = getNameStrings();
837 argos_record_.length = argos_record_.nameVector.size();
838 has_display_id_field_ = (argos_record_.nameVector[0] == "DID");
839 for(const auto formatter: getFormatVector()) {
840 argos_record_.delimVector.emplace_back(formatter);
841 }
842
843 if(collect && hasData_()){
844
845 // Set the start time for this transaction to be
846 // moment collection is enabled.
847 startNewRecord_();
848 }
849
850 // If the collected object is null, this Collectable
851 // object is to be explicitly collected
852 if(collected_object_ && auto_collect_) {
853 if(collect) {
854
855 // Add this Collectable to the PipelineCollector's
856 // list of objects requiring collection
857 pipeline_col_->addToAutoCollection(this, collection_phase);
858 }
859 else {
860
861 // Remove this Collectable from the
862 // PipelineCollector's list of objects requiring
863 // collection
864 pipeline_col_->removeFromAutoCollection(this);
865 }
866 }
867 }
868 // The pair collectable object to be collected
869 const DataT * collected_object_ = nullptr;
870
871 // The live transaction record
872 pair_t argos_record_;
873
874 // Ze Collec-tor
875 PipelineCollector * pipeline_col_ = nullptr;
876
877 // For those folks that want a value to automatically
878 // disappear in the future
879 sparta::EventSet event_set_;
881
882 // Is this collectable currently closed?
883 bool record_closed_ = true;
884
885 // Should we auto-collect?
886 bool auto_collect_ = true;
887
888 // Is the first field the display ID (DID)?
889 bool has_display_id_field_ = false;
890
891 std::string log_string_;
892 };
893 }//namespace collection
894}//namespace sparta
File that defines the EventSet class.
Contains a collection implementation of various compile-time metaprogramming and Type-Detection APIs ...
typename remove_any_pointer< T >::type remove_any_pointer_t
Alias Template for remove_pointer.
File that defines the PayloadEvent class.
Class to facilitate pipeline collection operations.
File that defines the phases used in simulation.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define SPARTA_EXPECT_FALSE(x)
A macro for hinting to the compiler a particular condition should be considered most likely false.
#define CREATE_SPARTA_HANDLER_WITH_DATA(clname, meth, dataT)
Set of Events that a unit (or sparta::TreeNode, sparta::Resource) contains and are visible through a ...
Definition EventSet.hpp:26
Class to schedule a Scheduleable in the future with a payload, typed on both the data type and the sc...
ScheduleableHandle preparePayload(const DataT &payload)
Prepare a Scheduleable Payload for scheduling either now or later.
void schedule()
Schedule this event with its pre-set delay using the pre-set Clock.
Tick getCurrentTick() const noexcept
The current tick the Scheduler is working on or just finished.
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:205
static const group_idx_type GROUP_IDX_NONE
GroupIndex indicating that a node has no group index because it belongs to no group.
Definition TreeNode.hpp:303
node_uid_type getNodeUID() const
Gets the unique ID of this node.
std::string getLocation() const override final
static constexpr char GROUP_NAME_NONE[]
Group name indicating that a node belongs to no group.
Definition TreeNode.hpp:314
An abstract type of TreeNode that has virtual calls to start collection on this node,...
bool isCollected() const
Determine whether or not this node has collection turned on or off.
Collectable(sparta::TreeNode *parent, const std::string &name, const DataT *collected_object, uint64_t parentid=0, const std::string &desc="Collectable <no desc>")
Construct the Collectable.
MetaStruct::enable_if_t< MetaStruct::is_any_pointer< T >::value, void > collectWithDuration(const T &val, sparta::Clock::Cycle duration)
Explicitly collect a value from a shared pointer for the given duration.
Collectable(sparta::TreeNode *parent, const std::string &name, const std::string &group, uint32_t index, uint64_t parentid=0, const std::string &desc="Collectable <manual, no desc>")
Construct the Collectable, no data object associated, part of a group.
Collectable(sparta::TreeNode *parent, const std::string &name, uint64_t parentid=0, const std::string &desc="Collectable <manual, no desc>")
Construct the Collectable, no data object associated.
Class used to either manually or auto-collect an Annotation String object in a pipeline database.
virtual ~Collectable()
Virtual destructor – does nothing.
void collect(const DataT &val)
void setManualCollection()
Do not perform any automatic collection The SchedulingPhase is ignored.
void restartRecord() override final
void collect() override final
void collectWithDuration(const DataT &val, sparta::Clock::Cycle duration)
Explicitly collect a value for the given duration.
void closeRecord(const bool &simulation_ending=false) override final
void initialize(const DataT &val)
For manual collection, provide an initial value.
Collectable(sparta::TreeNode *parent, const std::string &name, const std::string &group, uint32_t index, uint64_t parentid=0, const std::string &desc="Collectable <manual, no desc>")
Construct the Collectable, no data object associated, part of a group.
EventSet & getEventSet_()
Get a reference to the internal event set.
Collectable(sparta::TreeNode *parent, const std::string &name, uint64_t parentid=0, const std::string &desc="Collectable <manual, no desc>")
Construct the Collectable, no data object associated.
void collectWithDuration(sparta::Clock::Cycle duration)
Calls collectWithDuration using the internal collected_object_ specified at construction.
Collectable(sparta::TreeNode *parent, const std::string &name, const DataT *collected_object, uint64_t parentid=0, const std::string &desc="Collectable <no desc>")
Construct the Collectable.
uint64_t getUniqueTransactionId()
Return a unique transaction id using a dummy counter.
void writeRecord(const R_Type &dat)
Output a finized transaction to our Outputter class.
void removeFromAutoCollection(CollectableTreeNode *ctn)
Remove the given CollectableTreeNode from collection.
void addToAutoCollection(CollectableTreeNode *ctn, SchedulingPhase collection_phase=SchedulingPhase::Tick)
Add the CollectableTreeNode to auto collection.
Macros for handling exponential backoff.
T * notNull(T *p)
Ensures that a pointer is not null.
Definition Utils.hpp:224