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"
35
36namespace sparta::app {
37 class SimulationConfiguration;
38 class ReportStatsCollector;
39} // namespace sparta::app
40
41namespace sparta::trigger {
42 class SkippedAnnotatorBase;
43} // namespace sparta::trigger
44
45namespace sparta {
46
47 namespace report {
48 namespace format {
49 class BaseFormatter;
50 class FormatterFactory;
51 }
52 }
53
54 class Report;
55 class RootTreeNode;
56 class Scheduler;
57 class TreeNode;
58
59 namespace statistics {
61 class StreamNode;
62 }
63
64 namespace app {
65
66 class Simulation;
67
68 typedef std::unordered_map<std::string, boost::any> NamedExtensions;
69 typedef std::unordered_map<std::string, std::string> TriggerKeyValues;
70 typedef std::unordered_map<std::string, std::string> MetaDataKeyValues;
71
76 {
78 typedef std::pair<Report*, report::format::BaseFormatter*> inst_t;
79
85 std::set<const Report*> triggered_reports_;
86
95 std::set<const Report*> idle_reports_;
96
100 std::map<std::string, std::shared_ptr<report::format::BaseFormatter>> formatters_;
101
106 std::shared_ptr<statistics::ReportStatisticsArchive> report_archive_;
107
112 std::shared_ptr<statistics::StreamNode> streaming_stats_root_;
113
120 bool updateReportActiveState_(const Report * r);
121
126 std::vector<inst_t> instantiations_;
127
133 const sparta::report::format::FormatterFactory* fact_;
134
139 uint32_t writes_;
140
145 uint32_t updates_;
146
150 class DescUpdateTracker {
151 public:
152 void enable(const Scheduler * scheduler);
153 bool checkIfDuplicateUpdate();
154 private:
156 utils::ValidValue<uint64_t> last_update_at_tick_;
157 const Scheduler * scheduler_ = nullptr;
158 };
159
164 DescUpdateTracker update_tracker_;
165
170 std::shared_ptr<sparta::trigger::SkippedAnnotatorBase> skipped_annotator_;
171
175 bool report_stopped_ = false;
176
181 bool enabled_ = true;
182
190 std::string orig_dest_file_;
191
195 ReportStatsCollector* collector_ = nullptr;
196
206 bool legacy_reports_enabled_ = true;
207
214 void sweepSimDbStats_();
215
222 void skipSimDbStats_();
223
224 friend class ReportDescriptorCollection;
225
226 public:
227
231 static const char* GLOBAL_KEYWORD;
232
237 std::string loc_pattern;
238
242 std::string name;
243
248 std::string def_file;
249
254 std::string dest_file;
255
260 std::string format;
261
266 NamedExtensions extensions_;
267
271 MetaDataKeyValues header_metadata_;
272
278 void disable() {
279 enabled_ = false;
280 }
281
286 bool isEnabled() const {
287 return enabled_;
288 }
289
296 static bool isValidFormatName(const std::string& format);
297
325 ReportDescriptor(const std::string& _loc_pattern,
326 const std::string& _def_file,
327 const std::string& _dest_file,
328 const std::string& _format="text");
329
332
335
338
340 const std::string & getDescriptorName() const {
341 return name;
342 }
343
345 const std::string & getDescriptorPattern() const {
346 return loc_pattern;
347 }
348
350 const std::string & getDescriptorDefFile() const {
351 return def_file;
352 }
353
355 const std::string & getDescriptorDestFile() const {
356 return dest_file;
357 }
358
360 const std::string & getDescriptorFormat() const {
361 return format;
362 }
363
369 const std::string & getDescriptorOrigDestFile() const {
370 return orig_dest_file_;
371 }
372
379 std::shared_ptr<statistics::ReportStatisticsArchive> logOutputValuesToArchive(
380 const std::string & dir);
381
387 std::shared_ptr<statistics::StreamNode> createRootStatisticsStream();
388
393
397 report_stopped_ = true;
398 }
399
403 std::string stringize() const {
404 std::stringstream ss;
405 ss << "Report def \"" << def_file << "\" on node \"" << loc_pattern
406 << "\" -> \"" << (orig_dest_file_.empty() ? dest_file : orig_dest_file_) << "\"";
407 if(format.size() != 0){
408 ss << " (format=" << format << ")";
409 }
410 return ss.str();
411 }
412
434 report::format::BaseFormatter* addInstantiation(Report* r,
435 Simulation* sim,
436 std::ostream* out=nullptr);
437
442 std::vector<inst_t> getInstantiations() const {
443 return instantiations_;
444 }
445
450 std::vector<Report*> getPendingInstantiations() const {
451 auto iter = extensions_.find("pending-reports");
452 if (iter != extensions_.end()) {
453 const std::vector<Report*> & pending_reports =
454 boost::any_cast<const std::vector<Report*>&>(iter->second);
455
456 return pending_reports;
457 }
458 return {};
459 }
460
466 std::vector<Report*> getAllInstantiations() const {
467 std::vector<inst_t> current = getInstantiations();
468 std::vector<Report*> pending = getPendingInstantiations();
469
470 std::unordered_set<Report*> all(pending.begin(), pending.end());
471 for (const auto & cur : current) {
472 all.insert(cur.first);
473 }
474 return std::vector<Report*>(all.begin(), all.end());
475 }
476
492 legacy_reports_enabled_ = false;
493 }
494
501 uint32_t writeOutput(std::ostream* out=nullptr);
502
509 uint32_t updateOutput(std::ostream* out=nullptr);
510
515
520 void capUpdatesToOncePerTick(const Scheduler * scheduler);
521
526 void setSkippedAnnotator(std::shared_ptr<sparta::trigger::SkippedAnnotatorBase> annotator);
527
536
540 void teardown();
541
545 uint32_t getUsageCount() const { return instantiations_.size(); }
546
551 uint32_t getNumWrites() const { return writes_; }
552
557 uint32_t getNumUpdates() const { return updates_; }
558
571 std::string computeFilename(const Report* r,
572 const std::string& sim_name,
573 uint32_t idx) const;
574 };
575
576 typedef std::vector<ReportDescriptor> ReportDescVec;
577 typedef std::vector<std::pair<std::string, std::string>> ReportYamlReplacements;
578
589 {
590 public:
591 ReportDescriptorCollection() = default;
592 ~ReportDescriptorCollection() = default;
593
595 ReportDescriptorCollection & operator=(const ReportDescriptorCollection &) = delete;
596
597 //While not copyable, moves are enabled to support std::swap
600
602 void push_back(const ReportDescriptor & rd) {
603 //Internal book-keeping is only for Python shell workflows.
604 //We also do not perform any book-keeping for descriptors
605 //whose dest_file is "1" (print to stdout). That is the
606 //only use case where duplicate dest_file's is actually
607 //okay. We will add the descriptor to the collection, but
608 //any attempts to access this descriptor later (i.e. to
609 //change its parameters or to disable it from Python) will
610 //throw an exception that the descriptor "1" cannot be found.
611 if (rd.dest_file != "1") {
612 const std::string desc_name = getDescriptorName_(rd);
613 auto iter = indices_by_descriptor_name_.find(desc_name);
614 if (iter != indices_by_descriptor_name_.end()) {
615 //Descriptor found by this name. Throw if it is already
616 //enabled. Users can only replace disabled descriptors.
617 if (rep_descs_.at(iter->second).isEnabled()) {
618 throw SpartaException("Report descriptor named '") <<
619 desc_name << "' already exists in this configuration";
620 }
621 }
622 indices_by_descriptor_name_[desc_name] = rep_descs_.size();
623 }
624
625 //This incoming 'rd' variable could be a reference to a
626 //previously disabled descriptor:
627 //
628 // >>> rd = report_config.descriptors.foo_csv
629 // >>> report_config.removeReport('foo_csv')
630 // >>> report_config.addReport(rd)
631 //
632 //This would still count as an enabled descriptor.
633 rep_descs_.push_back(rd);
634 rep_descs_.back().enabled_ = true;
635 }
636
638 template <class... Args>
639 void emplace_back(Args&&... args) {
640 ReportDescriptor rd(std::forward<Args>(args)...);
641 push_back(rd);
642 }
643
645 void clear() {
646 for (auto &rd : rep_descs_) {
647 rd.disable();
648 }
649 }
650
652 size_t size() const {
653 size_t sz = 0;
654 for (const auto & rep_desc : rep_descs_) {
655 if (rep_desc.isEnabled()) {
656 ++sz;
657 }
658 }
659 return sz;
660 }
661
663 bool empty() const {
664 return size() == 0;
665 }
666
669 bool contains(const std::string & desc_name) const {
670 auto iter = indices_by_descriptor_name_.find(desc_name);
671 if (iter == indices_by_descriptor_name_.end()) {
672 //The name may have been given to us with a period, not
673 //an underscore, separating the file stem and the extension
674 const auto replaced_desc_name = replaceDotsWithUnderscores_(desc_name);
675 iter = indices_by_descriptor_name_.find(replaced_desc_name);
676 }
677
678 //If this name is not even found in our map, we do not
679 //contain this descriptor
680 if (iter == indices_by_descriptor_name_.end()) {
681 return false;
682 }
683
684 //But even if the descriptor is physcially here, we have
685 //to also ask it if is is still enabled
686 return rep_descs_.at(iter->second).isEnabled();
687 }
688
690 ReportDescriptor & getDescriptorByName(const std::string & desc_name) {
691 auto idx = getDescriptorIndexByName_(desc_name);
692 return rep_descs_.at(idx);
693 }
694
697 void removeDescriptorByName(const std::string & desc_name) {
698 getDescriptorByName(desc_name).disable();
699 }
700
703 std::vector<std::string> getAllDescriptorNames() const {
704 std::vector<std::string> names;
705 for (const auto & rd : indices_by_descriptor_name_) {
706 if (rep_descs_.at(rd.second).isEnabled()) {
707 names.push_back(rd.first);
708 }
709 }
710 return names;
711 }
712
714 std::deque<ReportDescriptor>::iterator begin() {
715 return rep_descs_.begin();
716 }
717
719 std::deque<ReportDescriptor>::const_iterator begin() const {
720 return rep_descs_.cbegin();
721 }
722
724 std::deque<ReportDescriptor>::iterator end() {
725 return rep_descs_.end();
726 }
727
729 std::deque<ReportDescriptor>::const_iterator end() const {
730 return rep_descs_.cend();
731 }
732
733 private:
734 std::string getDescriptorName_(const ReportDescriptor & rd) const {
735 return replaceDotsWithUnderscores_(rd.dest_file);
736 }
737
738 std::string replaceDotsWithUnderscores_(const std::string & str) const {
739 std::string replaced = str;
740 boost::replace_all(replaced, ".", "_");
741 return replaced;
742 }
743
744 size_t getDescriptorIndexByName_(const std::string & desc_name) const {
745 auto iter = indices_by_descriptor_name_.find(desc_name);
746 if (iter == indices_by_descriptor_name_.end()) {
747 //The name may have been given to us with a period, not
748 //an underscore, separating the file stem and the extension
749 const auto replaced_desc_name = replaceDotsWithUnderscores_(desc_name);
750 iter = indices_by_descriptor_name_.find(replaced_desc_name);
751
752 //If it still is not found, error out
753 if (iter == indices_by_descriptor_name_.end()) {
754 throw SpartaException(
755 "No descriptor named '") << desc_name << "' exists";
756 }
757 }
758
759 //The descriptor exists in this collection, but we still need
760 //to throw if it has been disabled, since we are not supposed
761 //to be using those descriptors for any reason anymore.
762 if (!rep_descs_.at(iter->second).isEnabled()) {
763 throw SpartaException("The descriptor named '") << desc_name
764 << "' has already been disabled";
765 }
766 return iter->second;
767 }
768
769 std::deque<ReportDescriptor> rep_descs_;
770 std::unordered_map<std::string, size_t> indices_by_descriptor_name_;
771 };
772
786 public:
788 ReportDescriptorCollection * collection,
789 RootTreeNode * root);
790
791 ReportConfiguration() = delete;
792 ~ReportConfiguration() = default;
793
795 ReportConfiguration & operator=(const ReportConfiguration &) = delete;
796
798 void addReport(const ReportDescriptor & rd);
799
803 void addReportsFromYaml(const std::string & yaml_file);
804
807 void removeReportByName(const std::string & rd_name);
808
813 void addMemoryReportsFromYaml(const std::string & yaml_file);
814
817
821
824
827
828 private:
829 //When using the Python shell, adding/removing reports
830 //from the configuration will refresh the appropriate
831 //variable(s) in the Python workspace
832 void republishReportCollection_();
833
834 //Remove report config variable from the Python namespace
835 void finishPythonInteraction_();
836
837 //Finalize any changes to the descriptors from here on out
838 void disallowChangesToDescriptors_();
839
840 //Give the Simulation base class access to the above private
841 //methods related to Python shell interaction. Note that the
842 //sim_config_, collection_, and root_ member variables are
843 //const pointers, so even with friend access, the Simulation
844 //class still won't be able to change those variables for any
845 //reason.
846 friend class Simulation;
847
848 SimulationConfiguration *const sim_config_;
849 ReportDescriptorCollection *const collection_;
850 RootTreeNode *const root_;
851 bool allow_descriptor_changes_ = true;
852 };
853
858 ReportYamlReplacements createReplacementsFromYaml(
859 const std::string & replacements_yaml);
860
867 const std::string & def_file,
868 TreeNode * context);
869
893 const std::string & def_file,
894 TreeNode * context,
895 const ReportYamlReplacements & placeholder_key_value_pairs);
896
902 const std::string & def_string,
903 TreeNode * context);
904
905 } // namespace app
906} // 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:204
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...
bool configSimDbReports(app::ReportStatsCollector *collector)
Get ready for SimDB report collection.
void disableLegacyReports()
In MAP v2.1, we provide report systems for both legacy reports and SimDB-exported reports....
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.
void teardown()
Called when the ReportRepository is shutting down.
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.
std::string name
The name of this report.
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...
const std::string & getDescriptorName() const
Getter for this report's name.
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.