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
243 std::string def_file;
244
249 std::string dest_file;
250
255 std::string format;
256
261 NamedExtensions extensions_;
262
266 MetaDataKeyValues header_metadata_;
267
273 void disable() {
274 enabled_ = false;
275 }
276
281 bool isEnabled() const {
282 return enabled_;
283 }
284
291 static bool isValidFormatName(const std::string& format);
292
320 ReportDescriptor(const std::string& _loc_pattern,
321 const std::string& _def_file,
322 const std::string& _dest_file,
323 const std::string& _format="text");
324
327
330
333
335 const std::string & getDescriptorPattern() const {
336 return loc_pattern;
337 }
338
340 const std::string & getDescriptorDefFile() const {
341 return def_file;
342 }
343
345 const std::string & getDescriptorDestFile() const {
346 return dest_file;
347 }
348
350 const std::string & getDescriptorFormat() const {
351 return format;
352 }
353
359 const std::string & getDescriptorOrigDestFile() const {
360 return orig_dest_file_;
361 }
362
369 std::shared_ptr<statistics::ReportStatisticsArchive> logOutputValuesToArchive(
370 const std::string & dir);
371
377 std::shared_ptr<statistics::StreamNode> createRootStatisticsStream();
378
383
387 report_stopped_ = true;
388 }
389
393 std::string stringize() const {
394 std::stringstream ss;
395 ss << "Report def \"" << def_file << "\" on node \"" << loc_pattern
396 << "\" -> \"" << (orig_dest_file_.empty() ? dest_file : orig_dest_file_) << "\"";
397 if(format.size() != 0){
398 ss << " (format=" << format << ")";
399 }
400 return ss.str();
401 }
402
424 report::format::BaseFormatter* addInstantiation(Report* r,
425 Simulation* sim,
426 std::ostream* out=nullptr);
427
432 std::vector<inst_t> getInstantiations() const {
433 return instantiations_;
434 }
435
440 std::vector<Report*> getPendingInstantiations() const {
441 auto iter = extensions_.find("pending-reports");
442 if (iter != extensions_.end()) {
443 const std::vector<Report*> & pending_reports =
444 boost::any_cast<const std::vector<Report*>&>(iter->second);
445
446 return pending_reports;
447 }
448 return {};
449 }
450
456 std::vector<Report*> getAllInstantiations() const {
457 std::vector<inst_t> current = getInstantiations();
458 std::vector<Report*> pending = getPendingInstantiations();
459
460 std::unordered_set<Report*> all(pending.begin(), pending.end());
461 for (const auto & cur : current) {
462 all.insert(cur.first);
463 }
464 return std::vector<Report*>(all.begin(), all.end());
465 }
466
482 legacy_reports_enabled_ = false;
483 }
484
491 uint32_t writeOutput(std::ostream* out=nullptr);
492
499 uint32_t updateOutput(std::ostream* out=nullptr);
500
505
510 void capUpdatesToOncePerTick(const Scheduler * scheduler);
511
516 void setSkippedAnnotator(std::shared_ptr<sparta::trigger::SkippedAnnotatorBase> annotator);
517
526
530 void teardown();
531
535 uint32_t getUsageCount() const { return instantiations_.size(); }
536
541 uint32_t getNumWrites() const { return writes_; }
542
547 uint32_t getNumUpdates() const { return updates_; }
548
561 std::string computeFilename(const Report* r,
562 const std::string& sim_name,
563 uint32_t idx) const;
564 };
565
566 typedef std::vector<ReportDescriptor> ReportDescVec;
567 typedef std::vector<std::pair<std::string, std::string>> ReportYamlReplacements;
568
579 {
580 public:
581 ReportDescriptorCollection() = default;
582 ~ReportDescriptorCollection() = default;
583
585 ReportDescriptorCollection & operator=(const ReportDescriptorCollection &) = delete;
586
587 //While not copyable, moves are enabled to support std::swap
590
592 void push_back(const ReportDescriptor & rd) {
593 //Internal book-keeping is only for Python shell workflows.
594 //We also do not perform any book-keeping for descriptors
595 //whose dest_file is "1" (print to stdout). That is the
596 //only use case where duplicate dest_file's is actually
597 //okay. We will add the descriptor to the collection, but
598 //any attempts to access this descriptor later (i.e. to
599 //change its parameters or to disable it from Python) will
600 //throw an exception that the descriptor "1" cannot be found.
601 if (rd.dest_file != "1") {
602 const std::string desc_name = getDescriptorName_(rd);
603 auto iter = indices_by_descriptor_name_.find(desc_name);
604 if (iter != indices_by_descriptor_name_.end()) {
605 //Descriptor found by this name. Throw if it is already
606 //enabled. Users can only replace disabled descriptors.
607 if (rep_descs_.at(iter->second).isEnabled()) {
608 throw SpartaException("Report descriptor named '") <<
609 desc_name << "' already exists in this configuration";
610 }
611 }
612 indices_by_descriptor_name_[desc_name] = rep_descs_.size();
613 }
614
615 //This incoming 'rd' variable could be a reference to a
616 //previously disabled descriptor:
617 //
618 // >>> rd = report_config.descriptors.foo_csv
619 // >>> report_config.removeReport('foo_csv')
620 // >>> report_config.addReport(rd)
621 //
622 //This would still count as an enabled descriptor.
623 rep_descs_.push_back(rd);
624 rep_descs_.back().enabled_ = true;
625 }
626
628 template <class... Args>
629 void emplace_back(Args&&... args) {
630 ReportDescriptor rd(std::forward<Args>(args)...);
631 push_back(rd);
632 }
633
635 void clear() {
636 for (auto &rd : rep_descs_) {
637 rd.disable();
638 }
639 }
640
642 size_t size() const {
643 size_t sz = 0;
644 for (const auto & rep_desc : rep_descs_) {
645 if (rep_desc.isEnabled()) {
646 ++sz;
647 }
648 }
649 return sz;
650 }
651
653 bool empty() const {
654 return size() == 0;
655 }
656
659 bool contains(const std::string & desc_name) const {
660 auto iter = indices_by_descriptor_name_.find(desc_name);
661 if (iter == indices_by_descriptor_name_.end()) {
662 //The name may have been given to us with a period, not
663 //an underscore, separating the file stem and the extension
664 const auto replaced_desc_name = replaceDotsWithUnderscores_(desc_name);
665 iter = indices_by_descriptor_name_.find(replaced_desc_name);
666 }
667
668 //If this name is not even found in our map, we do not
669 //contain this descriptor
670 if (iter == indices_by_descriptor_name_.end()) {
671 return false;
672 }
673
674 //But even if the descriptor is physcially here, we have
675 //to also ask it if is is still enabled
676 return rep_descs_.at(iter->second).isEnabled();
677 }
678
680 ReportDescriptor & getDescriptorByName(const std::string & desc_name) {
681 auto idx = getDescriptorIndexByName_(desc_name);
682 return rep_descs_.at(idx);
683 }
684
687 void removeDescriptorByName(const std::string & desc_name) {
688 getDescriptorByName(desc_name).disable();
689 }
690
693 std::vector<std::string> getAllDescriptorNames() const {
694 std::vector<std::string> names;
695 for (const auto & rd : indices_by_descriptor_name_) {
696 if (rep_descs_.at(rd.second).isEnabled()) {
697 names.push_back(rd.first);
698 }
699 }
700 return names;
701 }
702
704 std::deque<ReportDescriptor>::iterator begin() {
705 return rep_descs_.begin();
706 }
707
709 std::deque<ReportDescriptor>::const_iterator begin() const {
710 return rep_descs_.cbegin();
711 }
712
714 std::deque<ReportDescriptor>::iterator end() {
715 return rep_descs_.end();
716 }
717
719 std::deque<ReportDescriptor>::const_iterator end() const {
720 return rep_descs_.cend();
721 }
722
723 private:
724 std::string getDescriptorName_(const ReportDescriptor & rd) const {
725 return replaceDotsWithUnderscores_(rd.dest_file);
726 }
727
728 std::string replaceDotsWithUnderscores_(const std::string & str) const {
729 std::string replaced = str;
730 boost::replace_all(replaced, ".", "_");
731 return replaced;
732 }
733
734 size_t getDescriptorIndexByName_(const std::string & desc_name) const {
735 auto iter = indices_by_descriptor_name_.find(desc_name);
736 if (iter == indices_by_descriptor_name_.end()) {
737 //The name may have been given to us with a period, not
738 //an underscore, separating the file stem and the extension
739 const auto replaced_desc_name = replaceDotsWithUnderscores_(desc_name);
740 iter = indices_by_descriptor_name_.find(replaced_desc_name);
741
742 //If it still is not found, error out
743 if (iter == indices_by_descriptor_name_.end()) {
744 throw SpartaException(
745 "No descriptor named '") << desc_name << "' exists";
746 }
747 }
748
749 //The descriptor exists in this collection, but we still need
750 //to throw if it has been disabled, since we are not supposed
751 //to be using those descriptors for any reason anymore.
752 if (!rep_descs_.at(iter->second).isEnabled()) {
753 throw SpartaException("The descriptor named '") << desc_name
754 << "' has already been disabled";
755 }
756 return iter->second;
757 }
758
759 std::deque<ReportDescriptor> rep_descs_;
760 std::unordered_map<std::string, size_t> indices_by_descriptor_name_;
761 };
762
776 public:
778 ReportDescriptorCollection * collection,
779 RootTreeNode * root);
780
781 ReportConfiguration() = delete;
782 ~ReportConfiguration() = default;
783
785 ReportConfiguration & operator=(const ReportConfiguration &) = delete;
786
788 void addReport(const ReportDescriptor & rd);
789
793 void addReportsFromYaml(const std::string & yaml_file);
794
797 void removeReportByName(const std::string & rd_name);
798
803 void addMemoryReportsFromYaml(const std::string & yaml_file);
804
807
811
814
817
818 private:
819 //When using the Python shell, adding/removing reports
820 //from the configuration will refresh the appropriate
821 //variable(s) in the Python workspace
822 void republishReportCollection_();
823
824 //Remove report config variable from the Python namespace
825 void finishPythonInteraction_();
826
827 //Finalize any changes to the descriptors from here on out
828 void disallowChangesToDescriptors_();
829
830 //Give the Simulation base class access to the above private
831 //methods related to Python shell interaction. Note that the
832 //sim_config_, collection_, and root_ member variables are
833 //const pointers, so even with friend access, the Simulation
834 //class still won't be able to change those variables for any
835 //reason.
836 friend class Simulation;
837
838 SimulationConfiguration *const sim_config_;
839 ReportDescriptorCollection *const collection_;
840 RootTreeNode *const root_;
841 bool allow_descriptor_changes_ = true;
842 };
843
848 ReportYamlReplacements createReplacementsFromYaml(
849 const std::string & replacements_yaml);
850
857 const std::string & def_file,
858 TreeNode * context);
859
883 const std::string & def_file,
884 TreeNode * context,
885 const ReportYamlReplacements & placeholder_key_value_pairs);
886
892 const std::string & def_string,
893 TreeNode * context);
894
895 } // namespace app
896} // 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...
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.
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.