@@ -93,17 +93,22 @@ void CopyRecordedData(hdr_histogram* target, const hdr_histogram* source) {
9393}
9494} // namespace
9595
96- std::shared_ptr<Histogram> Histogram::Clone () const {
97- // The layout is fixed when the histogram is created, so the copy can be
98- // allocated without holding the lock.
99- hdr_histogram* copy ;
96+ std::shared_ptr<Histogram> Histogram::CreateWithSameLayout () const {
97+ // The layout is fixed when the histogram is created, so it can be read
98+ // without holding the lock.
99+ hdr_histogram* histogram ;
100100 if (hdr_init (histogram_->lowest_discernible_value ,
101101 histogram_->highest_trackable_value ,
102102 histogram_->significant_figures ,
103- © ) != 0 ) {
103+ &histogram ) != 0 ) {
104104 return {};
105105 }
106- auto clone = std::make_shared<Histogram>(HistogramPointer (copy), Options{});
106+ return std::make_shared<Histogram>(HistogramPointer (histogram), Options{});
107+ }
108+
109+ std::shared_ptr<Histogram> Histogram::Clone () const {
110+ std::shared_ptr<Histogram> clone = CreateWithSameLayout ();
111+ if (!clone) return {};
107112
108113 // Every member that holds recorded or statistical state must be copied
109114 // here. The recorded snapshot cache is not copied; the clone builds its own
@@ -112,6 +117,7 @@ std::shared_ptr<Histogram> Histogram::Clone() const {
112117 CopyRecordedData (clone->histogram_ .get (), histogram_.get ());
113118 clone->prev_ = prev_;
114119 clone->exceeds_ = exceeds_;
120+ clone->reset_count_ = reset_count_;
115121 clone->ewma_alpha_ = ewma_alpha_;
116122 clone->ewma_mean_ = ewma_mean_;
117123 clone->ewma_variance_ = ewma_variance_;
@@ -121,6 +127,59 @@ std::shared_ptr<Histogram> Histogram::Clone() const {
121127 return clone;
122128}
123129
130+ std::shared_ptr<Histogram> Histogram::Diff (const Histogram& other,
131+ DiffError* error) const {
132+ // Counts are subtracted index by index, so both histograms must map values
133+ // to the same indexes. None of these fields change after creation.
134+ if (!IsCompatible (other) || histogram_->normalizing_index_offset !=
135+ other.histogram_ ->normalizing_index_offset ) {
136+ *error = DiffError::kIncompatible ;
137+ return {};
138+ }
139+
140+ std::shared_ptr<Histogram> diff = CreateWithSameLayout ();
141+ if (!diff) {
142+ *error = DiffError::kOutOfMemory ;
143+ return {};
144+ }
145+
146+ // Only the recorded values and the exceeds count carry over. EWMA and timing
147+ // state cannot be subtracted.
148+ uint64_t reset_count;
149+ {
150+ RwLock::ScopedReadLock lock (mutex_);
151+ CopyRecordedData (diff->histogram_ .get (), histogram_.get ());
152+ diff->exceeds_ = exceeds_;
153+ reset_count = reset_count_;
154+ }
155+
156+ // `diff` is not shared yet, so only the lock of `other` is needed from here
157+ // on. Never holding both locks at once avoids lock ordering issues.
158+ RwLock::ScopedReadLock lock (other.mutex_ );
159+ if (reset_count != other.reset_count_ ) {
160+ *error = DiffError::kReset ;
161+ return {};
162+ }
163+ if (diff->exceeds_ < other.exceeds_ ) {
164+ *error = DiffError::kNotEarlier ;
165+ return {};
166+ }
167+
168+ hdr_histogram* target = diff->histogram_ .get ();
169+ const hdr_histogram* source = other.histogram_ .get ();
170+ for (int32_t i = 0 ; i < target->counts_len ; i++) {
171+ if (target->counts [i] < source->counts [i]) {
172+ *error = DiffError::kNotEarlier ;
173+ return {};
174+ }
175+ target->counts [i] -= source->counts [i];
176+ }
177+ diff->exceeds_ -= other.exceeds_ ;
178+ hdr_reset_internal_counters (target);
179+ *error = DiffError::kNone ;
180+ return diff;
181+ }
182+
124183void Histogram::MemoryInfo (MemoryTracker* tracker) const {
125184 tracker->TrackFieldWithSize (" histogram" , GetMemorySize ());
126185 tracker->TrackFieldWithSize (" qrde_snapshot" ,
@@ -313,6 +372,7 @@ double Histogram::Subtract(const Histogram& other) {
313372 }
314373 hdr_reset_internal_counters (histogram_.get ());
315374 InvalidateRecordedSnapshot ();
375+ reset_count_++;
316376 exceeds_ = (exceeds_ > other.exceeds_ ) ? exceeds_ - other.exceeds_ : 0 ;
317377 return static_cast <double >(dropped);
318378 };
@@ -1844,6 +1904,8 @@ void HistogramImpl::AddMethods(Isolate* isolate, Local<FunctionTemplate> tmpl) {
18441904 &fast_get_ewma_error_rate_);
18451905 SetProtoMethodNoSideEffect (isolate, tmpl, " export" , DoExport);
18461906 SetProtoMethodNoSideEffect (isolate, tmpl, " snapshot" , DoSnapshot);
1907+ SetProtoMethodNoSideEffect (isolate, tmpl, " diff" , DoDiff);
1908+ SetProtoMethodNoSideEffect (isolate, tmpl, " resetCount" , GetResetCount);
18471909 SetFastMethod (isolate, instance, " reset" , DoReset, &fast_reset_);
18481910}
18491911
@@ -1894,6 +1956,8 @@ void HistogramImpl::RegisterExternalReferences(
18941956 registry->Register (GetEwmaErrorRate);
18951957 registry->Register (DoExport);
18961958 registry->Register (DoSnapshot);
1959+ registry->Register (DoDiff);
1960+ registry->Register (GetResetCount);
18971961 registry->Register (fast_get_ewma_mean_);
18981962 registry->Register (fast_get_ewma_stddev_);
18991963 registry->Register (fast_get_ewma_error_rate_);
@@ -3054,6 +3118,39 @@ void HistogramImpl::DoSnapshot(const FunctionCallbackInfo<Value>& args) {
30543118 if (result) args.GetReturnValue ().Set (result->object ());
30553119}
30563120
3121+ void HistogramImpl::DoDiff (const FunctionCallbackInfo<Value>& args) {
3122+ Environment* env = Environment::GetCurrent (args);
3123+ HistogramImpl* histogram = HistogramImpl::FromJSObject (args.This ());
3124+ HistogramImpl* other = HistogramImpl::FromJSObject (args[0 ]);
3125+ Histogram::DiffError error;
3126+ std::shared_ptr<Histogram> diff =
3127+ (*histogram)->Diff (*(other->histogram ()), &error);
3128+ switch (error) {
3129+ case Histogram::DiffError::kNone :
3130+ break ;
3131+ case Histogram::DiffError::kOutOfMemory :
3132+ return THROW_ERR_MEMORY_ALLOCATION_FAILED (env);
3133+ case Histogram::DiffError::kIncompatible :
3134+ return THROW_ERR_INVALID_ARG_VALUE (
3135+ env, " other must have the same configuration as the histogram" );
3136+ case Histogram::DiffError::kReset :
3137+ return THROW_ERR_INVALID_STATE (
3138+ env, " Values were removed from the histogram after other was taken" );
3139+ case Histogram::DiffError::kNotEarlier :
3140+ return THROW_ERR_INVALID_ARG_VALUE (
3141+ env, " other contains values that are not in the histogram" );
3142+ }
3143+
3144+ BaseObjectPtr<HistogramBase> result =
3145+ HistogramBase::Create (env, std::move (diff));
3146+ if (result) args.GetReturnValue ().Set (result->object ());
3147+ }
3148+
3149+ void HistogramImpl::GetResetCount (const FunctionCallbackInfo<Value>& args) {
3150+ HistogramImpl* histogram = HistogramImpl::FromJSObject (args.This ());
3151+ args.GetReturnValue ().Set (static_cast <double >((*histogram)->ResetCount ()));
3152+ }
3153+
30573154void HistogramImpl::GetPercentilesAt (const FunctionCallbackInfo<Value>& args) {
30583155 Environment* env = Environment::GetCurrent (args);
30593156 HistogramImpl* histogram = HistogramImpl::FromJSObject (args.This ());
0 commit comments