blob: 65e97d76655ac44f94de1ea5ee18b17604745690 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#ifndef IMPALA_UTIL_RUNTIME_PROFILE_H
#define IMPALA_UTIL_RUNTIME_PROFILE_H
#include <boost/function.hpp>
#include <boost/thread/lock_guard.hpp>
#include <iostream>
#include <rapidjson/document.h>
#include "common/atomic.h"
#include "common/status.h"
#include "util/spinlock.h"
#include "gen-cpp/RuntimeProfile_types.h"
namespace impala {
class ObjectPool;
/// Runtime profile is a group of profiling counters. It supports adding named counters
/// and being able to serialize and deserialize them.
/// The profiles support a tree structure to form a hierarchy of counters.
/// Runtime profiles supports measuring wall clock rate based counters. There is a
/// single thread per process that will convert an amount (i.e. bytes) counter to a
/// corresponding rate based counter. This thread wakes up at fixed intervals and updates
/// all of the rate counters.
///
/// Runtime profile counters can be of several types. See their definition in
/// runtime-profile-counters.h for more details.
///
/// - Counter: Tracks a single value or bitmap. Also serves as the base class for several
/// | other counters.
/// |
/// - AveragedCounter: Maintains a set of child counters. Its current value is the
/// | average of the current values of its children.
/// |
/// - ConcurrentTimerCounter: Wraps a ConcurrentStopWatch to track concurrent running
/// | time for multiple threads.
/// |
/// - DerivedCounter: Computes its current value by calling a function passed during
/// | construction.
/// |
/// - HighWaterMarkCounter: Keeps track of the highest value seen so far.
/// |
/// - SummaryStatsCounter: Keeps track of minimum, maximum, and average value of all
/// values seen so far.
///
/// - EventSequence: Captures a sequence of events, each added by calling MarkEvent().
/// Events have a text label and a time, relative to when the sequence was started.
///
/// - ThreadCounters: Tracks thread runtime information, such as total time, user time,
/// sys time.
///
/// - TimeSeriesCounter (abstract): Keeps track of a value over time. Has two
/// | implementations.
/// |
/// - SamplingTimeSeriesCounter: Maintains a fixed array of 64 values and resamples if
/// | more value than that are added.
/// |
/// - ChunkedTimeSeriesCounter: Maintains an unbounded vector of values. Supports
/// clearing its values after they have been retrieved, and will track the number
/// of previously retrieved values.
///
/// All methods are thread-safe unless otherwise mentioned.
class RuntimeProfile { // NOLINT: This struct is not packed, but there are not so many
// of them that it makes a performance difference
public:
class Counter {
public:
Counter(TUnit::type unit, int64_t value = 0) :
value_(value),
unit_(unit) {
}
virtual ~Counter(){}
virtual void Add(int64_t delta) {
value_.Add(delta);
}
/// Use this to update if the counter is a bitmap
void BitOr(int64_t delta) {
int64_t old;
do {
old = value_.Load();
if (LIKELY((old | delta) == old)) return; // Bits already set, avoid atomic.
} while (UNLIKELY(!value_.CompareAndSwap(old, old | delta)));
}
virtual void Set(int64_t value) { value_.Store(value); }
virtual void Set(int value) { value_.Store(value); }
virtual void Set(double value) {
DCHECK_EQ(sizeof(value), sizeof(int64_t));
value_.Store(*reinterpret_cast<int64_t*>(&value));
}
virtual int64_t value() const { return value_.Load(); }
virtual double double_value() const {
int64_t v = value_.Load();
return *reinterpret_cast<const double*>(&v);
}
/// Builds a new Value into 'val', using (if required) the allocator from
/// 'document'. Should set the following fields where appropriate:
/// counter_name, value, kind, unit
virtual void ToJson(rapidjson::Document& document, rapidjson::Value* val) const;
/// Return the name of the counter type
virtual string CounterType() const {
return "Counter";
}
TUnit::type unit() const { return unit_; }
protected:
friend class RuntimeProfile;
AtomicInt64 value_;
TUnit::type unit_;
};
class AveragedCounter;
class ConcurrentTimerCounter;
class DerivedCounter;
class HighWaterMarkCounter;
class SummaryStatsCounter;
class EventSequence;
class ThreadCounters;
class TimeSeriesCounter;
class SamplingTimeSeriesCounter;
class ChunkedTimeSeriesCounter;
typedef boost::function<int64_t ()> SampleFunction;
/// Create a runtime profile object with 'name'. The profile, counters and any other
/// structures owned by the profile are allocated from 'pool'.
/// If 'is_averaged_profile' is true, the counters in this profile will be derived
/// averages (of unit AveragedCounter) from other profiles, so the counter map will
/// be left empty. Otherwise, the counter map is initialized with a single entry for
/// TotalTime.
static RuntimeProfile* Create(ObjectPool* pool, const std::string& name,
bool is_averaged_profile = false);
~RuntimeProfile();
/// Deserialize from thrift. Runtime profiles are allocated from the pool.
static RuntimeProfile* CreateFromThrift(ObjectPool* pool,
const TRuntimeProfileTree& profiles);
/// Adds a child profile.
/// Checks if 'child' is already added by searching for its name in the
/// child map, and only adds it if the name doesn't exist.
/// 'indent' indicates whether the child will be printed w/ extra indentation
/// relative to the parent.
/// If location is non-null, child will be inserted after location. Location must
/// already be added to the profile.
void AddChild(RuntimeProfile* child,
bool indent = true, RuntimeProfile* location = NULL);
/// Adds a child profile, similarly to AddChild(). The child profile is put before any
/// existing profiles.
void PrependChild(RuntimeProfile* child, bool indent = true);
/// Creates a new child profile with the given 'name'. A child profile with that name
/// must not already exist. If 'prepend' is true, prepended before other child profiles,
/// otherwise appended after other child profiles.
RuntimeProfile* CreateChild(
const std::string& name, bool indent = true, bool prepend = false);
/// Sorts all children according to descending total time. Does not
/// invalidate pointers to profiles.
void SortChildrenByTotalTime();
/// Updates the AveragedCounter counters in this profile with the counters from the
/// 'src' profile. If a counter is present in 'src' but missing in this profile, a new
/// AveragedCounter is created with the same name. This method should not be invoked
/// if is_average_profile_ is false. Obtains locks on the counter maps and child counter
/// maps in both this and 'src' profiles.
void UpdateAverage(RuntimeProfile* src);
/// Updates this profile w/ the thrift profile.
/// Counters and child profiles in thrift_profile that already exist in this profile
/// are updated. Counters that do not already exist are created.
/// Info strings matched up by key and are updated or added, depending on whether
/// the key has already been registered.
/// TODO: Event sequences are ignored
void Update(const TRuntimeProfileTree& thrift_profile);
/// Add a counter with 'name'/'unit'. Returns a counter object that the caller can
/// update. The counter is owned by the RuntimeProfile object.
/// If parent_counter_name is a non-empty string, the counter is added as a child of
/// parent_counter_name.
/// If the counter already exists, the existing counter object is returned.
Counter* AddCounter(const std::string& name, TUnit::type unit,
const std::string& parent_counter_name = "");
/// Adds a counter that tracks the min, max and average values to the runtime profile.
/// Otherwise, same behavior as AddCounter().
SummaryStatsCounter* AddSummaryStatsCounter(const std::string& name, TUnit::type unit,
const std::string& parent_counter_name = "");
/// Adds a high water mark counter to the runtime profile. Otherwise, same behavior
/// as AddCounter().
HighWaterMarkCounter* AddHighWaterMarkCounter(const std::string& name,
TUnit::type unit, const std::string& parent_counter_name = "");
ConcurrentTimerCounter* AddConcurrentTimerCounter(const std::string& name,
TUnit::type unit, const std::string& parent_counter_name = "");
/// Add a derived counter with 'name'/'unit'. The counter is owned by the
/// RuntimeProfile object.
/// If parent_counter_name is a non-empty string, the counter is added as a child of
/// parent_counter_name.
/// Returns NULL if the counter already exists.
DerivedCounter* AddDerivedCounter(const std::string& name, TUnit::type unit,
const SampleFunction& counter_fn,
const std::string& parent_counter_name = "");
/// Add a set of thread counters prefixed with 'prefix'. Returns a ThreadCounters object
/// that the caller can update. The counter is owned by the RuntimeProfile object.
ThreadCounters* AddThreadCounters(const std::string& prefix);
// Add a derived counter to capture the local time. This function can be called at most
// once.
void AddLocalTimeCounter(const SampleFunction& counter_fn);
/// Gets the counter object with 'name'. Returns NULL if there is no counter with
/// that name.
Counter* GetCounter(const std::string& name);
/// Gets the summary stats counter with 'name'. Returns NULL if there is no summary
/// stats counter with that name.
SummaryStatsCounter* GetSummaryStatsCounter(const std::string& name);
/// Adds all counters with 'name' that are registered either in this or
/// in any of the child profiles to 'counters'.
void GetCounters(const std::string& name, std::vector<Counter*>* counters);
/// Adds a string to the runtime profile. If a value already exists for 'key',
/// the value will be updated.
void AddInfoString(const std::string& key, const std::string& value);
/// Same as AddInfoString(), except that this method applies the redaction
/// rules on 'value' before adding it to the runtime profile.
void AddInfoStringRedacted(const std::string& key, const std::string& value);
/// Adds a string to the runtime profile. If a value already exists for 'key',
/// 'value' will be appended to the previous value, with ", " separating them.
void AppendInfoString(const std::string& key, const std::string& value);
/// Helper to append to the "ExecOption" info string.
void AppendExecOption(const std::string& option) {
AppendInfoString("ExecOption", option);
}
/// Helper to append "Codegen Enabled" or "Codegen Disabled" exec options. If
/// specified, 'extra_info' is appended to the exec option, and 'extra_label'
/// is prepended to the exec option.
void AddCodegenMsg(bool codegen_enabled, const std::string& extra_info = "",
const std::string& extra_label = "");
/// Helper wrapper for AddCodegenMsg() that takes a status instead of a string
/// describing why codegen was disabled. 'codegen_status' can be OK whether or
/// not 'codegen_enabled' is true (e.g. if codegen is disabled by a query option,
/// then no error occurred).
void AddCodegenMsg(bool codegen_enabled, const Status& codegen_status,
const std::string& extra_label = "") {
const string& err_msg = codegen_status.ok() ? "" : codegen_status.msg().msg();
AddCodegenMsg(codegen_enabled, err_msg, extra_label);
}
/// Creates and returns a new EventSequence (owned by the runtime
/// profile) - unless a timer with the same 'key' already exists, in
/// which case it is returned.
/// TODO: EventSequences are not merged by Merge() or Update()
EventSequence* AddEventSequence(const std::string& key);
EventSequence* AddEventSequence(const std::string& key, const TEventSequence& from);
/// Returns event sequence with the provided name if it exists, otherwise NULL.
EventSequence* GetEventSequence(const std::string& name) const;
/// Updates 'value' of info string with 'key'. No-op if the key doesn't exist.
void UpdateInfoString(const std::string& key, std::string value);
/// Returns a pointer to the info string value for 'key'. Returns NULL if
/// the key does not exist.
const std::string* GetInfoString(const std::string& key) const;
/// Stops updating all counters in this profile that are periodically updated by a
/// background thread (i.e. sampling, rate, bucketing and time series counters).
/// Must be called before the profile is destroyed if any such counters are active.
/// Does not stop counters on descendant profiles.
void StopPeriodicCounters();
/// Returns the counter for the total elapsed time.
Counter* total_time_counter() { return counter_map_[TOTAL_TIME_COUNTER_NAME]; }
Counter* inactive_timer() { return counter_map_[INACTIVE_TIME_COUNTER_NAME]; }
int64_t local_time() { return local_time_ns_; }
int64_t total_time() { return total_time_ns_; }
/// Prints the contents of the profile in a name: value format.
/// Does not hold locks when it makes any function calls.
void PrettyPrint(std::ostream* s, const std::string& prefix="") const;
/// Serializes profile to thrift.
/// Does not hold locks when it makes any function calls.
void ToThrift(TRuntimeProfileTree* tree) const;
void ToThrift(std::vector<TRuntimeProfileNode>* nodes) const;
/// Store profile into JSON format into a document
void ToJsonHelper(rapidjson::Value* parent, rapidjson::Document* d) const;
void ToJson(rapidjson::Document* d) const;
/// Serializes the runtime profile to a string. This first serializes the
/// object using thrift compact binary format, then gzip compresses it and
/// finally encodes it as base64. This is not a lightweight operation and
/// should not be in the hot path.
Status SerializeToArchiveString(std::string* out) const WARN_UNUSED_RESULT;
Status SerializeToArchiveString(std::stringstream* out) const WARN_UNUSED_RESULT;
/// Deserializes a string into a TRuntimeProfileTree. 'archive_str' is expected to have
/// been serialized by SerializeToArchiveString().
static Status DeserializeFromArchiveString(
const std::string& archive_str, TRuntimeProfileTree* out);
/// Divides all counters by n
void Divide(int n);
void GetChildren(std::vector<RuntimeProfile*>* children);
/// Gets all profiles in tree, including this one.
void GetAllChildren(std::vector<RuntimeProfile*>* children);
/// Returns the number of counters in this profile
int num_counters() const { return counter_map_.size(); }
/// Returns name of this profile
const std::string& name() const { return name_; }
/// *only call this on top-level profiles*
/// (because it doesn't re-file child profiles)
void set_name(const std::string& name) { name_ = name; }
const TRuntimeProfileNodeMetadata& metadata() const { return metadata_; }
/// Called if this corresponds to a plan node. Sets metadata so that later code that
/// analyzes the profile can identify this as the plan node's profile.
void SetPlanNodeId(int node_id);
/// Called if this corresponds to a data sink. Sets metadata so that later code that
/// analyzes the profile can identify this as the data sink's profile.
void SetDataSinkId(int sink_id);
/// Derived counter function: return measured throughput as input_value/second.
static int64_t UnitsPerSecond(const Counter* total_counter, const Counter* timer);
/// Derived counter function: return aggregated value
static int64_t CounterSum(const std::vector<Counter*>* counters);
/// Add a rate counter to the current profile based on src_counter with name.
/// The rate counter is updated periodically based on the src counter.
/// The rate counter has units in src_counter unit per second.
/// StopPeriodicCounters() must be called to stop the periodic updating before this
/// profile is destroyed. The periodic updating can be stopped earlier by calling
/// PeriodicCounterUpdater::StopRateCounter() if 'src_counter' stops changing.
Counter* AddRateCounter(const std::string& name, Counter* src_counter);
/// Same as 'AddRateCounter' above except values are taken by calling fn.
/// The resulting counter will be of 'unit'.
Counter* AddRateCounter(const std::string& name, SampleFunction fn,
TUnit::type unit);
/// Add a sampling counter to the current profile based on src_counter with name.
/// The sampling counter is updated periodically based on the src counter by averaging
/// the samples taken from the src counter.
/// The sampling counter has the same unit as src_counter unit.
/// StopPeriodicCounters() must be called to stop the periodic updating before this
/// profile is destroyed. The periodic updating can be stopped earlier by calling
/// PeriodicCounterUpdater::StopSamplingCounter() if 'src_counter' stops changing.
Counter* AddSamplingCounter(const std::string& name, Counter* src_counter);
/// Same as 'AddSamplingCounter' above except the samples are taken by calling fn.
Counter* AddSamplingCounter(const std::string& name, SampleFunction fn);
/// Create a set of counters, one per bucket, to store the sampled value of src_counter.
/// The 'src_counter' is sampled periodically to obtain the index of the bucket to
/// increment. E.g. if the value of 'src_counter' is 3, the bucket at index 3 is
/// updated. If the index exceeds the index of the last bucket, the last bucket is
/// updated.
///
/// The created counters do not appear in the profile when serialized or
/// pretty-printed. The caller must do its own processing of the counter value
/// (e.g. converting it to an info string).
/// TODO: make this interface more consistent and sane.
///
/// StopPeriodicCounters() must be called to stop the periodic updating before this
/// profile is destroyed. The periodic updating can be stopped earlier by calling
/// PeriodicCounterUpdater::StopBucketingCounters() if 'buckets' stops changing.
std::vector<Counter*>* AddBucketingCounters(Counter* src_counter, int num_buckets);
/// Creates a sampling time series counter. This begins sampling immediately. This
/// counter contains a number of samples that are collected periodically by calling
/// sample_fn(). StopPeriodicCounters() must be called to stop the periodic updating
/// before this profile is destroyed. The periodic updating can be stopped earlier by
/// calling PeriodicCounterUpdater::StopTimeSeriesCounter() if the input stops changing.
/// Note: these counters don't get merged (to make average profiles)
TimeSeriesCounter* AddSamplingTimeSeriesCounter(const std::string& name,
TUnit::type unit, SampleFunction sample_fn);
/// Same as above except the samples are collected from 'src_counter'.
TimeSeriesCounter* AddSamplingTimeSeriesCounter(const std::string& name, Counter*
src_counter);
/// Adds a chunked time series counter to the profile. This begins sampling immediately.
/// This counter will collect new samples periodically by calling 'sample_fn()'. Samples
/// are not re-sampled into larger intervals, instead owners of this profile can call
/// ClearChunkedTimeSeriesCounters() to reset the sample buffers of all chunked time
/// series counters, e.g. after their current values have been transmitted to a remote
/// node for profile aggregation.
TimeSeriesCounter* AddChunkedTimeSeriesCounter(
const std::string& name, TUnit::type unit, SampleFunction sample_fn);
/// Clear all chunked time series counters in this profile and all children.
void ClearChunkedTimeSeriesCounters();
/// Recursively compute the fraction of the 'total_time' spent in this profile and
/// its children.
/// This function updates local_time_percent_ for each profile.
void ComputeTimeInProfile();
/// Set ExecSummary
void SetTExecSummary(const TExecSummary& summary);
/// Get a copy of exec_summary tp t_exec_summary
void GetExecSummary(TExecSummary* t_exec_summary) const;
private:
/// Pool for allocated counters. Usually owned by the creator of this
/// object, but occasionally allocated in the constructor.
ObjectPool* pool_;
/// Name for this runtime profile.
std::string name_;
/// Detailed metadata that identifies the plan node, sink, etc.
TRuntimeProfileNodeMetadata metadata_;
/// True if this profile is an average derived from other profiles.
/// All counters in this profile must be of unit AveragedCounter.
bool is_averaged_profile_;
/// Map from counter names to counters. The profile owns the memory for the
/// counters.
typedef std::map<std::string, Counter*> CounterMap;
CounterMap counter_map_;
/// Map from parent counter name to a set of child counter name.
/// All top level counters are the child of "" (root).
typedef std::map<std::string, std::set<std::string>> ChildCounterMap;
ChildCounterMap child_counter_map_;
/// A set of bucket counters registered in this runtime profile.
std::set<std::vector<Counter*>*> bucketing_counters_;
/// Rate counters, which also appear in 'counter_map_'. Tracked separately to enable
/// stopping the counters.
std::vector<Counter*> rate_counters_;
/// Sampling counters, which also appear in 'counter_map_'. Tracked separately to enable
/// stopping the counters.
std::vector<Counter*> sampling_counters_;
/// Time series counters. These do not appear in 'counter_map_'. Tracked separately
/// because they are displayed separately in the profile and need to be stopped.
typedef std::map<std::string, TimeSeriesCounter*> TimeSeriesCounterMap;
TimeSeriesCounterMap time_series_counter_map_;
/// True if this profile has active periodic counters, including bucketing, rate,
/// sampling and time series counters.
bool has_active_periodic_counters_ = false;
/// Protects counter_map_, child_counter_map_, bucketing_counters_, rate_counters_,
/// sampling_counters_, time_series_counter_map_, and has_active_periodic_counters_.
mutable SpinLock counter_map_lock_;
/// Child profiles. Does not own memory.
/// We record children in both a map (to facilitate updates) and a vector
/// (to print things in the order they were registered)
typedef std::map<std::string, RuntimeProfile*> ChildMap;
ChildMap child_map_;
/// Vector of (profile, indentation flag).
typedef std::vector<std::pair<RuntimeProfile*, bool>> ChildVector;
ChildVector children_;
/// Protects child_map_ and children_.
mutable SpinLock children_lock_;
typedef std::map<std::string, std::string> InfoStrings;
InfoStrings info_strings_;
/// Keeps track of the order in which InfoStrings are displayed when printed.
typedef std::vector<std::string> InfoStringsDisplayOrder;
InfoStringsDisplayOrder info_strings_display_order_;
/// Protects info_strings_ and info_strings_display_order_.
mutable SpinLock info_strings_lock_;
typedef std::map<std::string, EventSequence*> EventSequenceMap;
EventSequenceMap event_sequence_map_;
/// Protects event_sequence_map_.
mutable SpinLock event_sequence_lock_;
typedef std::map<std::string, SummaryStatsCounter*> SummaryStatsCounterMap;
SummaryStatsCounterMap summary_stats_map_;
/// Protects summary_stats_map_.
mutable SpinLock summary_stats_map_lock_;
Counter counter_total_time_;
/// Total time spent waiting (on non-children) that should not be counted when
/// computing local_time_percent_. This is updated for example in the exchange
/// node when waiting on the sender from another fragment.
Counter inactive_timer_;
/// Time spent in just in this profile (i.e. not the children) as a fraction
/// of the total time in the entire profile tree.
double local_time_percent_;
/// Time spent in this node (not including the children). Computed in
/// ComputeTimeInProfile()
int64_t local_time_ns_;
/// Total time spent in this node. Computed in ComputeTimeInProfile() and is
/// the maximum of the total time spent in children and the value of
/// counter_total_time_.
int64_t total_time_ns_;
/// The Exec Summary
TExecSummary t_exec_summary_;
/// Protects exec_summary.
mutable SpinLock t_exec_summary_lock_;
/// Constructor used by Create().
RuntimeProfile(ObjectPool* pool, const std::string& name, bool is_averaged_profile);
/// Update a subtree of profiles from nodes, rooted at *idx.
/// On return, *idx points to the node immediately following this subtree.
void Update(const std::vector<TRuntimeProfileNode>& nodes, int* idx);
/// Helper function to compute compute the fraction of the total time spent in
/// this profile and its children.
/// Called recusively.
void ComputeTimeInProfile(int64_t total_time);
/// Implementation of AddInfoString() and AppendInfoString(). If 'append' is false,
/// implements AddInfoString(), otherwise implements AppendInfoString().
/// Redaction rules are applied on the info string if 'redact' is true.
/// Trailing whitspace is removed.
void AddInfoStringInternal(
const std::string& key, std::string value, bool append, bool redact = false);
/// Send exec_summary to thrift
void ExecSummaryToThrift(TRuntimeProfileTree* tree) const;
/// Name of the counter maintaining the total time.
static const std::string TOTAL_TIME_COUNTER_NAME;
static const std::string LOCAL_TIME_COUNTER_NAME;
static const std::string INACTIVE_TIME_COUNTER_NAME;
/// Create a subtree of runtime profiles from nodes, starting at *node_idx.
/// On return, *node_idx is the index one past the end of this subtree
static RuntimeProfile* CreateFromThrift(
ObjectPool* pool, const std::vector<TRuntimeProfileNode>& nodes, int* node_idx);
/// Internal implementations of the Add*Counter() functions for use when the caller
/// holds counter_map_lock_. Also returns 'created', which is true if a new counter was
/// created and false if a counter with the given name already existed.
Counter* AddCounterLocked(const std::string& name, TUnit::type unit,
const std::string& parent_counter_name, bool* created);
HighWaterMarkCounter* AddHighWaterMarkCounterLocked(const std::string& name,
TUnit::type unit, const std::string& parent_counter_name, bool* created);
ConcurrentTimerCounter* AddConcurrentTimerCounterLocked(const std::string& name,
TUnit::type unit, const std::string& parent_counter_name, bool* created);
/// Inserts 'child' before the iterator 'insert_pos' in 'children_'.
/// 'children_lock_' must be held by the caller.
void AddChildLocked(
RuntimeProfile* child, bool indent, ChildVector::iterator insert_pos);
/// Print the child counters of the given counter name
static void PrintChildCounters(const std::string& prefix,
const std::string& counter_name, const CounterMap& counter_map,
const ChildCounterMap& child_counter_map, std::ostream* s);
/// Add all the counters of this instance into the given parent node in JSON format
/// Args:
/// parent: the root node to add all the counters
/// d: document of this json, could be used to get Allocator
/// counter_name: this will be used to find its child counters in child_counter_map
/// counter_map: A map of counters name to counter
/// child_counter_map: A map of counter to its child counters
void ToJsonCounters(rapidjson::Value* parent, rapidjson::Document* d,
const string& counter_name, const CounterMap& counter_map,
const ChildCounterMap& child_counter_map) const;
};
}
#endif