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#include "sparta/log/NotificationSource.hpp"
36
37namespace sparta::app {
38 class SimulationConfiguration;
39 class ReportStatsCollector;
40} // namespace sparta::app
41
42namespace sparta::trigger {
43 class SkippedAnnotatorBase;
44} // namespace sparta::trigger
45
46namespace sparta {
47
48 namespace report {
49 namespace format {
50 class BaseFormatter;
51 class FormatterFactory;
52 }
53 }
54
55 class Report;
56 class RootTreeNode;
57 class Scheduler;
58 class TreeNode;
59
60 namespace statistics {
62 class StreamNode;
63 }
64
65 namespace app {
66
67 class Simulation;
68
69 typedef std::unordered_map<std::string, boost::any> NamedExtensions;
70 typedef std::unordered_map<std::string, std::string> TriggerKeyValues;
71 typedef std::unordered_map<std::string, std::string> MetaDataKeyValues;
72
77 {
79 typedef std::pair<Report*, report::format::BaseFormatter*> inst_t;
80
86 std::set<const Report*> triggered_reports_;
87
96 std::set<const Report*> idle_reports_;
97
101 std::map<std::string, std::shared_ptr<report::format::BaseFormatter>> formatters_;
102
107 std::shared_ptr<statistics::ReportStatisticsArchive> report_archive_;
108
113 std::shared_ptr<statistics::StreamNode> streaming_stats_root_;
114
121 bool updateReportActiveState_(const Report * r);
122
127 std::vector<inst_t> instantiations_;
128
134 const sparta::report::format::FormatterFactory* fact_;
135
140 uint32_t writes_;
141
146 uint32_t updates_;
147
151 class DescUpdateTracker {
152 public:
153 void enable(const Scheduler * scheduler);
154 bool checkIfDuplicateUpdate();
155 private:
157 utils::ValidValue<uint64_t> last_update_at_tick_;
158 const Scheduler * scheduler_ = nullptr;
159 };
160
165 DescUpdateTracker update_tracker_;
166
171 std::shared_ptr<sparta::trigger::SkippedAnnotatorBase> skipped_annotator_;
172
176 bool report_stopped_ = false;
177
182 bool enabled_ = true;
183
191 std::string orig_dest_file_;
192
196 ReportStatsCollector* collector_ = nullptr;
197
207 bool legacy_reports_enabled_ = true;
208
214 bool uses_sprintf_notif_trigger_ = false;
215
220 std::vector<std::string> sprintf_notif_trigger_dest_files_;
221
228 void sweepSimDbStats_();
229
236 void skipSimDbStats_();
237
238 friend class ReportDescriptorCollection;
239
240 public:
241
245 static const char* GLOBAL_KEYWORD;
246
251 std::string loc_pattern;
252
256 std::string name;
257
262 std::string def_file;
263
268 std::string dest_file;
269
274 std::string format;
275
280 NamedExtensions extensions_;
281
285 MetaDataKeyValues header_metadata_;
286
292 void disable() {
293 enabled_ = false;
294 }
295
300 bool isEnabled() const {
301 return enabled_;
302 }
303
310 static bool isValidFormatName(const std::string& format);
311
339 ReportDescriptor(const std::string& _loc_pattern,
340 const std::string& _def_file,
341 const std::string& _dest_file,
342 const std::string& _format="text");
343
346
349
352
354 const std::string & getDescriptorName() const {
355 return name;
356 }
357
359 const std::string & getDescriptorPattern() const {
360 return loc_pattern;
361 }
362
364 const std::string & getDescriptorDefFile() const {
365 return def_file;
366 }
367
369 const std::string & getDescriptorDestFile() const {
370 return dest_file;
371 }
372
374 const std::string & getDescriptorFormat() const {
375 return format;
376 }
377
383 const std::string & getDescriptorOrigDestFile() const {
384 return orig_dest_file_;
385 }
386
393 std::shared_ptr<statistics::ReportStatisticsArchive> logOutputValuesToArchive(
394 const std::string & dir);
395
401 std::shared_ptr<statistics::StreamNode> createRootStatisticsStream();
402
407
411 report_stopped_ = true;
412 }
413
417 std::string stringize() const {
418 auto comma_sep_files = [](const std::vector<std::string>& dest_files)
419 {
420 std::ostringstream oss;
421 bool comma = false;
422 for (const auto& s : dest_files)
423 {
424 oss << s;
425 if (comma)
426 {
427 oss << ", ";
428 }
429 comma = true;
430 }
431 return oss.str();
432 };
433
434 auto _dest_files = sprintf_notif_trigger_dest_files_.empty() ?
435 (orig_dest_file_.empty() ? dest_file : orig_dest_file_) :
436 comma_sep_files(sprintf_notif_trigger_dest_files_);
437
438 std::stringstream ss;
439 ss << "Report def \"" << def_file << "\" on node \"" << loc_pattern
440 << "\" -> \"" << _dest_files << "\"";
441 if(format.size() != 0){
442 ss << " (format=" << format << ")";
443 }
444 return ss.str();
445 }
446
468 report::format::BaseFormatter* addInstantiation(Report* r,
469 Simulation* sim,
470 std::ostream* out=nullptr);
471
476 std::vector<inst_t> getInstantiations() const {
477 return instantiations_;
478 }
479
484 std::vector<Report*> getPendingInstantiations() const {
485 auto iter = extensions_.find("pending-reports");
486 if (iter != extensions_.end()) {
487 const std::vector<Report*> & pending_reports =
488 boost::any_cast<const std::vector<Report*>&>(iter->second);
489
490 return pending_reports;
491 }
492 return {};
493 }
494
500 std::vector<Report*> getAllInstantiations() const {
501 std::vector<inst_t> current = getInstantiations();
502 std::vector<Report*> pending = getPendingInstantiations();
503
504 std::unordered_set<Report*> all(pending.begin(), pending.end());
505 for (const auto & cur : current) {
506 all.insert(cur.first);
507 }
508 return std::vector<Report*>(all.begin(), all.end());
509 }
510
526 legacy_reports_enabled_ = false;
527 }
528
535 uint32_t writeOutput(std::ostream* out=nullptr);
536
545 uint32_t writeOutput(const std::string& filename);
546
553 uint32_t updateOutput(std::ostream* out=nullptr);
554
559
564 void capUpdatesToOncePerTick(const Scheduler * scheduler);
565
572 uses_sprintf_notif_trigger_ = true;
573 notif_src->setFormatString(dest_file);
574 }
575
580 return uses_sprintf_notif_trigger_;
581 }
582
587 void setSkippedAnnotator(std::shared_ptr<sparta::trigger::SkippedAnnotatorBase> annotator);
588
597
601 void teardown();
602
606 uint32_t getUsageCount() const { return instantiations_.size(); }
607
612 uint32_t getNumWrites() const { return writes_; }
613
618 uint32_t getNumUpdates() const { return updates_; }
619
632 std::string computeFilename(const Report* r,
633 const std::string& sim_name,
634 uint32_t idx) const;
635 };
636
637 typedef std::vector<ReportDescriptor> ReportDescVec;
638 typedef std::vector<std::pair<std::string, std::string>> ReportYamlReplacements;
639
650 {
651 public:
652 ReportDescriptorCollection() = default;
653 ~ReportDescriptorCollection() = default;
654
656 ReportDescriptorCollection & operator=(const ReportDescriptorCollection &) = delete;
657
658 //While not copyable, moves are enabled to support std::swap
661
663 void push_back(const ReportDescriptor & rd) {
664 //Internal book-keeping is only for Python shell workflows.
665 //We also do not perform any book-keeping for descriptors
666 //whose dest_file is "1" (print to stdout). That is the
667 //only use case where duplicate dest_file's is actually
668 //okay. We will add the descriptor to the collection, but
669 //any attempts to access this descriptor later (i.e. to
670 //change its parameters or to disable it from Python) will
671 //throw an exception that the descriptor "1" cannot be found.
672 if (rd.dest_file != "1") {
673 const std::string desc_name = getDescriptorName_(rd);
674 auto iter = indices_by_descriptor_name_.find(desc_name);
675 if (iter != indices_by_descriptor_name_.end()) {
676 //Descriptor found by this name. Throw if it is already
677 //enabled. Users can only replace disabled descriptors.
678 if (rep_descs_.at(iter->second).isEnabled()) {
679 throw SpartaException("Report descriptor named '") <<
680 desc_name << "' already exists in this configuration";
681 }
682 }
683 indices_by_descriptor_name_[desc_name] = rep_descs_.size();
684 }
685
686 //This incoming 'rd' variable could be a reference to a
687 //previously disabled descriptor:
688 //
689 // >>> rd = report_config.descriptors.foo_csv
690 // >>> report_config.removeReport('foo_csv')
691 // >>> report_config.addReport(rd)
692 //
693 //This would still count as an enabled descriptor.
694 rep_descs_.push_back(rd);
695 rep_descs_.back().enabled_ = true;
696 }
697
699 template <class... Args>
700 void emplace_back(Args&&... args) {
701 ReportDescriptor rd(std::forward<Args>(args)...);
702 push_back(rd);
703 }
704
706 void clear() {
707 for (auto &rd : rep_descs_) {
708 rd.disable();
709 }
710 }
711
713 size_t size() const {
714 size_t sz = 0;
715 for (const auto & rep_desc : rep_descs_) {
716 if (rep_desc.isEnabled()) {
717 ++sz;
718 }
719 }
720 return sz;
721 }
722
724 bool empty() const {
725 return size() == 0;
726 }
727
730 bool contains(const std::string & desc_name) const {
731 auto iter = indices_by_descriptor_name_.find(desc_name);
732 if (iter == indices_by_descriptor_name_.end()) {
733 //The name may have been given to us with a period, not
734 //an underscore, separating the file stem and the extension
735 const auto replaced_desc_name = replaceDotsWithUnderscores_(desc_name);
736 iter = indices_by_descriptor_name_.find(replaced_desc_name);
737 }
738
739 //If this name is not even found in our map, we do not
740 //contain this descriptor
741 if (iter == indices_by_descriptor_name_.end()) {
742 return false;
743 }
744
745 //But even if the descriptor is physcially here, we have
746 //to also ask it if is is still enabled
747 return rep_descs_.at(iter->second).isEnabled();
748 }
749
751 ReportDescriptor & getDescriptorByName(const std::string & desc_name) {
752 auto idx = getDescriptorIndexByName_(desc_name);
753 return rep_descs_.at(idx);
754 }
755
758 void removeDescriptorByName(const std::string & desc_name) {
759 getDescriptorByName(desc_name).disable();
760 }
761
764 std::vector<std::string> getAllDescriptorNames() const {
765 std::vector<std::string> names;
766 for (const auto & rd : indices_by_descriptor_name_) {
767 if (rep_descs_.at(rd.second).isEnabled()) {
768 names.push_back(rd.first);
769 }
770 }
771 return names;
772 }
773
775 std::deque<ReportDescriptor>::iterator begin() {
776 return rep_descs_.begin();
777 }
778
780 std::deque<ReportDescriptor>::const_iterator begin() const {
781 return rep_descs_.cbegin();
782 }
783
785 std::deque<ReportDescriptor>::iterator end() {
786 return rep_descs_.end();
787 }
788
790 std::deque<ReportDescriptor>::const_iterator end() const {
791 return rep_descs_.cend();
792 }
793
794 private:
795 std::string getDescriptorName_(const ReportDescriptor & rd) const {
796 return replaceDotsWithUnderscores_(rd.dest_file);
797 }
798
799 std::string replaceDotsWithUnderscores_(const std::string & str) const {
800 std::string replaced = str;
801 boost::replace_all(replaced, ".", "_");
802 return replaced;
803 }
804
805 size_t getDescriptorIndexByName_(const std::string & desc_name) const {
806 auto iter = indices_by_descriptor_name_.find(desc_name);
807 if (iter == indices_by_descriptor_name_.end()) {
808 //The name may have been given to us with a period, not
809 //an underscore, separating the file stem and the extension
810 const auto replaced_desc_name = replaceDotsWithUnderscores_(desc_name);
811 iter = indices_by_descriptor_name_.find(replaced_desc_name);
812
813 //If it still is not found, error out
814 if (iter == indices_by_descriptor_name_.end()) {
815 throw SpartaException(
816 "No descriptor named '") << desc_name << "' exists";
817 }
818 }
819
820 //The descriptor exists in this collection, but we still need
821 //to throw if it has been disabled, since we are not supposed
822 //to be using those descriptors for any reason anymore.
823 if (!rep_descs_.at(iter->second).isEnabled()) {
824 throw SpartaException("The descriptor named '") << desc_name
825 << "' has already been disabled";
826 }
827 return iter->second;
828 }
829
830 std::deque<ReportDescriptor> rep_descs_;
831 std::unordered_map<std::string, size_t> indices_by_descriptor_name_;
832 };
833
847 public:
849 ReportDescriptorCollection * collection,
850 RootTreeNode * root);
851
852 ReportConfiguration() = delete;
853 ~ReportConfiguration() = default;
854
856 ReportConfiguration & operator=(const ReportConfiguration &) = delete;
857
859 void addReport(const ReportDescriptor & rd);
860
864 void addReportsFromYaml(const std::string & yaml_file);
865
868 void removeReportByName(const std::string & rd_name);
869
874 void addMemoryReportsFromYaml(const std::string & yaml_file);
875
878
882
885
888
889 private:
890 //When using the Python shell, adding/removing reports
891 //from the configuration will refresh the appropriate
892 //variable(s) in the Python workspace
893 void republishReportCollection_();
894
895 //Remove report config variable from the Python namespace
896 void finishPythonInteraction_();
897
898 //Finalize any changes to the descriptors from here on out
899 void disallowChangesToDescriptors_();
900
901 //Give the Simulation base class access to the above private
902 //methods related to Python shell interaction. Note that the
903 //sim_config_, collection_, and root_ member variables are
904 //const pointers, so even with friend access, the Simulation
905 //class still won't be able to change those variables for any
906 //reason.
907 friend class Simulation;
908
909 SimulationConfiguration *const sim_config_;
910 ReportDescriptorCollection *const collection_;
911 RootTreeNode *const root_;
912 bool allow_descriptor_changes_ = true;
913 };
914
919 ReportYamlReplacements createReplacementsFromYaml(
920 const std::string & replacements_yaml);
921
928 const std::string & def_file,
929 TreeNode * context);
930
954 const std::string & def_file,
955 TreeNode * context,
956 const ReportYamlReplacements & placeholder_key_value_pairs);
957
963 const std::string & def_string,
964 TreeNode * context);
965
966 } // namespace app
967} // 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.
Accepts a sprintf-style format string like "%s_%i.json" and uses the arguments given to postNotificat...
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.
bool usesSprintfNotifTrigger() const
Returns whether this descriptor uses a 'sprintf-notif' trigger.
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.
void setUsesSprintfNotifTrigger(SprintfNotificationSource *notif_src)
Marks this descriptor as using a 'sprintf-notif' trigger, meaning dest_file is a sprintf-style format...
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...
uint32_t writeOutput(const std::string &filename)
Overload used only by descriptors bound to a 'sprintf-notif' trigger. Writes all non-updatable instan...
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...
Class that is the "top level" or starting point for simulation.
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.