The Sparta Modeling Framework
Loading...
Searching...
No Matches
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"
32#include "simdb_fwd.hpp"
36
37namespace simdb {
38 class AsyncTaskEval;
39 class ObjectManager;
40} // namespace simdb
41
42namespace sparta::app {
43 class SimulationConfiguration;
44} // namespace sparta::app
45
46namespace sparta::trigger {
47 class SkippedAnnotatorBase;
48} // namespace sparta::trigger
49
50namespace sparta {
51
52 namespace report {
53 namespace format {
54 class BaseFormatter;
55 class FormatterFactory;
56 }
57 }
58
59 class Report;
60 class RootTreeNode;
61 class Scheduler;
62 class TreeNode;
63
64 namespace statistics {
66 class StreamNode;
67 }
68
69 namespace async {
70 class AsyncTimeseriesReport;
71 class AsyncNonTimeseriesReport;
72 }
73 namespace db {
74 class ReportHeader;
75 }
76
77 namespace app {
78
79 class Simulation;
80
81 typedef std::unordered_map<std::string, boost::any> NamedExtensions;
82 typedef std::unordered_map<std::string, std::string> TriggerKeyValues;
83 typedef std::unordered_map<std::string, std::string> MetaDataKeyValues;
84
89 {
91 typedef std::pair<Report*, report::format::BaseFormatter*> inst_t;
92
98 std::set<const Report*> triggered_reports_;
99
108 std::set<const Report*> idle_reports_;
109
113 std::map<std::string, std::shared_ptr<report::format::BaseFormatter>> formatters_;
114
119 std::shared_ptr<statistics::ReportStatisticsArchive> report_archive_;
120
125 std::shared_ptr<statistics::StreamNode> streaming_stats_root_;
126
133 bool updateReportActiveState_(const Report * r);
134
139 std::vector<inst_t> instantiations_;
140
146 const sparta::report::format::FormatterFactory* fact_;
147
152 uint32_t writes_;
153
158 uint32_t updates_;
159
163 class DescUpdateTracker {
164 public:
165 void enable(const Scheduler * scheduler);
166 bool checkIfDuplicateUpdate();
167 private:
169 utils::ValidValue<uint64_t> last_update_at_tick_;
170 const Scheduler * scheduler_ = nullptr;
171 };
172
177 DescUpdateTracker update_tracker_;
178
183 std::shared_ptr<sparta::trigger::SkippedAnnotatorBase> skipped_annotator_;
184
188 bool report_stopped_ = false;
189
194 bool enabled_ = true;
195
203 std::string orig_dest_file_;
204
211 std::shared_ptr<async::AsyncTimeseriesReport> db_timeseries_;
212
219 std::shared_ptr<async::AsyncNonTimeseriesReport> db_non_timeseries_;
220
234 const FeatureConfiguration::FeatureOptions * simdb_feature_opts_ = nullptr;
235
236 friend class ReportDescriptorCollection;
237
238 public:
239
243 static const char* GLOBAL_KEYWORD;
244
249 std::string loc_pattern;
250
255 std::string def_file;
256
261 std::string dest_file;
262
267 std::string format;
268
273 NamedExtensions extensions_;
274
278 MetaDataKeyValues header_metadata_;
279
285 void disable() {
286 enabled_ = false;
287 }
288
293 bool isEnabled() const {
294 return enabled_;
295 }
296
302
309
329 simdb::AsyncTaskEval * task_queue,
330 simdb::ObjectManager * sim_db,
331 const Clock & root_clk);
332
343 simdb::AsyncTaskEval * task_queue,
344 simdb::ObjectManager * sim_db);
345
352 db::ReportHeader * getTimeseriesDatabaseHeader();
353
364 simdb::AsyncTaskEval * task_queue,
365 simdb::ObjectManager * sim_db);
366
372 std::map<std::string, std::shared_ptr<
373 report::format::BaseFormatter>> getFormattersByFilename() const;
374
381 static bool isValidFormatName(const std::string& format);
382
410 ReportDescriptor(const std::string& _loc_pattern,
411 const std::string& _def_file,
412 const std::string& _dest_file,
413 const std::string& _format="text");
414
417
420
423
425 const std::string & getDescriptorPattern() const {
426 return loc_pattern;
427 }
428
430 const std::string & getDescriptorDefFile() const {
431 return def_file;
432 }
433
435 const std::string & getDescriptorDestFile() const {
436 return dest_file;
437 }
438
440 const std::string & getDescriptorFormat() const {
441 return format;
442 }
443
450 const std::string & getDescriptorOrigDestFile() const {
451 return orig_dest_file_;
452 }
453
460 std::shared_ptr<statistics::ReportStatisticsArchive> logOutputValuesToArchive(
461 const std::string & dir);
462
468 std::shared_ptr<statistics::StreamNode> createRootStatisticsStream();
469
474 const app::FeatureConfiguration * feature_config);
475
479 report_stopped_ = true;
480 }
481
485 std::string stringize() const {
486 std::stringstream ss;
487 ss << "Report def \"" << def_file << "\" on node \"" << loc_pattern
488 << "\" -> \"" << (orig_dest_file_.empty() ? dest_file : orig_dest_file_) << "\"";
489 if(format.size() != 0){
490 ss << " (format=" << format << ")";
491 }
492 return ss.str();
493 }
494
516 report::format::BaseFormatter* addInstantiation(Report* r,
517 Simulation* sim,
518 std::ostream* out=nullptr);
519
524 std::vector<inst_t> getInstantiations() const {
525 return instantiations_;
526 }
527
532 std::vector<Report*> getPendingInstantiations() const {
533 auto iter = extensions_.find("pending-reports");
534 if (iter != extensions_.end()) {
535 const std::vector<Report*> & pending_reports =
536 boost::any_cast<const std::vector<Report*>&>(iter->second);
537
538 return pending_reports;
539 }
540 return {};
541 }
542
548 std::vector<Report*> getAllInstantiations() const {
549 std::vector<inst_t> current = getInstantiations();
550 std::vector<Report*> pending = getPendingInstantiations();
551
552 std::unordered_set<Report*> all(pending.begin(), pending.end());
553 for (const auto & cur : current) {
554 all.insert(cur.first);
555 }
556 return std::vector<Report*>(all.begin(), all.end());
557 }
558
565 uint32_t writeOutput(std::ostream* out=nullptr);
566
573 uint32_t updateOutput(std::ostream* out=nullptr);
574
579
584 void capUpdatesToOncePerTick(const Scheduler * scheduler);
585
590 void setSkippedAnnotator(std::shared_ptr<sparta::trigger::SkippedAnnotatorBase> annotator);
591
600
604 uint32_t getUsageCount() const { return instantiations_.size(); }
605
610 uint32_t getNumWrites() const { return writes_; }
611
616 uint32_t getNumUpdates() const { return updates_; }
617
630 std::string computeFilename(const Report* r,
631 const std::string& sim_name,
632 uint32_t idx) const;
633 };
634
635 typedef std::vector<ReportDescriptor> ReportDescVec;
636 typedef std::vector<std::pair<std::string, std::string>> ReportYamlReplacements;
637
648 {
649 public:
650 ReportDescriptorCollection() = default;
651 ~ReportDescriptorCollection() = default;
652
654 ReportDescriptorCollection & operator=(const ReportDescriptorCollection &) = delete;
655
656 //While not copyable, moves are enabled to support std::swap
659
661 void push_back(const ReportDescriptor & rd) {
662 //Internal book-keeping is only for Python shell workflows.
663 //We also do not perform any book-keeping for descriptors
664 //whose dest_file is "1" (print to stdout). That is the
665 //only use case where duplicate dest_file's is actually
666 //okay. We will add the descriptor to the collection, but
667 //any attempts to access this descriptor later (i.e. to
668 //change its parameters or to disable it from Python) will
669 //throw an exception that the descriptor "1" cannot be found.
670 if (rd.dest_file != "1") {
671 const std::string desc_name = getDescriptorName_(rd);
672 auto iter = indices_by_descriptor_name_.find(desc_name);
673 if (iter != indices_by_descriptor_name_.end()) {
674 //Descriptor found by this name. Throw if it is already
675 //enabled. Users can only replace disabled descriptors.
676 if (rep_descs_.at(iter->second).isEnabled()) {
677 throw SpartaException("Report descriptor named '") <<
678 desc_name << "' already exists in this configuration";
679 }
680 }
681 indices_by_descriptor_name_[desc_name] = rep_descs_.size();
682 }
683
684 //This incoming 'rd' variable could be a reference to a
685 //previously disabled descriptor:
686 //
687 // >>> rd = report_config.descriptors.foo_csv
688 // >>> report_config.removeReport('foo_csv')
689 // >>> report_config.addReport(rd)
690 //
691 //This would still count as an enabled descriptor.
692 rep_descs_.push_back(rd);
693 rep_descs_.back().enabled_ = true;
694 }
695
697 template <class... Args>
698 void emplace_back(Args&&... args) {
699 ReportDescriptor rd(std::forward<Args>(args)...);
700 push_back(rd);
701 }
702
704 void clear() {
705 for (auto &rd : rep_descs_) {
706 rd.disable();
707 }
708 }
709
711 size_t size() const {
712 size_t sz = 0;
713 for (const auto & rep_desc : rep_descs_) {
714 if (rep_desc.isEnabled()) {
715 ++sz;
716 }
717 }
718 return sz;
719 }
720
722 bool empty() const {
723 return size() == 0;
724 }
725
728 bool contains(const std::string & desc_name) const {
729 auto iter = indices_by_descriptor_name_.find(desc_name);
730 if (iter == indices_by_descriptor_name_.end()) {
731 //The name may have been given to us with a period, not
732 //an underscore, separating the file stem and the extension
733 const auto replaced_desc_name = replaceDotsWithUnderscores_(desc_name);
734 iter = indices_by_descriptor_name_.find(replaced_desc_name);
735 }
736
737 //If this name is not even found in our map, we do not
738 //contain this descriptor
739 if (iter == indices_by_descriptor_name_.end()) {
740 return false;
741 }
742
743 //But even if the descriptor is physcially here, we have
744 //to also ask it if is is still enabled
745 return rep_descs_.at(iter->second).isEnabled();
746 }
747
749 ReportDescriptor & getDescriptorByName(const std::string & desc_name) {
750 auto idx = getDescriptorIndexByName_(desc_name);
751 return rep_descs_.at(idx);
752 }
753
756 void removeDescriptorByName(const std::string & desc_name) {
757 getDescriptorByName(desc_name).disable();
758 }
759
762 std::vector<std::string> getAllDescriptorNames() const {
763 std::vector<std::string> names;
764 for (const auto & rd : indices_by_descriptor_name_) {
765 if (rep_descs_.at(rd.second).isEnabled()) {
766 names.push_back(rd.first);
767 }
768 }
769 return names;
770 }
771
773 std::deque<ReportDescriptor>::iterator begin() {
774 return rep_descs_.begin();
775 }
776
778 std::deque<ReportDescriptor>::const_iterator begin() const {
779 return rep_descs_.cbegin();
780 }
781
783 std::deque<ReportDescriptor>::iterator end() {
784 return rep_descs_.end();
785 }
786
788 std::deque<ReportDescriptor>::const_iterator end() const {
789 return rep_descs_.cend();
790 }
791
792 private:
793 std::string getDescriptorName_(const ReportDescriptor & rd) const {
794 return replaceDotsWithUnderscores_(rd.dest_file);
795 }
796
797 std::string replaceDotsWithUnderscores_(const std::string & str) const {
798 std::string replaced = str;
799 boost::replace_all(replaced, ".", "_");
800 return replaced;
801 }
802
803 size_t getDescriptorIndexByName_(const std::string & desc_name) const {
804 auto iter = indices_by_descriptor_name_.find(desc_name);
805 if (iter == indices_by_descriptor_name_.end()) {
806 //The name may have been given to us with a period, not
807 //an underscore, separating the file stem and the extension
808 const auto replaced_desc_name = replaceDotsWithUnderscores_(desc_name);
809 iter = indices_by_descriptor_name_.find(replaced_desc_name);
810
811 //If it still is not found, error out
812 if (iter == indices_by_descriptor_name_.end()) {
813 throw SpartaException(
814 "No descriptor named '") << desc_name << "' exists";
815 }
816 }
817
818 //The descriptor exists in this collection, but we still need
819 //to throw if it has been disabled, since we are not supposed
820 //to be using those descriptors for any reason anymore.
821 if (!rep_descs_.at(iter->second).isEnabled()) {
822 throw SpartaException("The descriptor named '") << desc_name
823 << "' has already been disabled";
824 }
825 return iter->second;
826 }
827
828 std::deque<ReportDescriptor> rep_descs_;
829 std::unordered_map<std::string, size_t> indices_by_descriptor_name_;
830 };
831
845 public:
847 ReportDescriptorCollection * collection,
848 RootTreeNode * root);
849
850 ReportConfiguration() = delete;
851 ~ReportConfiguration() = default;
852
854 ReportConfiguration & operator=(const ReportConfiguration &) = delete;
855
857 void addReport(const ReportDescriptor & rd);
858
862 void addReportsFromYaml(const std::string & yaml_file);
863
866 void removeReportByName(const std::string & rd_name);
867
872 void addMemoryReportsFromYaml(const std::string & yaml_file);
873
876
880
883
886
887 private:
888 //When using the Python shell, adding/removing reports
889 //from the configuration will refresh the appropriate
890 //variable(s) in the Python workspace
891 void republishReportCollection_();
892
893 //Remove report config variable from the Python namespace
894 void finishPythonInteraction_();
895
896 //Finalize any changes to the descriptors from here on out
897 void disallowChangesToDescriptors_();
898
899 //Give the Simulation base class access to the above private
900 //methods related to Python shell interaction. Note that the
901 //sim_config_, collection_, and root_ member variables are
902 //const pointers, so even with friend access, the Simulation
903 //class still won't be able to change those variables for any
904 //reason.
905 friend class Simulation;
906
907 SimulationConfiguration *const sim_config_;
908 ReportDescriptorCollection *const collection_;
909 RootTreeNode *const root_;
910 bool allow_descriptor_changes_ = true;
911 };
912
917 ReportYamlReplacements createReplacementsFromYaml(
918 const std::string & replacements_yaml);
919
926 const std::string & def_file,
927 TreeNode * context);
928
952 const std::string & def_file,
953 TreeNode * context,
954 const ReportYamlReplacements & placeholder_key_value_pairs);
955
961 const std::string & def_string,
962 TreeNode * context);
963
964 } // namespace app
965} // 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.
A representation of simulated time.
Definition Clock.hpp:51
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
Collection of named feature values.
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...
std::map< std::string, std::shared_ptr< report::format::BaseFormatter > > getFormattersByFilename() const
Provide access to the formatters we have been using so they can coordinate with the reporting infrast...
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...
void configureAsyncTimeseriesReport(simdb::AsyncTaskEval *task_queue, simdb::ObjectManager *sim_db, const Clock &root_clk)
Switch this descriptor's timeseries report generation from synchronous .csv generation to asynchronou...
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.
void doPostProcessing(simdb::AsyncTaskEval *task_queue, simdb::ObjectManager *sim_db)
Do any post-simulation post processing steps needed. This is typically used for final wrap-up this de...
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.
db::ReportHeader * getTimeseriesDatabaseHeader()
Give access to the database timeseries header. This will return null when this descriptor is used for...
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".
void inspectSimulatorFeatureValues(const app::FeatureConfiguration *feature_config)
Give the descriptor a chance to see the –feature values that were set at the command line,...
const std::string & getDescriptorOrigDestFile() const
When SimDB has automatic report verification enabled, this descriptor may have had its dest_file chan...
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.
void configureAsyncNonTimeseriesReport(simdb::AsyncTaskEval *task_queue, simdb::ObjectManager *sim_db)
Switch this descriptor's report generation from synchronous to asynchronous database persistence....
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...
bool isSingleTimeseriesReport() const
Check if this descriptor holds only one report instantiation, and that it is a timeseries report (....
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".
bool isSingleNonTimeseriesReport() const
Check if this descriptor holds only one report instantiation, and that it is not a timeseries report....
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.