diff --git a/.azure-pipelines/azure-pipelines-osx.yml b/.azure-pipelines/azure-pipelines-osx.yml index 66d42e66d2..f3e9416919 100755 --- a/.azure-pipelines/azure-pipelines-osx.yml +++ b/.azure-pipelines/azure-pipelines-osx.yml @@ -22,6 +22,9 @@ jobs: # TODO: Fast finish on azure pipelines? - script: | export CI=azure + export flow_run_id=azure_$(Build.BuildNumber).$(System.JobAttempt) + export remote_url=$(Build.Repository.Uri) + export sha=$(Build.SourceVersion) export OSX_FORCE_SDK_DOWNLOAD="1" export GIT_BRANCH=$BUILD_SOURCEBRANCHNAME export FEEDSTOCK_NAME=$(basename ${BUILD_REPOSITORY_NAME}) diff --git a/.ci_support/linux_64_.yaml b/.ci_support/linux_64_.yaml index 009e5385eb..fa78cbc1d1 100644 --- a/.ci_support/linux_64_.yaml +++ b/.ci_support/linux_64_.yaml @@ -19,7 +19,7 @@ cxx_compiler_version: docker_image: - quay.io/condaforge/linux-anvil-cos7-x86_64 hdf5: -- 1.12.2 +- 1.14.2 linux_clang_version: - '15' numpy: diff --git a/.ci_support/osx_64_.yaml b/.ci_support/osx_64_.yaml index df7720d25e..3fe86c687e 100644 --- a/.ci_support/osx_64_.yaml +++ b/.ci_support/osx_64_.yaml @@ -7,7 +7,7 @@ boost_cpp: c_compiler: - clang c_compiler_version: -- '15' +- '16' channel_sources: - conda-forge channel_targets: @@ -15,9 +15,9 @@ channel_targets: cxx_compiler: - clangxx cxx_compiler_version: -- '15' +- '16' hdf5: -- 1.12.2 +- 1.14.2 macos_machine: - x86_64-apple-darwin13.4.0 numpy: diff --git a/.ci_support/osx_arm64_.yaml b/.ci_support/osx_arm64_.yaml index d2a3ebf9c4..dee6c16ba1 100644 --- a/.ci_support/osx_arm64_.yaml +++ b/.ci_support/osx_arm64_.yaml @@ -7,7 +7,7 @@ boost_cpp: c_compiler: - clang c_compiler_version: -- '15' +- '16' channel_sources: - conda-forge channel_targets: @@ -15,9 +15,9 @@ channel_targets: cxx_compiler: - clangxx cxx_compiler_version: -- '15' +- '16' hdf5: -- 1.12.2 +- 1.14.2 macos_machine: - arm64-apple-darwin20.0.0 numpy: diff --git a/.circleci/config.yml b/.circleci/config.yml index c2fe449549..15a7db477e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,6 +24,9 @@ jobs: # Run, test and (if we have a BINSTAR_TOKEN) upload the distributions. command: | export CI=circle + export flow_run_id="circle_$CIRCLE_WORKFLOW_ID" + export remote_url="$CIRCLE_REPOSITORY_URL" + export sha="$CIRCLE_SHA1" export FEEDSTOCK_NAME=$(basename ${CIRCLE_PROJECT_REPONAME}) if [[ "${CIRCLE_PR_NUMBER:-}" == "" ]]; then export IS_PR_BUILD="False" diff --git a/.scripts/build_steps.sh b/.scripts/build_steps.sh index e21030ca10..f1d0c0856f 100755 --- a/.scripts/build_steps.sh +++ b/.scripts/build_steps.sh @@ -28,14 +28,15 @@ conda-build: pkgs_dirs: - ${FEEDSTOCK_ROOT}/build_artifacts/pkg_cache - /opt/conda/pkgs +solver: libmamba CONDARC +export CONDA_LIBMAMBA_SOLVER_NO_CHANNELS_FROM_INSTALLED=1 - -mamba install --update-specs --yes --quiet --channel conda-forge \ - conda-build pip boa conda-forge-ci-setup=3 -mamba update --update-specs --yes --quiet --channel conda-forge \ - conda-build pip boa conda-forge-ci-setup=3 +mamba install --update-specs --yes --quiet --channel conda-forge --strict-channel-priority \ + pip mamba conda-build boa conda-forge-ci-setup=4 +mamba update --update-specs --yes --quiet --channel conda-forge --strict-channel-priority \ + pip mamba conda-build boa conda-forge-ci-setup=4 # set up the condarc setup_conda_rc "${FEEDSTOCK_ROOT}" "${RECIPE_ROOT}" "${CONFIG_FILE}" @@ -53,6 +54,12 @@ if [[ -f "${FEEDSTOCK_ROOT}/LICENSE.txt" ]]; then cp "${FEEDSTOCK_ROOT}/LICENSE.txt" "${RECIPE_ROOT}/recipe-scripts-license.txt" fi +if [[ "${sha:-}" == "" ]]; then + pushd ${FEEDSTOCK_ROOT} + sha=$(git rev-parse HEAD) + popd +fi + if [[ "${BUILD_WITH_CONDA_DEBUG:-0}" == 1 ]]; then if [[ "x${BUILD_OUTPUT_ID:-}" != "x" ]]; then EXTRA_CB_OPTIONS="${EXTRA_CB_OPTIONS:-} --output-id ${BUILD_OUTPUT_ID}" @@ -66,7 +73,8 @@ if [[ "${BUILD_WITH_CONDA_DEBUG:-0}" == 1 ]]; then else conda mambabuild "${RECIPE_ROOT}" -m "${CI_SUPPORT}/${CONFIG}.yaml" \ --suppress-variables ${EXTRA_CB_OPTIONS:-} \ - --clobber-file "${CI_SUPPORT}/clobber_${CONFIG}.yaml" + --clobber-file "${CI_SUPPORT}/clobber_${CONFIG}.yaml" \ + --extra-meta flow_run_id="${flow_run_id:-}" remote_url="${remote_url:-}" sha="${sha:-}" ( startgroup "Uploading packages" ) 2> /dev/null diff --git a/.scripts/logging_utils.sh b/.scripts/logging_utils.sh index 57bc95c242..aff009f0c5 100644 --- a/.scripts/logging_utils.sh +++ b/.scripts/logging_utils.sh @@ -12,7 +12,7 @@ function startgroup { echo "##[group]$1";; travis ) echo "$1" - echo -en 'travis_fold:start:'"${1// /}"'\\r';; + echo -en 'travis_fold:start:'"${1// /}"'\r';; github_actions ) echo "::group::$1";; * ) @@ -28,7 +28,7 @@ function endgroup { azure ) echo "##[endgroup]";; travis ) - echo -en 'travis_fold:end:'"${1// /}"'\\r';; + echo -en 'travis_fold:end:'"${1// /}"'\r';; github_actions ) echo "::endgroup::";; esac diff --git a/.scripts/run_docker_build.sh b/.scripts/run_docker_build.sh index 8289ddf992..ffde9ae806 100755 --- a/.scripts/run_docker_build.sh +++ b/.scripts/run_docker_build.sh @@ -91,6 +91,9 @@ docker run ${DOCKER_RUN_ARGS} \ -e CPU_COUNT \ -e BUILD_WITH_CONDA_DEBUG \ -e BUILD_OUTPUT_ID \ + -e flow_run_id \ + -e remote_url \ + -e sha \ -e BINSTAR_TOKEN \ "${DOCKER_IMAGE}" \ bash \ diff --git a/.scripts/run_osx_build.sh b/.scripts/run_osx_build.sh index fc0a720338..e574a0d243 100755 --- a/.scripts/run_osx_build.sh +++ b/.scripts/run_osx_build.sh @@ -22,11 +22,13 @@ bash $MINIFORGE_FILE -b -p ${MINIFORGE_HOME} source ${MINIFORGE_HOME}/etc/profile.d/conda.sh conda activate base +export CONDA_SOLVER="libmamba" +export CONDA_LIBMAMBA_SOLVER_NO_CHANNELS_FROM_INSTALLED=1 -mamba install --update-specs --quiet --yes --channel conda-forge \ - conda-build pip boa conda-forge-ci-setup=3 -mamba update --update-specs --yes --quiet --channel conda-forge \ - conda-build pip boa conda-forge-ci-setup=3 +mamba install --update-specs --quiet --yes --channel conda-forge --strict-channel-priority \ + pip mamba conda-build boa conda-forge-ci-setup=4 +mamba update --update-specs --yes --quiet --channel conda-forge --strict-channel-priority \ + pip mamba conda-build boa conda-forge-ci-setup=4 @@ -45,6 +47,10 @@ else echo -e "\n\nNot mangling homebrew as we are not running in CI" fi +if [[ "${sha:-}" == "" ]]; then + sha=$(git rev-parse HEAD) +fi + echo -e "\n\nRunning the build setup script." source run_conda_forge_build_setup @@ -55,7 +61,6 @@ source run_conda_forge_build_setup echo -e "\n\nMaking the build clobber file" make_build_number ./ ./conda.recipe ./.ci_support/${CONFIG}.yaml - if [[ -f LICENSE.txt ]]; then cp LICENSE.txt "conda.recipe/recipe-scripts-license.txt" fi @@ -71,9 +76,11 @@ if [[ "${BUILD_WITH_CONDA_DEBUG:-0}" == 1 ]]; then # Drop into an interactive shell /bin/bash else + conda mambabuild ./conda.recipe -m ./.ci_support/${CONFIG}.yaml \ --suppress-variables ${EXTRA_CB_OPTIONS:-} \ - --clobber-file ./.ci_support/clobber_${CONFIG}.yaml + --clobber-file ./.ci_support/clobber_${CONFIG}.yaml \ + --extra-meta flow_run_id="$flow_run_id" remote_url="$remote_url" sha="$sha" ( startgroup "Uploading packages" ) 2> /dev/null diff --git a/sparta/CMakeLists.txt b/sparta/CMakeLists.txt index 5b146479bc..c0845de6e4 100644 --- a/sparta/CMakeLists.txt +++ b/sparta/CMakeLists.txt @@ -61,6 +61,7 @@ list (APPEND SourceCppFiles src/SingleUpdateReport.cpp src/SINodeHierarchy.cpp src/StatisticDef.cpp + src/StatisticInstance.cpp src/StatisticsArchives.cpp src/StatisticsStreams.cpp src/StatsMapping.cpp diff --git a/sparta/python/sparta_support/module_sparta.hpp b/sparta/python/sparta_support/module_sparta.hpp index 3b1661d6fe..92eb34b3e6 100644 --- a/sparta/python/sparta_support/module_sparta.hpp +++ b/sparta/python/sparta_support/module_sparta.hpp @@ -865,5 +865,3 @@ class PortWrapper{ return inport; } }; - -#pragma once diff --git a/sparta/sparta/report/Report.hpp b/sparta/sparta/report/Report.hpp index a58aacfeee..3149a0b106 100644 --- a/sparta/sparta/report/Report.hpp +++ b/sparta/sparta/report/Report.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "sparta/statistics/StatisticInstance.hpp" @@ -54,11 +55,6 @@ namespace sparta { public: - /*! - * \brief Type for storing each stat added - */ - typedef std::pair stat_pair_t; - /*! * \brief Function pointer for deciding whether to make a subreport * during recursive addition of statistics/counters from a subtree @@ -225,7 +221,7 @@ namespace sparta } // Copy StatisticInstances - for(const stat_pair_t& sp : rhp.stats_){ + for(const statistics::stat_pair_t& sp : rhp.stats_){ add(*sp.second, sp.first); } } @@ -258,17 +254,6 @@ namespace sparta } } - /*! - * \brief Virtual Destructor - */ - virtual ~Report() - { - // Delete all StatisticInstances - for(auto& sp : stats_){ - delete sp.second; - } - } - //////////////////////////////////////////////////////////////////////// //! @} @@ -289,14 +274,10 @@ namespace sparta sr.setParent_(this); } - // Clear StatisticInstancesx - for(auto& sp : stats_){ - delete sp.second; - } stats_.clear(); // Clear local stats // Copy StatisticInstances - for(const stat_pair_t& sp : rhp.stats_){ + for(const statistics::stat_pair_t& sp : rhp.stats_){ add(*sp.second, sp.first); } start_tick_ = rhp.start_tick_; @@ -420,26 +401,7 @@ namespace sparta * by another item immediately in this report (not the name of a * subreport or item in a subreport) */ - StatAdder add(const StatisticInstance& si, const std::string& name="") { - if(name != "" && stat_names_.find(name) != stat_names_.end()){ - throw SpartaException("There is already a statistic instance in this Report (") - << getName() << ") named \"" << name << "\" pointing to " - << stat_names_.find(name)->second->getLocation() - << " and the new stat would be pointing to a StatisticInstance " - << si.getExpressionString(); - } - - // Track a new stat with helpful exception wrapping - addField_(name, si); - - if(name != ""){ - stat_names_[name] = stats_.back().second; - } - - addSubStatistics_(&si); - - return StatAdder(*this); - } + StatAdder add(const StatisticInstance& si, const std::string& name=""); /*! * \brief Moves an existing statistic instance into this Report @@ -452,102 +414,28 @@ namespace sparta * by another item immediately in this report (not the name of a * subreport or item in a subreport) */ - StatAdder add(StatisticInstance&& si, const std::string& name="") { - if(name != "" && stat_names_.find(name) != stat_names_.end()){ - throw SpartaException("There is already a statistic instance in this Report (") - << getName() << ") named \"" << name << "\" pointing to " - << stat_names_.find(name)->second->getLocation() - << " and the new stat would be pointing to a StatisticInstance " - << si.getExpressionString(); - } + StatAdder add(StatisticInstance&& si, const std::string& name=""); - // Track a new stat with helpful exception wrapping - addField_(name, si); - - if(name != ""){ - stat_names_[name] = stats_.back().second; - } - - addSubStatistics_(&si); - - return StatAdder(*this); - } - - StatAdder add(StatisticDef* sd, const std::string& name="") { - sparta_assert(sd); - if(name != "" && stat_names_.find(name) != stat_names_.end()){ - throw SpartaException("There is already a statistic instance in this Report (") - << getName() << ") named \"" << name << "\" pointing to " - << stat_names_.find(name)->second->getLocation() - << " and the new stat would be the the statistic def at " - << sd->getLocation() << " with the expression \"" - << sd->getExpression() << "\""; - } - - // Track a new stat with helpful exception wrapping - addField_(name, sd); - - if(name != ""){ - stat_names_[name] = stats_.back().second; - } - - return StatAdder(*this); - } - - StatAdder add(CounterBase* ctr, const std::string& name="") { - sparta_assert(ctr); - if(name != "" && stat_names_.find(name) != stat_names_.end()){ - throw SpartaException("There is already a statistic instance in this Report (") - << getName() << ") named \"" << name << "\" pointing to " - << stat_names_.find(name)->second->getLocation() - << " and the new stat would be the counter to " - << ctr->getLocation(); - } - - // Track a new stat with helpful exception wrapping - addField_(name, ctr); - - if(name != ""){ - stat_names_[name] = stats_.back().second; - } - - return StatAdder(*this); - } - - StatAdder add(TreeNode* n, const std::string& name="") { - sparta_assert(n); - if(name != "" && stat_names_.find(name) != stat_names_.end()){ - throw SpartaException("There is already a statistic instance in this Report (") - << getName() << ") named \"" << name << "\" pointing to " - << stat_names_.find(name)->second->getLocation() - << " and the new stat would be the node to " - << n->getLocation(); - } - - // Track a new stat with helpful exception wrapping - addField_(name, n); - - if(name != ""){ - stat_names_[name] = stats_.back().second; - } + /*! + * \brief Add a StatisticDef to the report + * \param sd Pointer to the StatisticDef + * \param name Name of this stat + */ + StatAdder add(StatisticDef* sd, const std::string& name=""); - return StatAdder(*this); - } + /*! + * \brief Add a Counter type to the report + * \param ctr The counter to add + * \param name The name of the counter + */ + StatAdder add(CounterBase* ctr, const std::string& name=""); - StatAdder addSubStats(StatisticDef * n, const std::string & name_prefix) { - sparta_assert(auto_expand_context_counter_stats_, - "Call to Report::addSubStats(StatisticDef*, name_prefix) is not " - "allowed since ContextCounter auto-expansion is disabled. Enable " - "this by calling Report::enableContextCounterAutoExpansion()"); - for (const auto & sub_stat : n->getSubStatistics()) { - TreeNode * sub_stat_node = const_cast(sub_stat.getNode()); - const std::string prefix = - !name_prefix.empty() ? name_prefix : sub_stat_node->getLocation(); - const std::string sub_stat_name = prefix + "." + sub_stat.getName(); - add(sub_stat_node, sub_stat_name); - } - return StatAdder(*this); - } + /*! + * \brief Add a TreeNode type that represents a counter/stat derivative + * \param n The TreeNode to add + * \param name The name of the item in the report + */ + StatAdder add(TreeNode* n, const std::string& name=""); /*! * \brief Add a single Expression parsed at the current context for this @@ -561,32 +449,7 @@ namespace sparta * \throw SpartaException if this Report currently has no context node * (see getContext). Throws if the expression could not be evaluated */ - StatAdder add(const std::string& expression, const std::string& name="") { - if(name != "" && stat_names_.find(name) != stat_names_.end()){ - throw SpartaException("There is already a statistic instance in this Report (") - << getName() << ") named \"" << name << "\" pointing to " - << stat_names_.find(name)->second->getLocation() - << " and the new stat would be the expression \"" - << expression << "\""; - } - if(nullptr == context_){ - throw SpartaException("This report currently has no context. To add an item by " - "expression \"") - << expression << "\", specify a context TreeNode using setContext as the " - "context from which TreeNodes can be searched for"; - } - - if(TreeNodePrivateAttorney::hasChild(context_, expression)){ - // Add as a TreeNode statistic - add(TreeNodePrivateAttorney::getChild(context_, expression), name); - }else{ - statistics::expression::Expression expr(expression, context_); - StatisticInstance si(std::move(expr)); - add(std::move(si), name); - } - - return StatAdder(*this); - } + StatAdder add(const std::string& expression, const std::string& name=""); /*! * \brief Adds any number of TreeNode items to this Report. Type of node @@ -600,12 +463,14 @@ namespace sparta * \warning If this method throws, only some items specified by the * pattern. There is no rollback of partial completion */ - StatAdder add(const std::vector& nv) { - for(TreeNode* n : nv){ - add(n); - } - return StatAdder(*this); - } + StatAdder add(const std::vector& nv); + + /*! + * \brief Add sub statisitc + * \param n The sub statistic to add + * \param name The name of the stat for the report + */ + StatAdder addSubStats(StatisticDef * n, const std::string & name_prefix); /*! * \brief By default, statistics reset their internal offsets whenever @@ -614,14 +479,7 @@ namespace sparta * update, and instead always substract the statistic value that was * present at the time of report start. */ - void accumulateStats() const { - for (const auto & stat : stats_) { - stat.second->accumulateStatistic(); - } - for (const auto & sr : getSubreports()) { - sr.accumulateStats(); - } - } + void accumulateStats() const; /*! * \brief Tell this report if ContextCounter stats should be auto- @@ -754,7 +612,7 @@ namespace sparta subreps_.push_back(sr); // Copies subreps_.back().setParent_(this); } - for(const stat_pair_t& sp : r.stats_){ + for(const statistics::stat_pair_t& sp : r.stats_){ add(*sp.second, sp.first); } } @@ -906,7 +764,7 @@ namespace sparta * \throw SpartaException if idx is out of bounds */ StatisticInstance& getStatistic(size_t idx) { - return *stats_.at(idx).second; + return *(stats_.at(idx).second); } /*! @@ -919,14 +777,25 @@ namespace sparta * \see hasStatistic */ StatisticInstance& getStatistic(const std::string& name) { - auto itr = stat_names_.find(name); - if(itr == stat_names_.end()){ + auto name_itr = stat_names_.find(name); + if(name_itr == stat_names_.end()){ throw SpartaException("Could not find statistic named \"") << name << "\" in report \"" << getName() << "\""; } - return *(itr->second); + // Get the statistic from the vector pair + auto itr = std::find_if(stats_.begin(), stats_.end(), + [name] (const auto & p) -> bool { + return name == p.first; + }); + sparta_assert(itr != stats_.end()); + return *(*itr).second; } + /*! + * \brief Return true if this report has the given stat name + * \param name The name of the stat to look for + * \return true has stat, false otherwise + */ bool hasStatistic(const std::string& name) const { auto itr = stat_names_.find(name); return (itr != stat_names_.end()); @@ -970,7 +839,7 @@ namespace sparta * \brief Gets the set of statistic instances immediately contained in * this Report */ - const std::vector& getStatistics() const { + const statistics::StatisticPairs& getStatistics() const { return stats_; } @@ -1333,7 +1202,7 @@ namespace sparta indent << INDENT_STR; - for(const stat_pair_t& si : stats_){ + for(const statistics::stat_pair_t& si : stats_){ o << indent.str(); if(si.first != ""){ // Print "custom_name = value" @@ -1348,7 +1217,7 @@ namespace sparta o << formatNumber(val); // Could print the expression after the value - //o << " # " << si.second->getExpressionString(); + //o << " # " << si.second.getExpressionString(); o << std::endl; } @@ -1392,9 +1261,8 @@ namespace sparta template void addField_(const std::string& name, T si_arg) { try{ - StatisticInstance * si; - stats_.emplace_back(name, si = new StatisticInstance(si_arg)); - si->setContext(scheduler_); + stats_.emplace_back(name, new StatisticInstance(si_arg)); + stats_.back().second->setContext(scheduler_); }catch(SpartaException& ex){ ex << " StatisticInstance would have been named \"" << name << "\""; throw; @@ -1418,7 +1286,7 @@ namespace sparta //Update mapping from statistic definition to substatistic instance const StatisticDef * stat_def = parent_stat->getStatisticDef(); if (stat_def != nullptr) { - sub_statistics_[stat_def].emplace_back(stats_.back().second); + sub_statistics_[stat_def].emplace_back(stats_.back().second.get()); } } } @@ -1601,12 +1469,12 @@ namespace sparta * \note Anything removed from this list needs to be removed from * stat_names_ as well. */ - std::vector stats_; + statistics::StatisticPairs stats_; /*! * \brief Map of string identifiers to statistics in the stats_ vector */ - std::map stat_names_; + std::set stat_names_; /*! * \brief Tick on which this statistic started (exclusive) diff --git a/sparta/sparta/report/db/ReportNodeHierarchy.hpp b/sparta/sparta/report/db/ReportNodeHierarchy.hpp index acd55265da..82a12bff7f 100644 --- a/sparta/sparta/report/db/ReportNodeHierarchy.hpp +++ b/sparta/sparta/report/db/ReportNodeHierarchy.hpp @@ -289,7 +289,7 @@ class ReportNodeHierarchy const std::string name = !stat.first.empty() ? stat.first : stat.second->getLocation(); - const StatisticInstance * stat_inst = stat.second; + const StatisticInstance * stat_inst = stat.second.get(); const StatisticDef * def = stat_inst->getStatisticDef(); const CounterBase * ctr = stat_inst->getCounter(); const ParameterBase * prm = stat_inst->getParameter(); @@ -321,7 +321,7 @@ class ReportNodeHierarchy leftmost_si_index, is_leaf, obj_mgr); - createLeafSIMetadata_(stat.second, + createLeafSIMetadata_(stat.second.get(), *leaf_report_node, obj_mgr); @@ -337,7 +337,7 @@ class ReportNodeHierarchy leaf_report_node->getId()); } - unordered_si_ids_[stat.second] = leaf_report_node->getId(); + unordered_si_ids_[stat.second.get()] = leaf_report_node->getId(); } for (const auto & sr : subreport.getSubreports()) { @@ -397,7 +397,7 @@ class ReportNodeHierarchy } const simdb::DatabaseID si_node_id = si_node_iter->second; - auto parent_si_node_iter = unordered_si_ids_.find(stat.second); + auto parent_si_node_iter = unordered_si_ids_.find(stat.second.get()); if (parent_si_node_iter == unordered_si_ids_.end()) { continue; } @@ -482,4 +482,3 @@ class ReportNodeHierarchy } // namespace statistics } // namespace sparta - diff --git a/sparta/sparta/report/format/BasicHTML.hpp b/sparta/sparta/report/format/BasicHTML.hpp index abb1d50328..89c2084916 100644 --- a/sparta/sparta/report/format/BasicHTML.hpp +++ b/sparta/sparta/report/format/BasicHTML.hpp @@ -314,7 +314,7 @@ class BasicHTML : public BaseOstreamFormatter // whole number portion of any output for the purpose of decimal // alignment size_t val_decimal_alignment = 0; - for(const Report::stat_pair_t& si : r->getStatistics()){ + for(const statistics::stat_pair_t& si : r->getStatistics()){ size_t leading_space = 0; size_t decimal_pos = 0; formatDecimalAlignedNum(si.second->getValue(), @@ -325,7 +325,7 @@ class BasicHTML : public BaseOstreamFormatter val_decimal_alignment = std::max(val_decimal_alignment, decimal_pos); } - const std::vector & stats = r->getStatistics(); + const statistics::StatisticPairs & stats = r->getStatistics(); uint32_t num_rows = stats.size() / num_stat_columns; if ((stats.size() % num_stat_columns) != 0) { num_rows++; @@ -345,7 +345,7 @@ class BasicHTML : public BaseOstreamFormatter continue; } - const Report::stat_pair_t & si = stats[stat_idx]; + const statistics::stat_pair_t & si = stats[stat_idx]; // Compute expression with < and > characters escaped for HTML // Be sure to omit range and fully resolve all sub-expressions @@ -438,4 +438,3 @@ class BasicHTML : public BaseOstreamFormatter } // namespace format } // namespace report } // namespace sparta - diff --git a/sparta/sparta/report/format/CSV.hpp b/sparta/sparta/report/format/CSV.hpp index 4e3a47f1bf..4ca269f526 100644 --- a/sparta/sparta/report/format/CSV.hpp +++ b/sparta/sparta/report/format/CSV.hpp @@ -15,6 +15,7 @@ #include "sparta/report/format/ReportHeader.hpp" #include "sparta/utils/SpartaException.hpp" #include "sparta/utils/SpartaAssert.hpp" +#include "sparta/statistics/Expression.hpp" // stat_pair_t namespace sparta { @@ -188,7 +189,7 @@ class CSV : public BaseOstreamFormatter out << ","; // Insert comma following last data (which has no trailing comma) before the first value here. } - const Report::stat_pair_t& si = *itr; + const statistics::stat_pair_t& si = *itr; if(si.first != ""){ // Print name = value out << prefix + si.first; @@ -269,7 +270,7 @@ class CSV : public BaseOstreamFormatter } // Print the value - const Report::stat_pair_t& si = *itr; + const statistics::stat_pair_t& si = *itr; out << Report::formatNumber(si.second->getValue()); wrote_value = true; // 1 or more values written here @@ -327,4 +328,3 @@ inline std::ostream& operator<< (std::ostream& out, CSV & f) { } // namespace format } // namespace report } // namespace sparta - diff --git a/sparta/sparta/report/format/Gnuplot.hpp b/sparta/sparta/report/format/Gnuplot.hpp index ab72c4564c..0d8b2ad684 100644 --- a/sparta/sparta/report/format/Gnuplot.hpp +++ b/sparta/sparta/report/format/Gnuplot.hpp @@ -98,7 +98,7 @@ class Gnuplot : public BaseOstreamFormatter */ int writeGPLTHeader_(std::ostream& out, const Report* r, int idx) const { - for (const Report::stat_pair_t& si : r->getStatistics()) { + for (const statistics::stat_pair_t& si : r->getStatistics()) { out << "# (" << idx++ << ")"; if (si.first != "") { out << " " << si.first; @@ -118,7 +118,7 @@ class Gnuplot : public BaseOstreamFormatter */ void writeData_(std::ostream& out, const Report* r) const { - for (const Report::stat_pair_t& si : r->getStatistics()) { + for (const statistics::stat_pair_t& si : r->getStatistics()) { out << si.second->getValue() << " "; } @@ -137,4 +137,3 @@ inline std::ostream& operator<< (std::ostream& out, Gnuplot & f) { } // namespace format } // namespace report } // namespace sparta - diff --git a/sparta/sparta/report/format/JSON_detail.hpp b/sparta/sparta/report/format/JSON_detail.hpp index a88f37106c..ecb7e49d89 100644 --- a/sparta/sparta/report/format/JSON_detail.hpp +++ b/sparta/sparta/report/format/JSON_detail.hpp @@ -212,7 +212,7 @@ class JSON_detail : public BaseOstreamFormatter local_name = p_name + "." + flattenReportName(r->getName()); } - auto extract_stat = [&local_name](const Report::stat_pair_t & si) { + auto extract_stat = [&local_name](const statistics::stat_pair_t & si) { std::string full_name = local_name + "." + si.first; std::string desc = si.second->getDesc(false); boost::replace_all(desc, "\"", "\\\""); @@ -235,9 +235,9 @@ class JSON_detail : public BaseOstreamFormatter std::set dont_print_these; std::set db_dont_print_these; - for (const Report::stat_pair_t& si : r->getStatistics()) { + for (const statistics::stat_pair_t& si : r->getStatistics()) { if(si.first != ""){ - const StatisticInstance * stat_inst = si.second; + const StatisticInstance * stat_inst = si.second.get(); const StatisticDef * stat_defn = si.second->getStatisticDef(); const CounterBase * ctr = si.second->getCounter(); const ParameterBase * prm = si.second->getParameter(); @@ -328,4 +328,3 @@ inline std::ostream& operator<< (std::ostream& out, JSON_detail & f) { } // namespace format } // namespace report } // namespace sparta - diff --git a/sparta/sparta/report/format/PythonDict.hpp b/sparta/sparta/report/format/PythonDict.hpp index 8060e624cf..646b21433b 100644 --- a/sparta/sparta/report/format/PythonDict.hpp +++ b/sparta/sparta/report/format/PythonDict.hpp @@ -108,7 +108,7 @@ class PythonDict : public BaseOstreamFormatter out << "\"" << leaf_name << "\"" << ": {" ; int elements_=0; - for (const Report::stat_pair_t& si : r->getStatistics()) { + for (const statistics::stat_pair_t& si : r->getStatistics()) { if(si.first != ""){ if(elements_ > 0){ out << ", "; diff --git a/sparta/sparta/report/format/Text.hpp b/sparta/sparta/report/format/Text.hpp index 66533345fc..5309af12f3 100644 --- a/sparta/sparta/report/format/Text.hpp +++ b/sparta/sparta/report/format/Text.hpp @@ -216,7 +216,7 @@ class Text : public BaseOstreamFormatter indent += INDENT_STR.size() + ADDITIONAL_STAT_INDENT.size(); - for(const Report::stat_pair_t& si : r->getStatistics()){ + for(const statistics::stat_pair_t& si : r->getStatistics()){ if(si.first != ""){ // Stat name rcol = std::max(rcol, indent + si.first.size()); @@ -318,7 +318,7 @@ class Text : public BaseOstreamFormatter if(val_col_after_indent >= indent.str().size()){ val_col_after_indent -= indent.str().size(); } - for(const Report::stat_pair_t& si : r->getStatistics()){ + for(const statistics::stat_pair_t& si : r->getStatistics()){ out << indent.str(); std::stringstream name; if(val_col_ > 0){ @@ -471,4 +471,3 @@ inline std::ostream& operator<< (std::ostream& out, Text & f) { } // namespace format } // namespace report } // namespace sparta - diff --git a/sparta/sparta/simulation/ClockManager.hpp b/sparta/sparta/simulation/ClockManager.hpp index 956c732f32..36dfd9f4bc 100644 --- a/sparta/sparta/simulation/ClockManager.hpp +++ b/sparta/sparta/simulation/ClockManager.hpp @@ -170,6 +170,4 @@ class RootTreeNode; }; // class ClockManager std::ostream &operator<<(std::ostream &os, const ClockManager &m); -} - -#pragma once +}; diff --git a/sparta/sparta/statistics/Expression.hpp b/sparta/sparta/statistics/Expression.hpp index ab5b21c791..c2ebfc7156 100644 --- a/sparta/sparta/statistics/Expression.hpp +++ b/sparta/sparta/statistics/Expression.hpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "sparta/simulation/TreeNode.hpp" #include "sparta/utils/SpartaAssert.hpp" @@ -14,21 +16,31 @@ namespace sparta { - class StatisticInstance; - class Clock; +class StatisticInstance; +class Clock; - /*! - * \brief Namespace containing methods for computing and generating - * statistical information using instrumentation extracted from sparta - * structures such as Counters - */ - namespace statistics { +/*! + * \brief Namespace containing methods for computing and generating + * statistical information using instrumentation extracted from sparta + * structures such as Counters + */ +namespace statistics { - /*! - * \brief Namespace containing methods for parsing, building, and - * evaluating statistical expressions in sparta - */ - namespace expression { +/*! + * \brief Type for storing each stat added + */ +typedef std::pair> stat_pair_t; + +/*! + * \brief Type for storing a list of stat_pair_t + */ +typedef std::vector StatisticPairs; + +/*! + * \brief Namespace containing methods for parsing, building, and + * evaluating statistical expressions in sparta + */ +namespace expression { /*! * \brief Expression container/builder. Contains a single ExpressionNode @@ -66,10 +78,7 @@ class Expression /*! * \brief Constructs an expression containing no content */ - Expression() - { - //std::cout << "Construction " << this << std::endl; - } + Expression() = default; /*! * \brief Copy Constructor @@ -101,6 +110,16 @@ class Expression Expression(const std::string& expression, TreeNode* context); + /*! + * \brief Construct with string expression + * \param expr String containing an arithmetic expression + * \param context TreeNode from which variables in the expression + * \param report_si Previously defined StatisticInstances in the report + */ + Expression(const std::string& expression, + TreeNode* context, + const StatisticPairs&report_si); + /*! * \brief Construct with string expression * \param expr String containing an arithmetic expression @@ -546,7 +565,8 @@ class Expression */ void parse_(const std::string& expression, TreeNode* context, - std::vector& already_used); + std::vector& already_used, + const StatisticPairs&report_si); }; inline Expression::Expression(double d) : @@ -778,5 +798,3 @@ inline std::ostream& operator<<(std::ostream& o, const sparta::statistics::expre } // namespace expression } // namespace statistics } // namespace sparta - -#pragma once diff --git a/sparta/sparta/statistics/ExpressionGrammar.hpp b/sparta/sparta/statistics/ExpressionGrammar.hpp index 48e2f6c74c..5678f8cbfe 100644 --- a/sparta/sparta/statistics/ExpressionGrammar.hpp +++ b/sparta/sparta/statistics/ExpressionGrammar.hpp @@ -55,7 +55,7 @@ class ExpressionGrammar : * \brief Constructor * \param n TreeNode context for evaluating builtin variables * \param used TreeNodes which cannot be variables because they have - * already been used by an expression containing this one + * already been used by an expression containing this one */ builtin_vars_(sparta::TreeNode* n, std::vector& used); @@ -73,10 +73,12 @@ class ExpressionGrammar : * \brief Constructor * \param n TreeNode context for evaluating dynamic variables * \param used TreeNodes which cannot be variables because they have - * already been used by an expression containing this one + * already been used by an expression containing this one + * \param report_si Exising report statistic instances */ variable_(sparta::TreeNode* n, - std::vector& used); + std::vector& used, + const StatisticPairs & report_si); qi::rule& already_used); + std::vector& already_used, + const StatisticPairs &report_si); }; // struct grammar @@ -218,5 +223,3 @@ class ExpressionGrammar : } // namespace expression } // namespace statistics } // namespace sparta - -#pragma once diff --git a/sparta/sparta/statistics/ExpressionNode.hpp b/sparta/sparta/statistics/ExpressionNode.hpp index da5b0a9f03..5ae29733f3 100644 --- a/sparta/sparta/statistics/ExpressionNode.hpp +++ b/sparta/sparta/statistics/ExpressionNode.hpp @@ -134,5 +134,3 @@ class ExpressionNode } // namespace expression } // namespace statistics } // namespace sparta - -#pragma once diff --git a/sparta/sparta/statistics/ExpressionNodeTypes.hpp b/sparta/sparta/statistics/ExpressionNodeTypes.hpp index 68d4a24944..67e74a735d 100644 --- a/sparta/sparta/statistics/ExpressionNodeTypes.hpp +++ b/sparta/sparta/statistics/ExpressionNodeTypes.hpp @@ -659,5 +659,3 @@ struct TernaryFunction : public ExpressionNode } // namespace expression } // namespace statistics } // namespace sparta - -#pragma once diff --git a/sparta/sparta/statistics/ExpressionNodeVariables.hpp b/sparta/sparta/statistics/ExpressionNodeVariables.hpp index aa00d7cc07..cbf54bbc3f 100644 --- a/sparta/sparta/statistics/ExpressionNodeVariables.hpp +++ b/sparta/sparta/statistics/ExpressionNodeVariables.hpp @@ -306,5 +306,3 @@ struct ReferenceVariable : public ExpressionNode } // namespace expression } // namespace statistics } // namespace sparta - -#pragma once diff --git a/sparta/sparta/statistics/ExpressionParser.hpp b/sparta/sparta/statistics/ExpressionParser.hpp index bed40f27ba..994e9db9d3 100644 --- a/sparta/sparta/statistics/ExpressionParser.hpp +++ b/sparta/sparta/statistics/ExpressionParser.hpp @@ -42,12 +42,16 @@ class ExpressionParser * \brief Construct a parser in the context of a specific TreeNode * \param n TreeNode context for parsing expressions using this parser * \param already_used Nodes which have been used by an expression - * containing this. These Nodes are off-limits for parsing here and should - * throw an Exception if encountered + * containing this. These Nodes are off-limits + * for parsing here and should throw an + * Exception if encountered + * \param report_si StatisticInstance objects already created from + * previous expressions that now live in the report */ ExpressionParser(TreeNode* n, - std::vector& already_used) : - grammar_(n, already_used) + std::vector& already_used, + const StatisticPairs &report_si) : + grammar_(n, already_used, report_si) { } Expression&& parse(const std::string& input) { @@ -71,5 +75,3 @@ class ExpressionParser } // namespace expression } // namespace statistics } // namespace sparta - -#pragma once diff --git a/sparta/sparta/statistics/StatisticInstance.hpp b/sparta/sparta/statistics/StatisticInstance.hpp index 7219ad69e6..8c189c8e27 100644 --- a/sparta/sparta/statistics/StatisticInstance.hpp +++ b/sparta/sparta/statistics/StatisticInstance.hpp @@ -1,4 +1,4 @@ -// -*- C++ -*- +// -*- C++ -*- /*! * \file StatisticInstance.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "sparta/kernel/Scheduler.hpp" #include "sparta/statistics/StatisticDef.hpp" @@ -28,6 +29,7 @@ namespace sparta //Forward declarations related to SimDB class StatInstValueLookup; class StatInstRowIterator; + using statistics::expression::Expression; /*! * \brief Exception indicating that the range of a StatisticInstance @@ -96,17 +98,7 @@ namespace sparta /*! * \brief Private Default constructor */ - StatisticInstance() : - sdef_(nullptr), - ctr_(nullptr), - par_(nullptr), - start_tick_(0), - end_tick_(Scheduler::INDEFINITE), - initial_(NAN), - result_(NAN), - sub_statistics_() - { - } + StatisticInstance() = default; /*! * \brief Private constructor. Exactly one of the pointers contained @@ -122,91 +114,7 @@ namespace sparta const CounterBase* ctr, const ParameterBase* par, const TreeNode* n, - std::vector* used) : - StatisticInstance() - { - const StatisticDef* stat_def; - if(!sd){ - stat_def = dynamic_cast(n); - - sdef_ = stat_def; - }else{ - sdef_ = stat_def = sd; - } - const CounterBase* counter; - if(!ctr){ - counter = dynamic_cast(n); - ctr_ = counter; - }else{ - ctr_ = counter = ctr; - } - const ParameterBase* param; - if(!par){ - param = dynamic_cast(n); - par_ = param; - }else{ - par_ = param = par; - } - - // Find the non-null argument - const TreeNode* node = n; - if(!node){ - node = sd; - if(!node){ - node = ctr; - if(!node){ - node = par; - sparta_assert(node, - "StatisticInstance was constructed with all null arguments. " - "This is not allowed"); - } - } - } - sparta_assert(int(nullptr != sdef_) + int(nullptr != ctr_) + int(nullptr != par_) == 1, - "Can only instantiate a StatisticInstance with either a StatisticDef, " - "a Counter, or a Parameter of any numeric type. Got Node: \"" << node->getLocation() - << "\". This node is not a stat, counter, or parameter."); - - // Get the Scheduler as context - if(node->getClock()) { - scheduler_ = n->getClock()->getScheduler(); - } - - if(sdef_){ - node_ref_ = stat_def->getWeakPtr(); - - std::vector* local_used_ptr; - std::vector temp_used; - if(!used){ - local_used_ptr = &temp_used; - }else{ - local_used_ptr = used; - } - stat_expr_ = sdef_->realizeExpression(*local_used_ptr); - if(stat_expr_.hasContent() == false){ - throw SpartaException("Cannot construct StatisticInstance based on node ") - << stat_def->getLocation() << " because its expression: " - << stat_def->getExpression() << " parsed to an empty expression"; - } - const auto & sub_stats_info = sdef_->getSubStatistics(); - for (auto & sub_stat_creation_info : sub_stats_info) { - addSubStatistic_(sub_stat_creation_info); - } - }else if(ctr_){ - node_ref_ = counter->getWeakPtr(); - }else if(par_){ - node_ref_ = param->getWeakPtr(); - }else{ - // Should not have been able to call constructor without 1 or - // the 3 args being non-null - throw SpartaException("Cannot instantiate a StatisticInstance without a statistic " - "definition or counter pointer"); - } - - start(); - - sparta_assert(false == node_ref_.expired()); - } + std::vector* used); public: @@ -221,10 +129,8 @@ namespace sparta * call. The Expression might or might not know the context for the Scheduler */ StatisticInstance(statistics::expression::Expression&& expr) : - StatisticInstance() - { - stat_expr_ = expr; - } + stat_expr_(expr) + { } /*! * \brief Construct with a StatisticDef or Counter as a TreeNode* @@ -250,106 +156,13 @@ namespace sparta * in this constructor) */ StatisticInstance(std::shared_ptr & calculator, - std::vector& used) : - StatisticInstance(nullptr, nullptr, nullptr, calculator->getNode(), &used) - { - // Creating SI's using this constructor essentially means that you - // want to perform your own StatisticDef calculation, the math/logic - // of which is too complicated or cumbersome to express in a single - // std::string. Counter and Parameter SI's are simple enough that - // SPARTA will not let you try to override their SI value calculation. - // StatisticDef's and their subclasses are the exception. - sparta_assert(sdef_); - sparta_assert(ctr_ == nullptr); - sparta_assert(par_ == nullptr); - user_calculated_si_value_ = calculator; - } + std::vector& used); //! \brief Copy Constructor - StatisticInstance(const StatisticInstance& rhp) : - node_ref_(rhp.node_ref_), - sdef_(rhp.sdef_), - ctr_(rhp.ctr_), - par_(rhp.par_), - stat_expr_(rhp.stat_expr_), - start_tick_(rhp.start_tick_), - end_tick_(rhp.end_tick_), - scheduler_(rhp.scheduler_), - initial_(rhp.initial_), - result_(rhp.result_), - sub_statistics_(rhp.sub_statistics_), - user_calculated_si_value_(rhp.user_calculated_si_value_), - direct_lookup_si_value_(rhp.direct_lookup_si_value_), - provided_metadata_(rhp.provided_metadata_) - { - if (rhp.provided_location_.isValid()) { - provided_location_ = rhp.provided_location_.getValue(); - } - if (rhp.provided_description_.isValid()) { - provided_description_ = rhp.provided_description_.getValue(); - } - if (rhp.provided_expr_string_.isValid()) { - provided_expr_string_ = rhp.provided_expr_string_.getValue(); - } - if (rhp.provided_value_semantic_.isValid()) { - provided_value_semantic_ = rhp.provided_value_semantic_.getValue(); - } - if (rhp.provided_visibility_.isValid()) { - provided_visibility_ = rhp.provided_visibility_.getValue(); - } - if (rhp.provided_class_.isValid()) { - provided_class_ = rhp.provided_class_.getValue(); - } - } + StatisticInstance(const StatisticInstance& rhp); //! \brief Move Constructor - StatisticInstance(StatisticInstance&& rhp) : - node_ref_(std::move(rhp.node_ref_)), - sdef_(rhp.sdef_), - ctr_(rhp.ctr_), - par_(rhp.par_), - stat_expr_(std::move(rhp.stat_expr_)), - start_tick_(rhp.start_tick_), - end_tick_(rhp.end_tick_), - scheduler_(rhp.scheduler_), - initial_(rhp.initial_), - result_(rhp.result_), - sub_statistics_(std::move(rhp.sub_statistics_)), - user_calculated_si_value_(std::move(rhp.user_calculated_si_value_)), - direct_lookup_si_value_(std::move(rhp.direct_lookup_si_value_)), - provided_metadata_(std::move(rhp.provided_metadata_)) - { - rhp.sdef_ = nullptr; - rhp.ctr_ = nullptr; - rhp.par_ = nullptr; - rhp.result_ = NAN; - - if (rhp.provided_location_.isValid()) { - provided_location_ = rhp.provided_location_.getValue(); - } - if (rhp.provided_description_.isValid()) { - provided_description_ = rhp.provided_description_.getValue(); - } - if (rhp.provided_expr_string_.isValid()) { - provided_expr_string_ = rhp.provided_expr_string_.getValue(); - } - if (rhp.provided_value_semantic_.isValid()) { - provided_value_semantic_ = rhp.provided_value_semantic_.getValue(); - } - if (rhp.provided_visibility_.isValid()) { - provided_visibility_ = rhp.provided_visibility_.getValue(); - } - if (rhp.provided_class_.isValid()) { - provided_class_ = rhp.provided_class_.getValue(); - } - - rhp.provided_location_.clearValid(); - rhp.provided_description_.clearValid(); - rhp.provided_expr_string_.clearValid(); - rhp.provided_value_semantic_.clearValid(); - rhp.provided_visibility_.clearValid(); - rhp.provided_class_.clearValid(); - } + StatisticInstance(StatisticInstance&& rhp); /*! * \brief Construct a StatisticInstance with its metadata @@ -362,17 +175,7 @@ namespace sparta const StatisticDef::ValueSemantic value_semantic, const InstrumentationNode::visibility_t visibility, const InstrumentationNode::class_t cls, - const std::vector> & metadata = {}) : - StatisticInstance() - { - provided_location_ = location; - provided_description_ = description; - provided_expr_string_ = expression_str; - provided_value_semantic_ = value_semantic; - provided_visibility_ = visibility; - provided_class_ = cls; - provided_metadata_ = metadata; - } + const std::vector> & metadata = {}); /*! * \brief Construct a StatisticInstance with its location and @@ -386,23 +189,10 @@ namespace sparta const std::shared_ptr & calculator, const InstrumentationNode::visibility_t visibility = InstrumentationNode::DEFAULT_VISIBILITY, const InstrumentationNode::class_t cls = InstrumentationNode::DEFAULT_CLASS, - const std::vector> & metadata = {}) : - StatisticInstance() - { - if (!location.empty()) { - provided_location_ = location; - } - if (!description.empty()) { - provided_description_ = description; - } - user_calculated_si_value_ = calculator; - provided_visibility_ = visibility; - provided_class_ = cls; - provided_metadata_ = metadata; - } + const std::vector> & metadata = {}); /*! - * \brief Virtual destructor + * \brief Non-Virtual destructor */ ~StatisticInstance() {;} @@ -418,26 +208,7 @@ namespace sparta //! @} //! \brief Assignment Operator - StatisticInstance& operator=(const StatisticInstance& rhp) { - node_ref_ = rhp.node_ref_; - sdef_ = rhp.sdef_; - ctr_ = rhp.ctr_; - par_ = rhp.par_; - - stat_expr_ = rhp.stat_expr_; - start_tick_ = rhp.start_tick_; - end_tick_ = rhp.end_tick_; - scheduler_ = rhp.scheduler_; - initial_ = rhp.initial_; - result_ = rhp.result_; - - sub_statistics_ = rhp.sub_statistics_; - user_calculated_si_value_ = rhp.user_calculated_si_value_; - direct_lookup_si_value_ = rhp.direct_lookup_si_value_; - provided_metadata_ = rhp.provided_metadata_; - - return *this; - } + StatisticInstance& operator=(const StatisticInstance& rhp); //! \name Computation Window //! @{ @@ -451,46 +222,7 @@ namespace sparta * \throw SpartaException if node reference is expired (and there is a * node reference) */ - void start() { - sparta_assert(direct_lookup_si_value_ == nullptr, - "You cannot call StatisticInstance::start() for an SI " - "that was recreated from a SimDB record"); - - start_tick_ = getScheduler_()->getElapsedTicks(); - end_tick_ = Scheduler::INDEFINITE; - - if(user_calculated_si_value_){ - initial_.resetValue(user_calculated_si_value_->getCurrentValue()); - result_ = NAN; - return; - } - - if(sdef_ != nullptr){ - if(node_ref_.expired() == true){ - throw SpartaException("Cannot start() a StatisticInstance referring to a " - "destructed StatisticDef"); - } - stat_expr_.start(); - initial_.resetValue(0); - }else if(ctr_){ - if(node_ref_.expired() == true){ - throw SpartaException("Cannot start() a StatisticInstance referring to a " - "destructed Counter"); - } - initial_.resetValue(ctr_->get()); - }else if(par_){ - if(node_ref_.expired() == true){ - throw SpartaException("Cannot start() a StatisticInstance referring to a " - "destructed Parameter"); - } - initial_.resetValue(par_->getDoubleValue()); - }else{ - stat_expr_.start(); - } - - // Clear result value - result_ = NAN; - } + void start(); /*! * \brief Ends the window for this instance. Computes and caches the @@ -500,38 +232,7 @@ namespace sparta * \throw SpartaException if node reference is expired (and there is a * node reference) */ - void end(){ - sparta_assert(direct_lookup_si_value_ == nullptr, - "You cannot call StatisticInstance::end() for an SI " - "that was recreated from a SimDB record"); - - end_tick_ = getScheduler_()->getElapsedTicks(); - - if(sdef_ != nullptr){ - if(node_ref_.expired() == true){ - throw SpartaException("Cannot end() a StatisticInstance referring to a " - "destructed StatisticDef"); - } - stat_expr_.end(); - }else if(ctr_ != nullptr){ - if(node_ref_.expired() == true){ - throw SpartaException("Cannot end() a StatisticInstance referring to a " - "destructed Counter"); - } - // Do nothing to counter - }else if(par_ != nullptr){ - if(node_ref_.expired() == true){ - throw SpartaException("Cannot end() a StatisticInstance referring to a " - "destructed Parameter"); - } - // Do nothing to Parameter - }else{ - stat_expr_.end(); - } - - // Recompute result value - result_ = computeValue_(); - } + void end(); /*! * \brief Returns the time at which this computation window was started. @@ -613,42 +314,7 @@ namespace sparta * Scheduler::INDEFINITE) and it is greater than the current scheduler * tick (Scheduler::getCurrentTick) */ - double getValue() const { - if (direct_lookup_si_value_ != nullptr) { - return computeValue_(); - } - - if(end_tick_ < start_tick_){ - throw ReversedStatisticRange("Range is reversed. End < start"); - } - - if(start_tick_ > getScheduler_()->getElapsedTicks()){ - throw FutureStatisticRange("Range starts in the future at ") << start_tick_; - } - - double value; - if(end_tick_ == Scheduler::INDEFINITE){ - // Compute Value - value = computeValue_(); - } - - else if(end_tick_ > getScheduler_()->getElapsedTicks()){ - // Rang ends in the future - probable because of a checkpoint - throw FutureStatisticRange("Range ends in the future at ") << end_tick_; - } - - else { - // End tick <= current tick. Use pre-computed value because this - // window ended in the past - value = result_; - } - - //Update any snapshot loggers that are listening for these updates - for (auto & logger : snapshot_loggers_) { - logger.takeSnapshot(value); - } - return value; - } + double getValue() const; /*! * \brief Returns the initial value of this instance at start_tick_ @@ -662,53 +328,12 @@ namespace sparta * statistic or counter it contains. This could fiffer from getValue() * since it disregards the computation window */ - double getRawLatest() const { - if(sdef_){ - if(node_ref_.expired() == true){ - return NAN; - } - // Evaluate the expression - return stat_expr_.evaluate(); - }else if(ctr_){ - if(node_ref_.expired() == true){ - return NAN; - } - return ctr_->get(); - }else if(par_){ - if(node_ref_.expired() == true){ - return NAN; - } - return par_->getDoubleValue(); - }else{ - return stat_expr_.evaluate(); - } + double getRawLatest() const; - return NAN; - } - - bool supportsCompression() const { - if (user_calculated_si_value_) { - return false; - } - if (sdef_) { - if (node_ref_.expired()) { - return false; - } - return stat_expr_.supportsCompression(); - } else if (ctr_) { - if (node_ref_.expired()) { - return false; - } - return ctr_->supportsCompression(); - } else if (par_) { - if (node_ref_.expired()) { - return false; - } - return par_->supportsCompression(); - } - - return stat_expr_.supportsCompression(); - } + /*! + * Does this StatisticInstance support compression (database)? + */ + bool supportsCompression() const; /*! * \brief Renders this StatisticInstance to a string containing @@ -721,38 +346,7 @@ namespace sparta * containing only counters. */ std::string stringize(bool show_range=true, - bool resolve_subexprs=true) const { - std::stringstream ss; - ss << "getLocation(); - }else{ - ss << ""; - } - }else{ - ss << "expression: " << getExpressionString(show_range, - resolve_subexprs); - } - - // Range - if(show_range){ - ss << " [" << start_tick_ << ","; - if(end_tick_ == Scheduler::INDEFINITE){ - ss << "now"; - }else{ - ss << end_tick_; - } - ss << "]"; - } - - // Value - //! \note Could produce nan, -nan, -inf, +inf, or inf depending on glibc - ss << " = " << getValue() << ">"; - return ss.str(); - } + bool resolve_subexprs=true) const; /*! * \brief Returns a string containing the expression that this statistic @@ -766,35 +360,7 @@ namespace sparta * containing only counters. */ std::string getExpressionString(bool show_range=true, - bool resolve_subexprs=true) const { - if(provided_expr_string_.isValid()) { - return provided_expr_string_.getValue(); - } - if(sdef_){ - if(node_ref_.expired() == false){ - // Print the fully rendered expression string instead of the - // string used to construct the StatisticDef node - return stat_expr_.stringize(show_range, resolve_subexprs); - //return sdef_->getExpression(resolve_subexprs); - }else{ - return ""; - } - }else if(ctr_){ - if(node_ref_.expired() == false){ - return ctr_->getLocation(); - }else{ - return ""; - } - }else if(par_){ - if(node_ref_.expired() == false){ - return par_->getLocation(); - }else{ - return ""; - } - }else{ - return stat_expr_.stringize(show_range, resolve_subexprs); - } - } + bool resolve_subexprs=true) const; /*! * \brief Returns a string that describes the statistic instance @@ -804,41 +370,7 @@ namespace sparta * \param show_stat_node_expressions If true, also shows expressions for * nodes which are StatisticDefs */ - std::string getDesc(bool show_stat_node_expressions) const { - if(provided_description_.isValid()) { - return provided_description_.getValue(); - } - if(sdef_){ - if(node_ref_.expired() == false){ - std::string result = sdef_->getDesc(); - if(show_stat_node_expressions){ - result += " "; - result += stat_expr_.stringize(false, // show_range - true); // result_subexprs; - } - return result; - }else{ - return ""; - } - }else if(ctr_){ - if(node_ref_.expired() == false){ - return ctr_->getDesc(); - }else{ - return ""; - } - }else if(par_){ - if(node_ref_.expired() == false){ - return par_->getDesc(); - }else{ - return ""; - } - } - - std::string result = "Free Expression: "; - result += stat_expr_.stringize(false, // show_range - true); // result_subexprs - return result; - } + std::string getDesc(bool show_stat_node_expressions) const; /*! * \brief Renders this StatisticInstance to a string containing @@ -847,29 +379,7 @@ namespace sparta * \oaram show_range Should range information for this instance be * written to \a o? */ - void dump(std::ostream& o, bool show_range=false) const { - // Source - if(false == node_ref_.expired()){ - o << node_ref_.lock()->getLocation() << " # " - << getExpressionString(); - }else{ - o << ""; - } - - // Range - if(show_range){ - o << " [" << start_tick_ << ","; - if(end_tick_ == Scheduler::INDEFINITE){ - o << "now"; - }else{ - o << end_tick_; - } - o << "]"; - } - - // Value - o << " = " << getValue(); - } + void dump(std::ostream& o, bool show_range=false) const; /*! * \brief Allow this statistic instance to emit statistic value @@ -904,32 +414,7 @@ namespace sparta * "". If any referenced node is expired, returns * "". */ - std::string getLocation() const { - if(provided_location_.isValid()) { - return provided_location_.getValue(); - } - if(sdef_){ - if(node_ref_.expired() == false){ - return node_ref_.lock()->getLocation(); - }else{ - return ""; - } - }else if(ctr_){ - if(node_ref_.expired() == false){ - return node_ref_.lock()->getLocation(); - }else{ - return ""; - } - }else if(par_){ - if(node_ref_.expired() == false){ - return node_ref_.lock()->getLocation(); - }else{ - return ""; - } - }else{ - return ""; - } - } + std::string getLocation() const; /*! * \brief Gets the statistic value semantic associated with this @@ -938,68 +423,19 @@ namespace sparta * For counters and expressions, returns StatisticDef::VS_INVALID. * For expired node references, returns StatisticDef::VS_INVALID */ - StatisticDef::ValueSemantic getValueSemantic() const { - if(provided_value_semantic_.isValid()) { - return provided_value_semantic_.getValue(); - } - if(sdef_){ - if(node_ref_.expired() == false){ - return sdef_->getValueSemantic(); - }else{ - return StatisticDef::VS_INVALID; - } - }else if(ctr_){ - return StatisticDef::VS_INVALID; - }else if(par_){ - return StatisticDef::VS_INVALID; - }else{ - return StatisticDef::VS_INVALID; - } - } + StatisticDef::ValueSemantic getValueSemantic() const; /*! * \brief Gets the visibility associated with this * statistic instance. */ - InstrumentationNode::visibility_t getVisibility() const { - if(provided_visibility_.isValid()) { - return provided_visibility_.getValue(); - } - if(node_ref_.expired()) { - return InstrumentationNode::VIS_NORMAL; - } - if(sdef_){ - return sdef_->getVisibility(); - }else if(ctr_){ - return ctr_->getVisibility(); - }else if(par_){ - return InstrumentationNode::VIS_NORMAL; // Use normal for parameters for now - } - - return InstrumentationNode::VIS_NORMAL; - } + InstrumentationNode::visibility_t getVisibility() const; /*! * \brief Gets the Class associated with this * statistic instance. */ - InstrumentationNode::class_t getClass() const { - if(provided_class_.isValid()) { - return provided_class_.getValue(); - } - if(node_ref_.expired()) { - return InstrumentationNode::DEFAULT_CLASS; - } - if(sdef_){ - return sdef_->getClass(); - }else if(ctr_){ - return ctr_->getClass(); - }else if(par_){ - return InstrumentationNode::DEFAULT_CLASS; // Use normal for parameters for now - } - - return InstrumentationNode::DEFAULT_CLASS; - } + InstrumentationNode::class_t getClass() const; /*! * \brief Give the reporting infrastructure access to all metadata @@ -1020,6 +456,14 @@ namespace sparta return sdef_; } + /*! + * \brief Get the underlying expression representing this SI + * \return Expression + */ + const Expression & getStatisticExpression() const { + return stat_expr_; + } + /*! * \Returns the counter used to compute this statistic */ @@ -1049,28 +493,7 @@ namespace sparta * \throw SpartaException if this StatisticInstance refers to an expired * TreeNode. */ - void getClocks(std::vector& clocks) const { - if(sdef_){ - if(node_ref_.expired() == true){ - throw SpartaException("Cannot getClocks() on a StatisticInstance refering to " - "an expired TreeNode reference"); - } - - stat_expr_.getClocks(clocks); - }else if(ctr_){ - if(node_ref_.expired() == true){ - throw SpartaException("Cannot getClocks() on a Counter refering to " - "an expired TreeNode reference"); - } - - const Clock* clk = node_ref_.lock()->getClock(); - if(clk != nullptr){ - clocks.push_back(clk); - } - }else{ - stat_expr_.getClocks(clocks); - } - } + void getClocks(std::vector& clocks) const; //////////////////////////////////////////////////////////////////////// //! @} @@ -1102,38 +525,7 @@ namespace sparta * counters or StatisticDefs have expired, returns NAN. * \pre Referenced StatisticDef or Counter must not have been destroyed */ - double computeValue_() const { - if(user_calculated_si_value_){ - return user_calculated_si_value_->getCurrentValue() - getInitial(); - } - if(direct_lookup_si_value_){ - return getCurrentValueFromDirectLookup_(); - } - if(sdef_){ - if(node_ref_.expired() == true){ - return NAN; - } - // Evaluate the expression - return stat_expr_.evaluate(); - }else if(ctr_){ - if(node_ref_.expired() == true){ - return NAN; - } - if(ctr_->getBehavior() == CounterBase::COUNT_LATEST){ - return ctr_->get(); - }else{ - // Compute the delta - return ctr_->get() - getInitial(); - } - }else if(par_){ - if(node_ref_.expired() == true){ - return NAN; - } - return par_->getDoubleValue(); - }else{ - return stat_expr_.evaluate(); - } - } + double computeValue_() const; /*! * \brief Ask the StatInstValueLookup object for our current @@ -1168,41 +560,41 @@ namespace sparta * compute its value. (nullptr if there is no StatisticDef to * reference) */ - const StatisticDef* sdef_; + const StatisticDef* sdef_ = nullptr; /*! * \brief Counter reference from which this statistic instance will * compute its value (nullptr if there is no Counter to reference) */ - const CounterBase* ctr_; + const CounterBase* ctr_ = nullptr; /*! * \brief Parameter reference from which this statistic instance will * compute its value (nullptr if there is no Parameter to reference) */ - const ParameterBase* par_; + const ParameterBase* par_ = nullptr; /*! * \brief Expression containing the representation of sdef_. - * If this StatisticInstance refers to a StatisticDef, this will contain - * the instantiate expression from that stat def. This is not used for - * Counters. If this StatisticInstance is - * constructed only with a anonymous Expression, then this will be a - * copy of that expression + * If this StatisticInstance refers to a StatisticDef, this + * will contain the instantiated expression from that stat + * def. This is not used for Counters. If this + * StatisticInstance is constructed only with a anonymous + * Expression, then this will be a copy of that expression */ sparta::statistics::expression::Expression stat_expr_; /*! * \brief Tick on which this statistic started (exclusive) */ - Scheduler::Tick start_tick_; + Scheduler::Tick start_tick_{0}; /*! * \brief Tick on which this statistic ended (inclusive) * * Is Scheduler::INDEFINITE; if not yet ended */ - Scheduler::Tick end_tick_; + Scheduler::Tick end_tick_{Scheduler::INDEFINITE}; /*! * \brief Cached Scheduler object @@ -1212,30 +604,7 @@ namespace sparta /*! * \brief Get the Scheduler associated with this StatisticInstance */ - const Scheduler * getScheduler_() const { - if (scheduler_) { - return scheduler_; - } - - sparta_assert(false == node_ref_.expired(), - "This node has expired and taken the Scheduler with it"); - - const Clock * clk = nullptr; - if (sdef_) { - clk = sdef_->getClock(); - } else if (ctr_) { - clk = ctr_->getClock(); - } else if (par_) { - clk = par_->getClock(); - } - if (clk) { - scheduler_ = clk->getScheduler(); - } - - // Should always be able to fall back on singleton scheduler - sparta_assert(nullptr != scheduler_); - return scheduler_; - } + const Scheduler * getScheduler_() const; /*! * \brief Helper class which wraps an initial value together @@ -1291,12 +660,12 @@ namespace sparta /*! * \brief Initial value at start_tick_ */ - mutable InitialStatValue initial_; + mutable InitialStatValue initial_{NAN}; /*! * \brief Result value (truncated during output if required) */ - double result_; + double result_{NAN}; /*! * \brief Snapshot objects who have requested access to statistics values @@ -1345,6 +714,7 @@ namespace sparta }; // class StatisticInstance + //! \brief StatisticInstance stream operator inline std::ostream& operator<< (std::ostream& out, sparta::StatisticInstance const & si) { @@ -1364,4 +734,3 @@ namespace sparta } } // namespace sparta - diff --git a/sparta/sparta/statistics/dispatch/ReportStatisticsHierTree.hpp b/sparta/sparta/statistics/dispatch/ReportStatisticsHierTree.hpp index f9216dd8ba..1cdd77fadd 100644 --- a/sparta/sparta/statistics/dispatch/ReportStatisticsHierTree.hpp +++ b/sparta/sparta/statistics/dispatch/ReportStatisticsHierTree.hpp @@ -94,10 +94,10 @@ class ReportStatisticsHierTree } boost::replace_all(name, ".", "_"); - std::shared_ptr si_node(new LeafNodeT(name, stat.second)); + std::shared_ptr si_node(new LeafNodeT(name, stat.second.get())); si_node->setParent(report_node); children.emplace_back(si_node); - flattened_leaves.emplace_back(std::make_pair(si_node.get(), stat.second)); + flattened_leaves.emplace_back(std::make_pair(si_node.get(), stat.second.get())); } } @@ -124,4 +124,3 @@ class ReportStatisticsHierTree } // namespace statistics } // namespace sparta - diff --git a/sparta/sparta/tree/filter/Expression.hpp b/sparta/sparta/tree/filter/Expression.hpp index ddc591847f..0a87c9ef6a 100644 --- a/sparta/sparta/tree/filter/Expression.hpp +++ b/sparta/sparta/tree/filter/Expression.hpp @@ -433,5 +433,3 @@ inline std::ostream& operator<<(std::ostream& out, const Expression& e){ } // namespace filter } // namespace tree } // namespace sparta - -#pragma once diff --git a/sparta/sparta/tree/filter/Grammar.hpp b/sparta/sparta/tree/filter/Grammar.hpp index d439c7e98d..bba9b8cc5f 100644 --- a/sparta/sparta/tree/filter/Grammar.hpp +++ b/sparta/sparta/tree/filter/Grammar.hpp @@ -326,5 +326,3 @@ class Grammar : } // namespace filter } // namespace tree } // namespace sparta - -#pragma once diff --git a/sparta/sparta/tree/filter/Parser.hpp b/sparta/sparta/tree/filter/Parser.hpp index 3e596ad28d..c9cda313f9 100644 --- a/sparta/sparta/tree/filter/Parser.hpp +++ b/sparta/sparta/tree/filter/Parser.hpp @@ -72,5 +72,3 @@ class Parser } // namespace filter } // namespace tree } // namespace sparta - -#pragma once diff --git a/sparta/sparta/utils/Colors.hpp b/sparta/sparta/utils/Colors.hpp index bb000e777d..aa0a708083 100644 --- a/sparta/sparta/utils/Colors.hpp +++ b/sparta/sparta/utils/Colors.hpp @@ -225,5 +225,3 @@ namespace color { } // namespace color } // namespace sparta - -#pragma once diff --git a/sparta/src/CMakeLists.txt b/sparta/src/CMakeLists.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sparta/src/Expression.cpp b/sparta/src/Expression.cpp index dfa787aa82..3042457267 100644 --- a/sparta/src/Expression.cpp +++ b/sparta/src/Expression.cpp @@ -14,16 +14,25 @@ Expression::Expression(const std::string& expression, TreeNode* context) { std::vector already_used; - parse_(expression, context, already_used); + static const StatisticPairs report_si; + parse_(expression, context, already_used, report_si); } Expression::Expression(const std::string& expression, TreeNode* context, std::vector& already_used) { - parse_(expression, context, already_used); + static const StatisticPairs report_si; + parse_(expression, context, already_used, report_si); } +Expression::Expression(const std::string& expression, + TreeNode* context, + const StatisticPairs&report_si) +{ + std::vector already_used; + parse_(expression, context, already_used, report_si); +} Expression::Expression(const TreeNode* n, std::vector& used) : Expression(new StatVariable(n, used)) @@ -37,10 +46,12 @@ Expression::~Expression() { void Expression::parse_(const std::string& expression, TreeNode* context, - std::vector& already_used) { + std::vector& already_used, + const StatisticPairs &report_si) +{ sparta_assert(context, "cannot parse an expression \"" << expression << "\" without a null context"); - ExpressionParser parser(context, already_used); + ExpressionParser parser(context, already_used, report_si); try{ Expression ex = parser.parse(expression); content_ = std::move(ex.content_); diff --git a/sparta/src/ExpressionGrammar.cpp b/sparta/src/ExpressionGrammar.cpp index b6c5c2b677..4c50b52158 100644 --- a/sparta/src/ExpressionGrammar.cpp +++ b/sparta/src/ExpressionGrammar.cpp @@ -173,6 +173,7 @@ class lazy_gen_var_ { TreeNode* n_; //!< Context node std::vector& used_; //!< Disallowed nodes + const StatisticPairs & report_si_; //!< existing SI instances in the report public: @@ -181,17 +182,20 @@ class lazy_gen_var_ * \param n Context node in which to search for children * \param used Children already used in a parent expression. These nodes * must be rejected as they would create cycles if encountered + * \param report_si Exising report statistic instances */ - lazy_gen_var_(TreeNode* n, std::vector& used) : + lazy_gen_var_(TreeNode* n, std::vector& used, + const StatisticPairs & report_si) : n_(n), - used_(used) + used_(used), + report_si_(report_si) { } template struct result { typedef Expression type; }; template - Expression operator()(A1 a1) const + Expression operator()(const A1 & a1) const { if(n_ == nullptr){ // Must construct with non-null to actually use @@ -221,6 +225,15 @@ class lazy_gen_var_ } } + if(auto it = std::find_if(report_si_.begin(), + report_si_.end(), + [&a1] (const auto & si_pair) { + return (si_pair.first == a1); + }); it != report_si_.end()) + { + return it->second->getStatisticExpression(); + } + if(n == nullptr) { sparta_assert(calculator == nullptr); SpartaException ex("While parsing the expression or term of expression: '"); @@ -236,10 +249,8 @@ class lazy_gen_var_ } else { sv = new StatVariable(calculator, used_); } - used_.pop_back(); // Remove the stat so it can be used higher up or by - // other sibling expressions in the expression - + // other sibling expressions in the expression return Expression(sv); } }; // class lazy_gen_var_ @@ -301,7 +312,7 @@ ExpressionGrammar::builtin_vars_::builtin_vars_(TreeNode* n, std::vector& used) { sparta_assert(nullptr != n, - "cannot construct ExpressionGrammar::builtin_vars_ with a null context"); + "cannot construct ExpressionGrammar::builtin_vars_ with a null context"); static auto get_clock_from_node = [](TreeNode* n) -> const Clock & { //If we are tied to a clock object directly, return it. @@ -386,17 +397,18 @@ ExpressionGrammar::builtin_vars_::builtin_vars_(TreeNode* n, } ExpressionGrammar::variable_::variable_(sparta::TreeNode* n, - std::vector& used) : + std::vector& used, + const StatisticPairs & report_si) : variable_::base_type(start) { sparta_assert(nullptr != n, - "cannot construct ExpressionGrammar::variable_ with a null context"); + "cannot construct ExpressionGrammar::variable_ with a null context"); using qi::ascii::char_; using qi::_val; // Variable factory - helpers::lazy_gen_var_ lgv(n, used); + helpers::lazy_gen_var_ lgv(n, used, report_si); phoenix::function lazy_gen_var(lgv); start = str [_val = lazy_gen_var(qi::_1)]; @@ -540,18 +552,19 @@ ExpressionGrammar::tfunc_::tfunc_(const std::vector& used) } ExpressionGrammar::ExpressionGrammar(sparta::TreeNode* root, - std::vector& used) : + std::vector& used, + const StatisticPairs & report_si) : ExpressionGrammar::base_type(expression), builtin_vars(root, used), ufunc(used), bfunc(used), tfunc(used), - var(root, used), + var(root, used, report_si), root_(root) { (void) root_; sparta_assert(nullptr != root, - "cannot construct ExpressionGrammar with a null context"); + "cannot construct ExpressionGrammar with a null context"); namespace qi = qi; using qi::real_parser; diff --git a/sparta/src/JavascriptObject.cpp b/sparta/src/JavascriptObject.cpp index 3e8f3e75dc..e034d990f2 100644 --- a/sparta/src/JavascriptObject.cpp +++ b/sparta/src/JavascriptObject.cpp @@ -197,10 +197,10 @@ void sparta::report::format::JavascriptObject::mergeReportList_(std::ostream& ou void sparta::report::format::JavascriptObject::writeStats_(std::ostream& out, const Report & report, const std::string & stat_prefix, std::vector & all_stat_names) const { - const std::vector& stats = report.getStatistics(); + const statistics::StatisticPairs& stats = report.getStatistics(); for (uint32_t idx = 0; idx < stats.size(); idx++) { - const Report::stat_pair_t & si = stats[idx]; + const statistics::stat_pair_t & si = stats[idx]; std::string sname = stat_prefix; if (si.first == "") { diff --git a/sparta/src/JsonFormatter.cpp b/sparta/src/JsonFormatter.cpp index 905a488d29..95807f77ce 100644 --- a/sparta/src/JsonFormatter.cpp +++ b/sparta/src/JsonFormatter.cpp @@ -113,10 +113,11 @@ void extractStatisticsJsonFull(rapidjson::Document & doc, const Report::SubStaticticInstances & sub_stats = r->getSubStatistics(); const Report::DBSubStatisticInstances & db_sub_stats = r->getDBSubStatistics(); - for (const Report::stat_pair_t & si : r->getStatistics()) { + for (const statistics::stat_pair_t & si : r->getStatistics()) + { const std::string stat_name = !si.first.empty() ? si.first : si.second->getLocation(); if (!stat_name.empty()) { - StatisticInstance * stat_inst = si.second; + const StatisticInstance * stat_inst = si.second.get(); const StatisticDef * def = stat_inst->getStatisticDef(); const CounterBase * ctr = stat_inst->getCounter(); const ParameterBase * prm = stat_inst->getParameter(); @@ -395,10 +396,10 @@ void extractStatisticsJsonReduced(rapidjson::Document & doc, std::set dont_print_these; std::set db_dont_print_these; - for (const Report::stat_pair_t & si : r->getStatistics()) { + for (const statistics::stat_pair_t & si : r->getStatistics()) { const std::string stat_name = !si.first.empty() ? si.first : si.second->getLocation(); if (!stat_name.empty()) { - StatisticInstance * stat_inst = si.second; + const StatisticInstance * stat_inst = si.second.get(); const StatisticDef * def = stat_inst->getStatisticDef(); const CounterBase * ctr = stat_inst->getCounter(); const ParameterBase * prm = stat_inst->getParameter(); diff --git a/sparta/src/Report.cpp b/sparta/src/Report.cpp index f909daf32e..fd7571e758 100644 --- a/sparta/src/Report.cpp +++ b/sparta/src/Report.cpp @@ -325,13 +325,26 @@ class ReportFileParserYAML } } - bool handleLeafScalarUnknownKey_(TreeNode* n, + bool handleLeafScalarUnknownKey_(TreeNode* node_context, const std::string& value, const std::string& assoc_key, - const NavNode& scope) override { - sparta_assert(n); + const NavNode& scope) override + { + sparta_assert(node_context); bool in_content = in_content_stack_.top(); - //Report* const r = report_stack_.top(); + + auto add_expression = [this, &node_context, &value, &scope] (Expression & expr) + { + // Build the StatisticInstance responsible for evaluating. + StatisticInstance si(std::move(expr)); + si.setContext(node_context); + std::string full_name = value; + auto& captures = scope.second; + Report* const r = report_map_.at(scope.uid); + if(this->getSubstituteForStatName(full_name, node_context, captures)){ + r->add(si, full_name); + } + }; if(in_content){ if(current_autopop_block_.size() > 0){ @@ -381,7 +394,7 @@ class ReportFileParserYAML // Get child node from path string. const auto child_node = - path_in_report.empty() ? n : n->getChild(path_in_report); + path_in_report.empty() ? node_context : node_context->getChild(path_in_report); // Attempt to cast to cycle_histogram node. const auto cycle_histogram_node = dynamic_cast(child_node); @@ -403,15 +416,8 @@ class ReportFileParserYAML value, bound_fcn, statistics::expression::Expression(0.0)); - // Build the StatisticInstance responsible for evaluating. - StatisticInstance si(std::move(expr)); - si.setContext(n); - std::string full_name = value; - auto& captures = scope.second; - Report* const r = report_map_.at(scope.uid); - if(getSubstituteForStatName(full_name, n, captures)){ - r->add(si, full_name); - } + // Add the expresssion + add_expression(expr); } else{ // Attempt to cast to histogram node. @@ -432,45 +438,30 @@ class ReportFileParserYAML value, bound_fcn, statistics::expression::Expression(0.0)); - // Build the StatisticInstance responsible for evaluating. - StatisticInstance si(std::move(expr)); - si.setContext(n); - std::string full_name = value; - auto& captures = scope.second; - Report* const r = report_map_.at(scope.uid); - if(getSubstituteForStatName(full_name, n, captures)){ - r->add(si, full_name); - } + // Add the expresssion + add_expression(expr); } } } else{ - statistics::expression::Expression expr(assoc_key, n); - StatisticInstance si(std::move(expr)); - si.setContext(n); - std::string full_name = value; - auto& captures = scope.second; Report* const r = report_map_.at(scope.uid); - if(getSubstituteForStatName(full_name, n, captures)){ - r->add(si, full_name); - } + statistics::expression::Expression expr(assoc_key, node_context, r->getStatistics()); + + // Add the expresssion + add_expression(expr); } } else{ - statistics::expression::Expression expr(assoc_key, n); - StatisticInstance si(std::move(expr)); - si.setContext(n); - std::string full_name = value; - auto& captures = scope.second; Report* const r = report_map_.at(scope.uid); - if(getSubstituteForStatName(full_name, n, captures)){ - r->add(si, full_name); - } + statistics::expression::Expression expr(assoc_key, node_context, r->getStatistics()); + + // Add the expresssion + add_expression(expr); } }catch(SpartaException& ex){ std::stringstream ss; ss << "Unable to parse expression: \"" << assoc_key << "\" within context: " - << n->getLocation() << " in report file \"" << getFilename() + << node_context->getLocation() << " in report file \"" << getFilename() << "\" for the following reason: " << ex.what(); if(in_optional_) { // Possibly in an optional block where the @@ -1159,6 +1150,157 @@ class ReportFileParserYAML std::string filename_; //!< For recalling errors }; // class ReportFileParserYAML +Report::StatAdder Report::add(const StatisticInstance& si, const std::string& name) { + if(name != "" && stat_names_.find(name) != stat_names_.end()){ + throw SpartaException("There is already a statistic instance in this Report (") + << getName() << ") named \"" << name << "\" pointing to " + << getStatistic(name).getLocation() + << " and the new stat would be pointing to a StatisticInstance " + << si.getExpressionString(); + } + + // Track a new stat with helpful exception wrapping + addField_(name, si); + + if(name != ""){ stat_names_.insert(name); } + + addSubStatistics_(&si); + + return Report::StatAdder(*this); +} + +Report::StatAdder Report::add(StatisticInstance&& si, const std::string& name) { + if(name != "" && stat_names_.find(name) != stat_names_.end()){ + throw SpartaException("There is already a statistic instance in this Report (") + << getName() << ") named \"" << name << "\" pointing to " + << getStatistic(name).getLocation() + << " and the new stat would be pointing to a StatisticInstance " + << si.getExpressionString(); + } + + // Track a new stat with helpful exception wrapping + addField_(name, si); + + if(name != ""){ stat_names_.insert(name); } + + addSubStatistics_(&si); + + return Report::StatAdder(*this); +} + +Report::StatAdder Report::add(StatisticDef* sd, const std::string& name) { + sparta_assert(sd); + if(name != "" && stat_names_.find(name) != stat_names_.end()){ + throw SpartaException("There is already a statistic instance in this Report (") + << getName() << ") named \"" << name << "\" pointing to " + << getStatistic(name).getLocation() + << " and the new stat would be the the statistic def at " + << sd->getLocation() << " with the expression \"" + << sd->getExpression() << "\""; + } + + // Track a new stat with helpful exception wrapping + addField_(name, sd); + + if(name != ""){ stat_names_.insert(name); } + + return Report::StatAdder(*this); +} + +Report::StatAdder Report::add(CounterBase* ctr, const std::string& name) { + sparta_assert(ctr); + if(name != "" && stat_names_.find(name) != stat_names_.end()){ + throw SpartaException("There is already a statistic instance in this Report (") + << getName() << ") named \"" << name << "\" pointing to " + << getStatistic(name).getLocation() + << " and the new stat would be the counter to " + << ctr->getLocation(); + } + + // Track a new stat with helpful exception wrapping + addField_(name, ctr); + + if(name != ""){ stat_names_.insert(name); } + + return Report::StatAdder(*this); +} + +Report::StatAdder Report::add(TreeNode* n, const std::string& name) { + sparta_assert(n); + if(name != "" && stat_names_.find(name) != stat_names_.end()){ + throw SpartaException("There is already a statistic instance in this Report (") + << getName() << ") named \"" << name << "\" pointing to " + << getStatistic(name).getLocation() + << " and the new stat would be the node to " + << n->getLocation(); + } + + // Track a new stat with helpful exception wrapping + addField_(name, n); + + if(name != ""){ stat_names_.insert(name); } + + return Report::StatAdder(*this); +} + +Report::StatAdder Report::add(const std::string& expression, const std::string& name) { + if(name != "" && stat_names_.find(name) != stat_names_.end()){ + throw SpartaException("There is already a statistic instance in this Report (") + << getName() << ") named \"" << name << "\" pointing to " + << getStatistic(name).getLocation() + << " and the new stat would be the expression \"" + << expression << "\""; + } + if(nullptr == context_){ + throw SpartaException("This report currently has no context. To add an item by " + "expression \"") + << expression << "\", specify a context TreeNode using setContext as the " + "context from which TreeNodes can be searched for"; + } + + if(TreeNodePrivateAttorney::hasChild(context_, expression)){ + // Add as a TreeNode statistic + add(TreeNodePrivateAttorney::getChild(context_, expression), name); + }else{ + statistics::expression::Expression expr(expression, context_); + StatisticInstance si(std::move(expr)); + add(std::move(si), name); + } + + return Report::StatAdder(*this); +} + +Report::StatAdder Report::add(const std::vector& nv) { + for(TreeNode* n : nv){ + add(n); + } + return Report::StatAdder(*this); +} + +Report::StatAdder Report::addSubStats(StatisticDef * n, const std::string & name_prefix) { + sparta_assert(auto_expand_context_counter_stats_, + "Call to Report::addSubStats(StatisticDef*, name_prefix) is not " + "allowed since ContextCounter auto-expansion is disabled. Enable " + "this by calling Report::enableContextCounterAutoExpansion()"); + for (const auto & sub_stat : n->getSubStatistics()) { + TreeNode * sub_stat_node = const_cast(sub_stat.getNode()); + const std::string prefix = + !name_prefix.empty() ? name_prefix : sub_stat_node->getLocation(); + const std::string sub_stat_name = prefix + "." + sub_stat.getName(); + add(sub_stat_node, sub_stat_name); + } + return Report::StatAdder(*this); +} + +void Report::accumulateStats() const { + for (const auto & stat : stats_) { + stat.second->accumulateStatistic(); + } + for (const auto & sr : getSubreports()) { + sr.accumulateStats(); + } +} + void Report::addFile(const std::string& file_path, bool verbose) { const std::vector replacements; @@ -1646,87 +1788,6 @@ std::unique_ptr createSIFromSimDB( return si; } -/*! - * \brief This method lets SimDB-recreated Report objects - * set placeholders this SI will soon use to get - * SI data values directly from a SimDB blob (not - * from an actual simulation). - */ -void StatisticInstance::setSIValueDirectLookupPlaceholder( - const std::shared_ptr & direct_lookup) -{ - direct_lookup_si_value_ = direct_lookup; -} - -/*! - * \brief Our StatInstValueLookup *placeholder* object - * needs to bind itself to a StatInstRowIterator object, - * since these two classes go hand in hand. Now that we're - * being given the row iterator, we can use it to "realize" - * our "SI direct value lookup" object now. - */ -void StatisticInstance::realizeSIValueDirectLookup( - const StatInstRowIterator & si_row_iterator) -{ - if (direct_lookup_si_value_ != nullptr) { - auto realized_lookup = direct_lookup_si_value_-> - realizePlaceholder(si_row_iterator.getRowAccessor()); - - sparta_assert(realized_lookup != nullptr); - direct_lookup_si_value_.reset(realized_lookup); - } -} - -/*! - * \brief If this SI is using a StatInstValueLookup object - * to get its SI values, ask if this direct-lookup object - * can be used to get the current SI value. - */ -bool StatisticInstance::isSIValueDirectLookupValid() const -{ - if (direct_lookup_si_value_ == nullptr) { - return false; - } - - //The following function call throws if this direct - //lookup object is a placeholders::StatInstValueLookup - //which has not yet been realized. - try { - return direct_lookup_si_value_->isIndexValidForCurrentRow(); - } catch (...) { - } - - return false; -} - -/*! - * \brief Ask the StatInstValueLookup object for our current - * SI value. Throws an exception if the direct-value object - * is not being used. - */ -double StatisticInstance::getCurrentValueFromDirectLookup_() const -{ - if (direct_lookup_si_value_ == nullptr) { - throw SpartaException("StatisticInstance asked for its SI ") - << "value from a null direct-lookup object"; - } - - sparta_assert(getInitial() == 0, - "Unexpectedly encountered a StatisticInstance that " - "was created from a SimDB record, but whose SI offset " - "value (SI::getInitial()) was not zero. This is a bug."); - - //The following function call throws if this direct - //lookup object is a placeholders::StatInstValueLookup - //which has not yet been realized. - try { - return direct_lookup_si_value_->getCurrentValue(); - } catch (...) { - } - - return NAN; -} - /*! * \brief Starting with the given report node database ID, * find its root report node ID in the provided database. @@ -2212,7 +2273,7 @@ void Report::recursGetReportAndSINodeDatabaseIDs_( sparta_assert(stats_.size() == si_node_ids_.size()); for (size_t idx = 0; idx < stats_.size(); ++idx) { - si_nodes_by_id[si_node_ids_[idx]] = stats_[idx].second; + si_nodes_by_id[si_node_ids_[idx]] = stats_[idx].second.get(); } for (auto & sr : subreps_) { diff --git a/sparta/src/StatisticInstance.cpp b/sparta/src/StatisticInstance.cpp new file mode 100644 index 0000000000..a7e77605f5 --- /dev/null +++ b/sparta/src/StatisticInstance.cpp @@ -0,0 +1,768 @@ +// -*- C++ -*- + +/*! + * \file StatisticInstance.cpp + * \brief Contains a StatisticInstance which refers to a StatisticDef or Counter + * and some local state to compute a value over a specific sample range + */ + +#include "sparta/statistics/StatisticInstance.hpp" +#include "sparta/report/db/StatInstValueLookup.hpp" +#include "sparta/report/db/StatInstRowIterator.hpp" + +namespace sparta +{ + StatisticInstance::StatisticInstance(const StatisticDef* sd, + const CounterBase* ctr, + const ParameterBase* par, + const TreeNode* n, + std::vector* used) + { + const StatisticDef* stat_def; + if(!sd){ + stat_def = dynamic_cast(n); + + sdef_ = stat_def; + }else{ + sdef_ = stat_def = sd; + } + const CounterBase* counter; + if(!ctr){ + counter = dynamic_cast(n); + ctr_ = counter; + }else{ + ctr_ = counter = ctr; + } + const ParameterBase* param; + if(!par){ + param = dynamic_cast(n); + par_ = param; + }else{ + par_ = param = par; + } + + // Find the non-null argument + const TreeNode* node = n; + if(!node){ + node = sd; + if(!node){ + node = ctr; + if(!node){ + node = par; + sparta_assert(node, + "StatisticInstance was constructed with all null arguments. " + "This is not allowed"); + } + } + } + sparta_assert(int(nullptr != sdef_) + int(nullptr != ctr_) + int(nullptr != par_) == 1, + "Can only instantiate a StatisticInstance with either a StatisticDef, " + "a Counter, or a Parameter of any numeric type. Got Node: \"" << node->getLocation() + << "\". This node is not a stat, counter, or parameter."); + + // Get the Scheduler as context + if(node->getClock()) { + scheduler_ = n->getClock()->getScheduler(); + } + + if(sdef_){ + node_ref_ = stat_def->getWeakPtr(); + + std::vector* local_used_ptr; + std::vector temp_used; + if(!used){ + local_used_ptr = &temp_used; + }else{ + local_used_ptr = used; + } + stat_expr_ = sdef_->realizeExpression(*local_used_ptr); + if(stat_expr_.hasContent() == false){ + throw SpartaException("Cannot construct StatisticInstance based on node ") + << stat_def->getLocation() << " because its expression: " + << stat_def->getExpression() << " parsed to an empty expression"; + } + const auto & sub_stats_info = sdef_->getSubStatistics(); + for (auto & sub_stat_creation_info : sub_stats_info) { + addSubStatistic_(sub_stat_creation_info); + } + }else if(ctr_){ + node_ref_ = counter->getWeakPtr(); + }else if(par_){ + node_ref_ = param->getWeakPtr(); + }else{ + // Should not have been able to call constructor without 1 or + // the 3 args being non-null + throw SpartaException("Cannot instantiate a StatisticInstance without a statistic " + "definition or counter pointer"); + } + + start(); + + sparta_assert(false == node_ref_.expired()); + } + + StatisticInstance::StatisticInstance(std::shared_ptr & calculator, + std::vector& used) : + StatisticInstance(nullptr, nullptr, nullptr, calculator->getNode(), &used) + { + // Creating SI's using this constructor essentially means that you + // want to perform your own StatisticDef calculation, the math/logic + // of which is too complicated or cumbersome to express in a single + // std::string. Counter and Parameter SI's are simple enough that + // SPARTA will not let you try to override their SI value calculation. + // StatisticDef's and their subclasses are the exception. + sparta_assert(sdef_); + sparta_assert(ctr_ == nullptr); + sparta_assert(par_ == nullptr); + user_calculated_si_value_ = calculator; + } + + //! \brief Copy Constructor + StatisticInstance::StatisticInstance(const StatisticInstance& rhp) : + node_ref_(rhp.node_ref_), + sdef_(rhp.sdef_), + ctr_(rhp.ctr_), + par_(rhp.par_), + stat_expr_(rhp.stat_expr_), + start_tick_(rhp.start_tick_), + end_tick_(rhp.end_tick_), + scheduler_(rhp.scheduler_), + initial_(rhp.initial_), + result_(rhp.result_), + sub_statistics_(rhp.sub_statistics_), + user_calculated_si_value_(rhp.user_calculated_si_value_), + direct_lookup_si_value_(rhp.direct_lookup_si_value_), + provided_metadata_(rhp.provided_metadata_) + { + if (rhp.provided_location_.isValid()) { + provided_location_ = rhp.provided_location_.getValue(); + } + if (rhp.provided_description_.isValid()) { + provided_description_ = rhp.provided_description_.getValue(); + } + if (rhp.provided_expr_string_.isValid()) { + provided_expr_string_ = rhp.provided_expr_string_.getValue(); + } + if (rhp.provided_value_semantic_.isValid()) { + provided_value_semantic_ = rhp.provided_value_semantic_.getValue(); + } + if (rhp.provided_visibility_.isValid()) { + provided_visibility_ = rhp.provided_visibility_.getValue(); + } + if (rhp.provided_class_.isValid()) { + provided_class_ = rhp.provided_class_.getValue(); + } + } + + //! \brief Move Constructor + StatisticInstance::StatisticInstance(StatisticInstance&& rhp) : + node_ref_(std::move(rhp.node_ref_)), + sdef_(rhp.sdef_), + ctr_(rhp.ctr_), + par_(rhp.par_), + stat_expr_(std::move(rhp.stat_expr_)), + start_tick_(rhp.start_tick_), + end_tick_(rhp.end_tick_), + scheduler_(rhp.scheduler_), + initial_(rhp.initial_), + result_(rhp.result_), + sub_statistics_(std::move(rhp.sub_statistics_)), + user_calculated_si_value_(std::move(rhp.user_calculated_si_value_)), + direct_lookup_si_value_(std::move(rhp.direct_lookup_si_value_)), + provided_metadata_(std::move(rhp.provided_metadata_)) + { + rhp.sdef_ = nullptr; + rhp.ctr_ = nullptr; + rhp.par_ = nullptr; + rhp.result_ = NAN; + + if (rhp.provided_location_.isValid()) { + provided_location_ = rhp.provided_location_.getValue(); + } + if (rhp.provided_description_.isValid()) { + provided_description_ = rhp.provided_description_.getValue(); + } + if (rhp.provided_expr_string_.isValid()) { + provided_expr_string_ = rhp.provided_expr_string_.getValue(); + } + if (rhp.provided_value_semantic_.isValid()) { + provided_value_semantic_ = rhp.provided_value_semantic_.getValue(); + } + if (rhp.provided_visibility_.isValid()) { + provided_visibility_ = rhp.provided_visibility_.getValue(); + } + if (rhp.provided_class_.isValid()) { + provided_class_ = rhp.provided_class_.getValue(); + } + + rhp.provided_location_.clearValid(); + rhp.provided_description_.clearValid(); + rhp.provided_expr_string_.clearValid(); + rhp.provided_value_semantic_.clearValid(); + rhp.provided_visibility_.clearValid(); + rhp.provided_class_.clearValid(); + } + + StatisticInstance::StatisticInstance(const std::string & location, + const std::string & description, + const std::string & expression_str, + const StatisticDef::ValueSemantic value_semantic, + const InstrumentationNode::visibility_t visibility, + const InstrumentationNode::class_t cls, + const std::vector> & metadata) : + provided_location_(location), + provided_description_(description), + provided_expr_string_(expression_str), + provided_value_semantic_(value_semantic), + provided_visibility_(visibility), + provided_class_(cls), + provided_metadata_(metadata) + {} + + StatisticInstance::StatisticInstance(const std::string & location, + const std::string & description, + const std::shared_ptr & calculator, + const InstrumentationNode::visibility_t visibility, + const InstrumentationNode::class_t cls, + const std::vector> & metadata) : + user_calculated_si_value_(calculator), + provided_visibility_(visibility), + provided_class_(cls), + provided_metadata_(metadata) + { + if (!location.empty()) { + provided_location_ = location; + } + if (!description.empty()) { + provided_description_ = description; + } + } + + StatisticInstance& StatisticInstance::operator=(const StatisticInstance& rhp) { + node_ref_ = rhp.node_ref_; + sdef_ = rhp.sdef_; + ctr_ = rhp.ctr_; + par_ = rhp.par_; + + stat_expr_ = rhp.stat_expr_; + start_tick_ = rhp.start_tick_; + end_tick_ = rhp.end_tick_; + scheduler_ = rhp.scheduler_; + initial_ = rhp.initial_; + result_ = rhp.result_; + + sub_statistics_ = rhp.sub_statistics_; + user_calculated_si_value_ = rhp.user_calculated_si_value_; + direct_lookup_si_value_ = rhp.direct_lookup_si_value_; + provided_metadata_ = rhp.provided_metadata_; + + return *this; + } + + void StatisticInstance::start() { + sparta_assert(direct_lookup_si_value_ == nullptr, + "You cannot call StatisticInstance::start() for an SI " + "that was recreated from a SimDB record"); + + start_tick_ = getScheduler_()->getElapsedTicks(); + end_tick_ = Scheduler::INDEFINITE; + + if(user_calculated_si_value_){ + initial_.resetValue(user_calculated_si_value_->getCurrentValue()); + result_ = NAN; + return; + } + + if(sdef_ != nullptr){ + if(node_ref_.expired() == true){ + throw SpartaException("Cannot start() a StatisticInstance referring to a " + "destructed StatisticDef"); + } + stat_expr_.start(); + initial_.resetValue(0); + }else if(ctr_){ + if(node_ref_.expired() == true){ + throw SpartaException("Cannot start() a StatisticInstance referring to a " + "destructed Counter"); + } + initial_.resetValue(ctr_->get()); + }else if(par_){ + if(node_ref_.expired() == true){ + throw SpartaException("Cannot start() a StatisticInstance referring to a " + "destructed Parameter"); + } + initial_.resetValue(par_->getDoubleValue()); + }else{ + stat_expr_.start(); + } + + // Clear result value + result_ = NAN; + } + + void StatisticInstance::end(){ + sparta_assert(direct_lookup_si_value_ == nullptr, + "You cannot call StatisticInstance::end() for an SI " + "that was recreated from a SimDB record"); + + end_tick_ = getScheduler_()->getElapsedTicks(); + + if(sdef_ != nullptr){ + if(node_ref_.expired() == true){ + throw SpartaException("Cannot end() a StatisticInstance referring to a " + "destructed StatisticDef"); + } + stat_expr_.end(); + }else if(ctr_ != nullptr){ + if(node_ref_.expired() == true){ + throw SpartaException("Cannot end() a StatisticInstance referring to a " + "destructed Counter"); + } + // Do nothing to counter + }else if(par_ != nullptr){ + if(node_ref_.expired() == true){ + throw SpartaException("Cannot end() a StatisticInstance referring to a " + "destructed Parameter"); + } + // Do nothing to Parameter + }else{ + stat_expr_.end(); + } + + // Recompute result value + result_ = computeValue_(); + } + + double StatisticInstance::getValue() const { + if (direct_lookup_si_value_ != nullptr) { + return computeValue_(); + } + + if(SPARTA_EXPECT_FALSE(end_tick_ < start_tick_)) { + throw ReversedStatisticRange("Range is reversed. End < start"); + } + + if(SPARTA_EXPECT_FALSE(start_tick_ > getScheduler_()->getElapsedTicks())) { + throw FutureStatisticRange("Range starts in the future at ") << start_tick_; + } + + double value; + if(end_tick_ == Scheduler::INDEFINITE){ + // Compute Value + value = computeValue_(); + } + + else if(SPARTA_EXPECT_FALSE(end_tick_ > getScheduler_()->getElapsedTicks())) { + // Rang ends in the future - probable because of a checkpoint + throw FutureStatisticRange("Range ends in the future at ") << end_tick_; + } + + else { + // End tick <= current tick. Use pre-computed value because this + // window ended in the past + value = result_; + } + + //Update any snapshot loggers that are listening for these updates + for (auto & logger : snapshot_loggers_) { + logger.takeSnapshot(value); + } + return value; + } + + double StatisticInstance::getRawLatest() const + { + if(sdef_){ + if(node_ref_.expired() == true){ + return NAN; + } + // Evaluate the expression + return stat_expr_.evaluate(); + }else if(ctr_){ + if(node_ref_.expired() == true){ + return NAN; + } + return ctr_->get(); + }else if(par_){ + if(node_ref_.expired() == true){ + return NAN; + } + return par_->getDoubleValue(); + }else{ + return stat_expr_.evaluate(); + } + + return NAN; + } + + bool StatisticInstance::supportsCompression() const { + if (user_calculated_si_value_) { + return false; + } + if (sdef_) { + if (node_ref_.expired()) { + return false; + } + return stat_expr_.supportsCompression(); + } else if (ctr_) { + if (node_ref_.expired()) { + return false; + } + return ctr_->supportsCompression(); + } else if (par_) { + if (node_ref_.expired()) { + return false; + } + return par_->supportsCompression(); + } + + return stat_expr_.supportsCompression(); + } + + std::string StatisticInstance::stringize(bool show_range, + bool resolve_subexprs) const { + std::stringstream ss; + ss << "getLocation(); + }else{ + ss << ""; + } + }else{ + ss << "expression: " << getExpressionString(show_range, + resolve_subexprs); + } + + // Range + if(show_range){ + ss << " [" << start_tick_ << ","; + if(end_tick_ == Scheduler::INDEFINITE){ + ss << "now"; + }else{ + ss << end_tick_; + } + ss << "]"; + } + + // Value + //! \note Could produce nan, -nan, -inf, +inf, or inf depending on glibc + ss << " = " << getValue() << ">"; + return ss.str(); + } + + std::string StatisticInstance::getExpressionString(bool show_range, + bool resolve_subexprs) const { + if(provided_expr_string_.isValid()) { + return provided_expr_string_.getValue(); + } + if(sdef_){ + if(node_ref_.expired() == false){ + // Print the fully rendered expression string instead of the + // string used to construct the StatisticDef node + return stat_expr_.stringize(show_range, resolve_subexprs); + //return sdef_->getExpression(resolve_subexprs); + }else{ + return ""; + } + }else if(ctr_){ + if(node_ref_.expired() == false){ + return ctr_->getLocation(); + }else{ + return ""; + } + }else if(par_){ + if(node_ref_.expired() == false){ + return par_->getLocation(); + }else{ + return ""; + } + }else{ + return stat_expr_.stringize(show_range, resolve_subexprs); + } + } + + std::string StatisticInstance::getDesc(bool show_stat_node_expressions) const { + if(provided_description_.isValid()) { + return provided_description_.getValue(); + } + if(sdef_){ + if(node_ref_.expired() == false){ + std::string result = sdef_->getDesc(); + if(show_stat_node_expressions){ + result += " "; + result += stat_expr_.stringize(false, // show_range + true); // result_subexprs; + } + return result; + }else{ + return ""; + } + }else if(ctr_){ + if(node_ref_.expired() == false){ + return ctr_->getDesc(); + }else{ + return ""; + } + }else if(par_){ + if(node_ref_.expired() == false){ + return par_->getDesc(); + }else{ + return ""; + } + } + + std::string result = "Free Expression: "; + result += stat_expr_.stringize(false, // show_range + true); // result_subexprs + return result; + } + + void StatisticInstance::dump(std::ostream& o, bool show_range) const { + // Source + if(false == node_ref_.expired()){ + o << node_ref_.lock()->getLocation() << " # " + << getExpressionString(); + }else{ + o << ""; + } + + // Range + if(show_range){ + o << " [" << start_tick_ << ","; + if(end_tick_ == Scheduler::INDEFINITE){ + o << "now"; + }else{ + o << end_tick_; + } + o << "]"; + } + + // Value + o << " = " << getValue(); + } + + std::string StatisticInstance::getLocation() const { + if(provided_location_.isValid()) { + return provided_location_.getValue(); + } + if(sdef_){ + if(node_ref_.expired() == false){ + return node_ref_.lock()->getLocation(); + }else{ + return ""; + } + }else if(ctr_){ + if(node_ref_.expired() == false){ + return node_ref_.lock()->getLocation(); + }else{ + return ""; + } + }else if(par_){ + if(node_ref_.expired() == false){ + return node_ref_.lock()->getLocation(); + }else{ + return ""; + } + }else{ + return ""; + } + } + + StatisticDef::ValueSemantic StatisticInstance::getValueSemantic() const { + if(provided_value_semantic_.isValid()) { + return provided_value_semantic_.getValue(); + } + if(sdef_){ + if(node_ref_.expired() == false){ + return sdef_->getValueSemantic(); + }else{ + return StatisticDef::VS_INVALID; + } + }else if(ctr_){ + return StatisticDef::VS_INVALID; + }else if(par_){ + return StatisticDef::VS_INVALID; + }else{ + return StatisticDef::VS_INVALID; + } + } + + InstrumentationNode::visibility_t StatisticInstance::getVisibility() const { + if(provided_visibility_.isValid()) { + return provided_visibility_.getValue(); + } + if(node_ref_.expired()) { + return InstrumentationNode::VIS_NORMAL; + } + if(sdef_){ + return sdef_->getVisibility(); + }else if(ctr_){ + return ctr_->getVisibility(); + }else if(par_){ + return InstrumentationNode::VIS_NORMAL; // Use normal for parameters for now + } + + return InstrumentationNode::VIS_NORMAL; + } + + InstrumentationNode::class_t StatisticInstance::getClass() const { + if(provided_class_.isValid()) { + return provided_class_.getValue(); + } + if(node_ref_.expired()) { + return InstrumentationNode::DEFAULT_CLASS; + } + if(sdef_){ + return sdef_->getClass(); + }else if(ctr_){ + return ctr_->getClass(); + }else if(par_){ + return InstrumentationNode::DEFAULT_CLASS; // Use normal for parameters for now + } + + return InstrumentationNode::DEFAULT_CLASS; + } + + void StatisticInstance::getClocks(std::vector& clocks) const { + if(sdef_){ + if(node_ref_.expired() == true){ + throw SpartaException("Cannot getClocks() on a StatisticInstance refering to " + "an expired TreeNode reference"); + } + + stat_expr_.getClocks(clocks); + }else if(ctr_){ + if(node_ref_.expired() == true){ + throw SpartaException("Cannot getClocks() on a Counter refering to " + "an expired TreeNode reference"); + } + + const Clock* clk = node_ref_.lock()->getClock(); + if(clk != nullptr){ + clocks.push_back(clk); + } + }else{ + stat_expr_.getClocks(clocks); + } + } + + void StatisticInstance::setSIValueDirectLookupPlaceholder( + const std::shared_ptr & direct_lookup) + { + direct_lookup_si_value_ = direct_lookup; + } + + void StatisticInstance::realizeSIValueDirectLookup( + const StatInstRowIterator & si_row_iterator) + { + if (direct_lookup_si_value_ != nullptr) { + auto realized_lookup = direct_lookup_si_value_-> + realizePlaceholder(si_row_iterator.getRowAccessor()); + + sparta_assert(realized_lookup != nullptr); + direct_lookup_si_value_.reset(realized_lookup); + } + } + + bool StatisticInstance::isSIValueDirectLookupValid() const + { + if (direct_lookup_si_value_ == nullptr) { + return false; + } + + //The following function call throws if this direct + //lookup object is a placeholders::StatInstValueLookup + //which has not yet been realized. + try { + return direct_lookup_si_value_->isIndexValidForCurrentRow(); + } catch (...) { + } + + return false; + } + + double StatisticInstance::computeValue_() const { + if(user_calculated_si_value_){ + return user_calculated_si_value_->getCurrentValue() - getInitial(); + } + if(direct_lookup_si_value_){ + return getCurrentValueFromDirectLookup_(); + } + if(sdef_){ + if(node_ref_.expired() == true){ + return NAN; + } + // Evaluate the expression + return stat_expr_.evaluate(); + }else if(ctr_){ + if(node_ref_.expired() == true){ + return NAN; + } + if(ctr_->getBehavior() == CounterBase::COUNT_LATEST){ + return ctr_->get(); + }else{ + // Compute the delta + return ctr_->get() - getInitial(); + } + }else if(par_){ + if(node_ref_.expired() == true){ + return NAN; + } + return par_->getDoubleValue(); + }else{ + return stat_expr_.evaluate(); + } + } + + const Scheduler * StatisticInstance::getScheduler_() const { + if (scheduler_) { + return scheduler_; + } + + sparta_assert(false == node_ref_.expired(), + "This node has expired and taken the Scheduler with it"); + + const Clock * clk = nullptr; + if (sdef_) { + clk = sdef_->getClock(); + } else if (ctr_) { + clk = ctr_->getClock(); + } else if (par_) { + clk = par_->getClock(); + } + if (clk) { + scheduler_ = clk->getScheduler(); + } + + // Should always be able to fall back on singleton scheduler + sparta_assert(nullptr != scheduler_); + return scheduler_; + } + + double StatisticInstance::getCurrentValueFromDirectLookup_() const + { + if (direct_lookup_si_value_ == nullptr) { + throw SpartaException("StatisticInstance asked for its SI ") + << "value from a null direct-lookup object"; + } + + sparta_assert(getInitial() == 0, + "Unexpectedly encountered a StatisticInstance that " + "was created from a SimDB record, but whose SI offset " + "value (SI::getInitial()) was not zero. This is a bug."); + + //The following function call throws if this direct + //lookup object is a placeholders::StatInstValueLookup + //which has not yet been realized. + try { + return direct_lookup_si_value_->getCurrentValue(); + } catch (...) { + } + + return NAN; + } + +} diff --git a/sparta/test/Report/Report_test.cpp b/sparta/test/Report/Report_test.cpp index 0cc3f7fd79..e64800b19b 100644 --- a/sparta/test/Report/Report_test.cpp +++ b/sparta/test/Report/Report_test.cpp @@ -535,6 +535,25 @@ R"(name: "String-based report Autopopulation Test" r9.setContext(root.getSearchScope()); r9.addFile("test_report_multi_nested.yaml"); + // issue #311: referring to pre-defined expressions + const std::string report_def2 = +R"(name: "String-based report Autopopulation Test" +content: + top: + subreport: + name: Self Referring + content: + core0.stats: + c1 : "" + c2 : "" + "c1 + c2": c1_plus_c2 + c1_plus_c2 : "" + "c1_plus_c2 + cycles": c1_plus_c2_plus_cycles + )"; + + Report r10; + r10.setContext(root.getSearchScope()); + r10.addDefinitionString(report_def2); // Create a report formatter to which we will append data over time sparta::report::format::CSV periodic_csv(&r5, "test_periodic.csv", std::ios::out); @@ -592,6 +611,10 @@ R"(name: "String-based report Autopopulation Test" // Write report to a few files + // Write the self-referring SI test + sparta::report::format::Text txt_10(&r10); + txt_10.writeTo("test_self_referring.txt"); + // dumb "dump" of report directly (no formatter) std::ofstream("test_report_out", std::ios::out) << r; std::ofstream("test_wildcard_report_out", std::ios::out) << r5; @@ -683,7 +706,7 @@ R"(name: "String-based report Autopopulation Test" (void) subreps; std::cout << "Num subreports : " << r.getNumSubreports() << std::endl; std::cout << "Subreport depth : " << r.getSubreportDepth() << std::endl; - const std::vector& immediate_stats = r.getStatistics(); + const sparta::statistics::StatisticPairs& immediate_stats = r.getStatistics(); (void) immediate_stats; std::cout << "Num immediate stats: " << r.getNumStatistics() << std::endl; std::cout << "Num recursive stats: " << r.getRecursiveNumStatistics() << std::endl;