The Sparta Modeling Framework
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ReportDescriptor.hpp
Go to the documentation of this file.
1
7#pragma once
8
9#include <algorithm>
10#include <boost/algorithm/string/replace.hpp>
11#include <boost/any.hpp>
12#include <boost/iterator/iterator_traits.hpp>
13#include <boost/type_index/type_index_facade.hpp>
14#include <cstddef>
15#include <cstdint>
16#include <deque>
17#include <map>
18#include <memory>
19#include <ostream>
20#include <set>
21#include <string>
22#include <unordered_map>
23#include <unordered_set>
24#include <utility>
25#include <vector>
26
28#include "sparta/report/Report.hpp"
29#include "sparta/utils/Utils.hpp"
30#include "sparta/report/format/BaseFormatter.hpp"
35
36namespace sparta::app {
37 class SimulationConfiguration;
38} // namespace sparta::app
39
40namespace sparta::trigger {
41 class SkippedAnnotatorBase;
42} // namespace sparta::trigger
43
44namespace sparta {
45
46 namespace report {
47 namespace format {
48 class BaseFormatter;
49 class FormatterFactory;
50 }
51 }
52
53 class Report;
54 class RootTreeNode;
55 class Scheduler;
56 class TreeNode;
57
58 namespace statistics {
60 class StreamNode;
61 }
62
63 namespace app {
64
65 class Simulation;
66
67 typedef std::unordered_map<std::string, boost::any> NamedExtensions;
68 typedef std::unordered_map<std::string, std::string> TriggerKeyValues;
69 typedef std::unordered_map<std::string, std::string> MetaDataKeyValues;
70
75 {
77 typedef std::pair<Report*, report::format::BaseFormatter*> inst_t;
78
84 std::set<const Report*> triggered_reports_;
85
94 std::set<const Report*> idle_reports_;
95
99 std::map<std::string, std::shared_ptr<report::format::BaseFormatter>> formatters_;
100
105 std::shared_ptr<statistics::ReportStatisticsArchive> report_archive_;
106
111 std::shared_ptr<statistics::StreamNode> streaming_stats_root_;
112
119 bool updateReportActiveState_(const Report * r);
120
125 std::vector<inst_t> instantiations_;
126
132 const sparta::report::format::FormatterFactory* fact_;
133
138 uint32_t writes_;
139
144 uint32_t updates_;
145
149 class DescUpdateTracker {
150 public:
151 void enable(const Scheduler * scheduler);
152 bool checkIfDuplicateUpdate();
153 private:
155 utils::ValidValue<uint64_t> last_update_at_tick_;
156 const Scheduler * scheduler_ = nullptr;
157 };
158
163 DescUpdateTracker update_tracker_;
164
169 std::shared_ptr<sparta::trigger::SkippedAnnotatorBase> skipped_annotator_;
170
174 bool report_stopped_ = false;
175
180 bool enabled_ = true;
181
189 std::string orig_dest_file_;
190
191 friend class ReportDescriptorCollection;
192
193 public:
194
198 static const char* GLOBAL_KEYWORD;
199
204 std::string loc_pattern;
205
210 std::string def_file;
211
216 std::string dest_file;
217
222 std::string format;
223
228 NamedExtensions extensions_;
229
233 MetaDataKeyValues header_metadata_;
234
240 void disable() {
241 enabled_ = false;
242 }
243
248 bool isEnabled() const {
249 return enabled_;
250 }
251
258 static bool isValidFormatName(const std::string& format);
259
287 ReportDescriptor(const std::string& _loc_pattern,
288 const std::string& _def_file,
289 const std::string& _dest_file,
290 const std::string& _format="text");
291
294
297
300
302 const std::string & getDescriptorPattern() const {
303 return loc_pattern;
304 }
305
307 const std::string & getDescriptorDefFile() const {
308 return def_file;
309 }
310
312 const std::string & getDescriptorDestFile() const {
313 return dest_file;
314 }
315
317 const std::string & getDescriptorFormat() const {
318 return format;
319 }
320
326 const std::string & getDescriptorOrigDestFile() const {
327 return orig_dest_file_;
328 }
329
336 std::shared_ptr<statistics::ReportStatisticsArchive> logOutputValuesToArchive(
337 const std::string & dir);
338
344 std::shared_ptr<statistics::StreamNode> createRootStatisticsStream();
345
349 report_stopped_ = true;
350 }
351
355 std::string stringize() const {
356 std::stringstream ss;
357 ss << "Report def \"" << def_file << "\" on node \"" << loc_pattern
358 << "\" -> \"" << (orig_dest_file_.empty() ? dest_file : orig_dest_file_) << "\"";
359 if(format.size() != 0){
360 ss << " (format=" << format << ")";
361 }
362 return ss.str();
363 }
364
386 report::format::BaseFormatter* addInstantiation(Report* r,
387 Simulation* sim,
388 std::ostream* out=nullptr);
389
394 std::vector<inst_t> getInstantiations() const {
395 return instantiations_;
396 }
397
402 std::vector<Report*> getPendingInstantiations() const {
403 auto iter = extensions_.find("pending-reports");
404 if (iter != extensions_.end()) {
405 const std::vector<Report*> & pending_reports =
406 boost::any_cast<const std::vector<Report*>&>(iter->second);
407
408 return pending_reports;
409 }
410 return {};
411 }
412
418 std::vector<Report*> getAllInstantiations() const {
419 std::vector<inst_t> current = getInstantiations();
420 std::vector<Report*> pending = getPendingInstantiations();
421
422 std::unordered_set<Report*> all(pending.begin(), pending.end());
423 for (const auto & cur : current) {
424 all.insert(cur.first);
425 }
426 return std::vector<Report*>(all.begin(), all.end());
427 }
428
435 uint32_t writeOutput(std::ostream* out=nullptr);
436
443 uint32_t updateOutput(std::ostream* out=nullptr);
444
449
454 void capUpdatesToOncePerTick(const Scheduler * scheduler);
455
460 void setSkippedAnnotator(std::shared_ptr<sparta::trigger::SkippedAnnotatorBase> annotator);
461
470
474 uint32_t getUsageCount() const { return instantiations_.size(); }
475
480 uint32_t getNumWrites() const { return writes_; }
481
486 uint32_t getNumUpdates() const { return updates_; }
487
500 std::string computeFilename(const Report* r,
501 const std::string& sim_name,
502 uint32_t idx) const;
503 };
504
505 typedef std::vector<ReportDescriptor> ReportDescVec;
506 typedef std::vector<std::pair<std::string, std::string>> ReportYamlReplacements;
507
518 {
519 public:
520 ReportDescriptorCollection() = default;
521 ~ReportDescriptorCollection() = default;
522
524 ReportDescriptorCollection & operator=(const ReportDescriptorCollection &) = delete;
525
526 //While not copyable, moves are enabled to support std::swap
529
531 void push_back(const ReportDescriptor & rd) {
532 //Internal book-keeping is only for Python shell workflows.
533 //We also do not perform any book-keeping for descriptors
534 //whose dest_file is "1" (print to stdout). That is the
535 //only use case where duplicate dest_file's is actually
536 //okay. We will add the descriptor to the collection, but
537 //any attempts to access this descriptor later (i.e. to
538 //change its parameters or to disable it from Python) will
539 //throw an exception that the descriptor "1" cannot be found.
540 if (rd.dest_file != "1") {
541 const std::string desc_name = getDescriptorName_(rd);
542 auto iter = indices_by_descriptor_name_.find(desc_name);
543 if (iter != indices_by_descriptor_name_.end()) {
544 //Descriptor found by this name. Throw if it is already
545 //enabled. Users can only replace disabled descriptors.
546 if (rep_descs_.at(iter->second).isEnabled()) {
547 throw SpartaException("Report descriptor named '") <<
548 desc_name << "' already exists in this configuration";
549 }
550 }
551 indices_by_descriptor_name_[desc_name] = rep_descs_.size();
552 }
553
554 //This incoming 'rd' variable could be a reference to a
555 //previously disabled descriptor:
556 //
557 // >>> rd = report_config.descriptors.foo_csv
558 // >>> report_config.removeReport('foo_csv')
559 // >>> report_config.addReport(rd)
560 //
561 //This would still count as an enabled descriptor.
562 rep_descs_.push_back(rd);
563 rep_descs_.back().enabled_ = true;
564 }
565
567 template <class... Args>
568 void emplace_back(Args&&... args) {
569 ReportDescriptor rd(std::forward<Args>(args)...);
570 push_back(rd);
571 }
572
574 void clear() {
575 for (auto &rd : rep_descs_) {
576 rd.disable();
577 }
578 }
579
581 size_t size() const {
582 size_t sz = 0;
583 for (const auto & rep_desc : rep_descs_) {
584 if (rep_desc.isEnabled()) {
585 ++sz;
586 }
587 }
588 return sz;
589 }
590
592 bool empty() const {
593 return size() == 0;
594 }
595
598 bool contains(const std::string & desc_name) const {
599 auto iter = indices_by_descriptor_name_.find(desc_name);
600 if (iter == indices_by_descriptor_name_.end()) {
601 //The name may have been given to us with a period, not
602 //an underscore, separating the file stem and the extension
603 const auto replaced_desc_name = replaceDotsWithUnderscores_(desc_name);
604 iter = indices_by_descriptor_name_.find(replaced_desc_name);
605 }
606
607 //If this name is not even found in our map, we do not
608 //contain this descriptor
609 if (iter == indices_by_descriptor_name_.end()) {
610 return false;
611 }
612
613 //But even if the descriptor is physcially here, we have
614 //to also ask it if is is still enabled
615 return rep_descs_.at(iter->second).isEnabled();
616 }
617
619 ReportDescriptor & getDescriptorByName(const std::string & desc_name) {
620 auto idx = getDescriptorIndexByName_(desc_name);
621 return rep_descs_.at(idx);
622 }
623
626 void removeDescriptorByName(const std::string & desc_name) {
627 getDescriptorByName(desc_name).disable();
628 }
629
632 std::vector<std::string> getAllDescriptorNames() const {
633 std::vector<std::string> names;
634 for (const auto & rd : indices_by_descriptor_name_) {
635 if (rep_descs_.at(rd.second).isEnabled()) {
636 names.push_back(rd.first);
637 }
638 }
639 return names;
640 }
641
643 std::deque<ReportDescriptor>::iterator begin() {
644 return rep_descs_.begin();
645 }
646
648 std::deque<ReportDescriptor>::const_iterator begin() const {
649 return rep_descs_.cbegin();
650 }
651
653 std::deque<ReportDescriptor>::iterator end() {
654 return rep_descs_.end();
655 }
656
658 std::deque<ReportDescriptor>::const_iterator end() const {
659 return rep_descs_.cend();
660 }
661
662 private:
663 std::string getDescriptorName_(const ReportDescriptor & rd) const {
664 return replaceDotsWithUnderscores_(rd.dest_file);
665 }
666
667 std::string replaceDotsWithUnderscores_(const std::string & str) const {
668 std::string replaced = str;
669 boost::replace_all(replaced, ".", "_");
670 return replaced;
671 }
672
673 size_t getDescriptorIndexByName_(const std::string & desc_name) const {
674 auto iter = indices_by_descriptor_name_.find(desc_name);
675 if (iter == indices_by_descriptor_name_.end()) {
676 //The name may have been given to us with a period, not
677 //an underscore, separating the file stem and the extension
678 const auto replaced_desc_name = replaceDotsWithUnderscores_(desc_name);
679 iter = indices_by_descriptor_name_.find(replaced_desc_name);
680
681 //If it still is not found, error out
682 if (iter == indices_by_descriptor_name_.end()) {
683 throw SpartaException(
684 "No descriptor named '") << desc_name << "' exists";
685 }
686 }
687
688 //The descriptor exists in this collection, but we still need
689 //to throw if it has been disabled, since we are not supposed
690 //to be using those descriptors for any reason anymore.
691 if (!rep_descs_.at(iter->second).isEnabled()) {
692 throw SpartaException("The descriptor named '") << desc_name
693 << "' has already been disabled";
694 }
695 return iter->second;
696 }
697
698 std::deque<ReportDescriptor> rep_descs_;
699 std::unordered_map<std::string, size_t> indices_by_descriptor_name_;
700 };
701
715 public:
717 ReportDescriptorCollection * collection,
718 RootTreeNode * root);
719
720 ReportConfiguration() = delete;
721 ~ReportConfiguration() = default;
722
724 ReportConfiguration & operator=(const ReportConfiguration &) = delete;
725
727 void addReport(const ReportDescriptor & rd);
728
732 void addReportsFromYaml(const std::string & yaml_file);
733
736 void removeReportByName(const std::string & rd_name);
737
742 void addMemoryReportsFromYaml(const std::string & yaml_file);
743
746
750
753
756
757 private:
758 //When using the Python shell, adding/removing reports
759 //from the configuration will refresh the appropriate
760 //variable(s) in the Python workspace
761 void republishReportCollection_();
762
763 //Remove report config variable from the Python namespace
764 void finishPythonInteraction_();
765
766 //Finalize any changes to the descriptors from here on out
767 void disallowChangesToDescriptors_();
768
769 //Give the Simulation base class access to the above private
770 //methods related to Python shell interaction. Note that the
771 //sim_config_, collection_, and root_ member variables are
772 //const pointers, so even with friend access, the Simulation
773 //class still won't be able to change those variables for any
774 //reason.
775 friend class Simulation;
776
777 SimulationConfiguration *const sim_config_;
778 ReportDescriptorCollection *const collection_;
779 RootTreeNode *const root_;
780 bool allow_descriptor_changes_ = true;
781 };
782
787 ReportYamlReplacements createReplacementsFromYaml(
788 const std::string & replacements_yaml);
789
796 const std::string & def_file,
797 TreeNode * context);
798
822 const std::string & def_file,
823 TreeNode * context,
824 const ReportYamlReplacements & placeholder_key_value_pairs);
825
831 const std::string & def_string,
832 TreeNode * context);
833
834 } // namespace app
835} // namespace sparta
File that defines the Clock class.
Exception class for all of Sparta.
Basic Node framework in sparta device tree composite pattern.
File that defines a ValidValue.
TreeNode which represents the root ("top") of a device tree.
A class that lets you schedule events now and in the future.
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:205
Configuration applicator class that is used for configuring a simulator's reports....
void addMemoryReportsFromYaml(const std::string &yaml_file)
const ReportDescriptorCollection * getDescriptors() const
Access the underlying report descriptors.
ReportDescriptorCollection * getDescriptors()
Access the underlying report descriptors.
void showAllReportDescriptorInfo()
Pretty print information about all app::ReportDescriptor's.
void addReportsFromYaml(const std::string &yaml_file)
void removeReportByName(const std::string &rd_name)
void addReport(const ReportDescriptor &rd)
Add one report descriptor to this collection.
This collection of ReportDescriptors is designed to never deallocate memory once it has been allocate...
std::vector< std::string > getAllDescriptorNames() const
bool contains(const std::string &desc_name) const
void removeDescriptorByName(const std::string &desc_name)
std::deque< ReportDescriptor >::const_iterator begin() const
Iterator access.
size_t size() const
Get the number of enabled descriptors in this collection.
ReportDescriptor & getDescriptorByName(const std::string &desc_name)
Access a descriptor by the app::ReportDescriptor's "dest_file".
std::deque< ReportDescriptor >::iterator end()
Iterator access.
std::deque< ReportDescriptor >::iterator begin()
Iterator access.
void push_back(const ReportDescriptor &rd)
Add one report descriptor to this collection.
std::deque< ReportDescriptor >::const_iterator end() const
Iterator access.
void emplace_back(Args &&... args)
Add one report descriptor to this collection.
bool empty() const
See if there are any enabled descriptors in this collection.
void clear()
Remove (disable) all descriptors from this collection.
Describes one or more report to instantiate.
~ReportDescriptor()
Destructor (note that triggered reports are automatically flushed)
MetaDataKeyValues header_metadata_
Metadata to include in report headers.
uint32_t getUsageCount() const
Returns the usage count (incremented by addInstantiation)
const std::string & getDescriptorDefFile() const
Getter for this descriptor's def_file, e.g. "simple_stats.yaml".
std::string format
Optional formatting string (how to write to the file). This is converted to lower-case at constructio...
std::vector< Report * > getAllInstantiations() const
Returns all report instantiations, including those already instantiated (no report start trigger) and...
std::string computeFilename(const Report *r, const std::string &sim_name, uint32_t idx) const
Computes the filename to which this reportdescriptor will be saved using any necessary variables in t...
uint32_t writeOutput(std::ostream *out=nullptr)
Saves all of the instantiations whose formatters do not support 'update' to their respective destinat...
void capUpdatesToOncePerTick(const Scheduler *scheduler)
Instruct this descriptor to automatically ignore any "duplicate" updates that occur at the exact same...
report::format::BaseFormatter * addInstantiation(Report *r, Simulation *sim, std::ostream *out=nullptr)
Tracks a report instantiated based on this descriptor and allocates a new formatter for it....
const std::string & getDescriptorFormat() const
Getter for this descriptor's format, e.g. "json_reduced".
std::string dest_file
Destination filename to which report will be written. Later this will represent other types of destin...
uint32_t getNumWrites() const
Returns the number of writes done on this report descriptor's instantiated report formatters.
uint32_t getNumUpdates() const
Returns the number of updates done on this report descriptor's instantiated report formatters.
std::vector< Report * > getPendingInstantiations() const
Returns a vector of reports that have not been instantiated yet, but will be when this report descrip...
static const char * GLOBAL_KEYWORD
Global search scope node keyword for report locations.
ReportDescriptor & operator=(const ReportDescriptor &)=default
Allow assignment.
std::string loc_pattern
Node location string (pattern) on which report should be generated. Typically "" or top.
ReportDescriptor(const std::string &_loc_pattern, const std::string &_def_file, const std::string &_dest_file, const std::string &_format="text")
Construct a report decsriptor.
void clearDestinationFiles(const Simulation &sim)
Clears all destination files that will be filled with instances of this report descriptor.
ReportDescriptor(const ReportDescriptor &)=default
Allow construction.
void disable()
Calling this method causes the simulation to skip this descriptor when it is setting up its reports....
const std::string & getDescriptorDestFile() const
Getter for this descriptor's dest_file, e.g. "out.json".
const std::string & getDescriptorOrigDestFile() const
This descriptor may have had its dest_file changed when the the Simulation::setupReports() method was...
bool isEnabled() const
See if this descriptor is enabled or not. Disabling a descriptor means that it will be filtered from ...
std::vector< inst_t > getInstantiations() const
Returns the vector of instantiated reports based on this descriptor.
NamedExtensions extensions_
Key-value extensions used by parsers to bind opaque report configurations to descriptors.
static bool isValidFormatName(const std::string &format)
Determines if format is a valid name for formatter. This allows the command line parser to disregard ...
void skipOutput()
Let the descriptor know to skip over one update of data.
std::shared_ptr< statistics::ReportStatisticsArchive > logOutputValuesToArchive(const std::string &dir)
Tell the descriptor to send all of its writeOutput / updateOutput statistics values to a binary archi...
std::string def_file
Filename of report definition. If '@', auto generates reports with all counters and stats of loc_patt...
std::shared_ptr< statistics::StreamNode > createRootStatisticsStream()
Create and return a StreamNode object that sits at the top of a tree hierarchy that describes the Rep...
void ignoreFurtherUpdates()
Report descriptors may be triggered to stop early - ensure no further updates are written to disk.
std::string stringize() const
Represents this descriptor as a string.
const std::string & getDescriptorPattern() const
Getter for this descriptor's pattern, e.g. "_global".
void setSkippedAnnotator(std::shared_ptr< sparta::trigger::SkippedAnnotatorBase > annotator)
Give this descriptor a specific annotator subclass for printing skipped update information to reports...
uint32_t updateOutput(std::ostream *out=nullptr)
Updates all of the instantiations whose formatters support 'update', possibly by writing to the desti...
Configuration applicator class that is used for configuring a simulator. Works in conjunction with sp...
Simulator which builds a sparta DeviceTree.
This class coordinates live SPARTA simulations (source) with binary output archives (sink).
When a simulation is configured to stream its statistics values for asynchronous processing,...
Provides a wrapper around a value to ensure that the value is assigned.
Sparta Application framework.
ReportDescVec createDescriptorsFromFile(const std::string &def_file, TreeNode *context)
Given a multi-report definition YAML file, parse it out into individual descriptors,...
ReportYamlReplacements createReplacementsFromYaml(const std::string &replacements_yaml)
Parse a YAML file containing key-value pairs into a single ReportYamlReplacements data structure.
ReportDescVec createDescriptorsFromDefinitionString(const std::string &def_string, TreeNode *context)
Given a multi-report definition string, parse it out into individual descriptors.
ReportDescVec createDescriptorsFromFileWithPlaceholderReplacements(const std::string &def_file, TreeNode *context, const ReportYamlReplacements &placeholder_key_value_pairs)
This method is similar to "createDescriptorsFromFile()", except that it can be used for report yaml f...
Macros for handling exponential backoff.