diff options
Diffstat (limited to 'absl/strings/internal')
-rw-r--r-- | absl/strings/internal/cordz_info.cc | 78 | ||||
-rw-r--r-- | absl/strings/internal/cordz_info.h | 67 | ||||
-rw-r--r-- | absl/strings/internal/cordz_info_test.cc | 76 | ||||
-rw-r--r-- | absl/strings/internal/cordz_statistics.h | 12 | ||||
-rw-r--r-- | absl/strings/internal/cordz_update_tracker.h | 22 | ||||
-rw-r--r-- | absl/strings/internal/cordz_update_tracker_test.cc | 25 |
6 files changed, 220 insertions, 60 deletions
diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index 4dec63d5..6540d134 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -19,6 +19,7 @@ #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/synchronization/mutex.h" #include "absl/types/span.h" @@ -44,18 +45,15 @@ CordzInfo* CordzInfo::Next(const CordzSnapshot& snapshot) const { return ci_next_unsafe(); } -CordzInfo* CordzInfo::TrackCord(CordRep* rep, const CordzInfo* src) { - CordzInfo* ci = new CordzInfo(rep); - if (src) { - ci->parent_stack_depth_ = src->stack_depth_; - memcpy(ci->parent_stack_, src->stack_, sizeof(void*) * src->stack_depth_); - } +CordzInfo* CordzInfo::TrackCord(CordRep* rep, const CordzInfo* src, + MethodIdentifier method) { + CordzInfo* ci = new CordzInfo(rep, src, method); ci->Track(); return ci; } -CordzInfo* CordzInfo::TrackCord(CordRep* rep) { - return TrackCord(rep, nullptr); +CordzInfo* CordzInfo::TrackCord(CordRep* rep, MethodIdentifier method) { + return TrackCord(rep, nullptr, method); } void CordzInfo::UntrackCord(CordzInfo* cordz_info) { @@ -66,12 +64,35 @@ void CordzInfo::UntrackCord(CordzInfo* cordz_info) { } } -CordzInfo::CordzInfo(CordRep* rep) +CordzInfo::MethodIdentifier CordzInfo::GetParentMethod(const CordzInfo* src) { + if (src == nullptr) return MethodIdentifier::kUnknown; + return src->parent_method_ != MethodIdentifier::kUnknown ? src->parent_method_ + : src->method_; +} + +int CordzInfo::FillParentStack(const CordzInfo* src, void** stack) { + assert(stack); + if (src == nullptr) return 0; + if (src->parent_stack_depth_) { + memcpy(stack, src->parent_stack_, src->parent_stack_depth_ * sizeof(void*)); + return src->parent_stack_depth_; + } + memcpy(stack, src->stack_, src->stack_depth_ * sizeof(void*)); + return src->stack_depth_; +} + +CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src, + MethodIdentifier method) : rep_(rep), stack_depth_(absl::GetStackTrace(stack_, /*max_depth=*/kMaxStackDepth, /*skip_count=*/1)), - parent_stack_depth_(0), - create_time_(absl::Now()) {} + parent_stack_depth_(FillParentStack(src, parent_stack_)), + method_(method), + parent_method_(GetParentMethod(src)), + create_time_(absl::Now()), + size_(rep->length) { + update_tracker_.LossyAdd(method); +} CordzInfo::~CordzInfo() { // `rep_` is potentially kept alive if CordzInfo is included @@ -96,7 +117,7 @@ void CordzInfo::Untrack() { { // TODO(b/117940323): change this to assuming ownership instead once all // Cord logic is properly keeping `rep_` in sync with the Cord root rep. - absl::MutexLock lock(&mutex()); + absl::MutexLock lock(&mutex_); rep_ = nullptr; } @@ -120,9 +141,31 @@ void CordzInfo::Untrack() { } } +void CordzInfo::Lock(MethodIdentifier method) + ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_) { + mutex_.Lock(); + update_tracker_.LossyAdd(method); + assert(rep_); +} + +void CordzInfo::Unlock() ABSL_UNLOCK_FUNCTION(mutex_) { + bool tracked = rep_ != nullptr; + if (rep_) { + size_.store(rep_->length); + } + mutex_.Unlock(); + if (!tracked) { + Untrack(); + CordzHandle::Delete(this); + } +} + void CordzInfo::SetCordRep(CordRep* rep) { - mutex().AssertHeld(); + mutex_.AssertHeld(); rep_ = rep; + if (rep) { + size_.store(rep->length); + } } absl::Span<void* const> CordzInfo::GetStack() const { @@ -133,6 +176,15 @@ absl::Span<void* const> CordzInfo::GetParentStack() const { return absl::MakeConstSpan(parent_stack_, parent_stack_depth_); } +CordzStatistics CordzInfo::GetCordzStatistics() const { + CordzStatistics stats; + stats.method = method_; + stats.parent_method = parent_method_; + stats.update_tracker = update_tracker_; + stats.size = size_.load(std::memory_order_relaxed); + return stats; +} + } // namespace cord_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h index c1090a1c..5345be75 100644 --- a/absl/strings/internal/cordz_info.h +++ b/absl/strings/internal/cordz_info.h @@ -24,6 +24,7 @@ #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/synchronization/mutex.h" #include "absl/types/span.h" @@ -42,13 +43,20 @@ namespace cord_internal { // the destructor of a CordzSampleToken object. class CordzInfo : public CordzHandle { public: + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + // All profiled Cords should be accompanied by a call to TrackCord. // TrackCord creates a CordzInfo instance which tracks important metrics of // the sampled cord. CordzInfo instances are placed in a global list which is // used to discover and snapshot all actively tracked cords. // Callers are responsible for calling UntrackCord() before the tracked Cord // instance is deleted, or to stop tracking the sampled Cord. - static CordzInfo* TrackCord(CordRep* rep); + // Callers are also responsible for guarding changes to `rep` through the + // Lock() and Unlock() calls, and calling SetCordRep() if the root of the + // sampled cord changes before the old root has been unreffed and/or deleted. + // `method` identifies the Cord method which initiated the cord to be sampled. + static CordzInfo* TrackCord( + CordRep* rep, MethodIdentifier method = MethodIdentifier::kUnknown); // Stops tracking changes for a sampled cord, and deletes the provided info. // This function must be called before the sampled cord instance is deleted, @@ -58,10 +66,12 @@ class CordzInfo : public CordzHandle { static void UntrackCord(CordzInfo* cordz_info); // Identical to TrackCord(), except that this function fills the - // 'parent_stack' property of the returned CordzInfo instance from the - // provided `src` instance if `src` is not null. + // `parent_stack` and `parent_method` properties of the returned CordzInfo + // instance from the provided `src` instance if `src` is not null. // This function should be used for sampling 'copy constructed' cords. - static CordzInfo* TrackCord(CordRep* rep, const CordzInfo* src); + static CordzInfo* TrackCord( + CordRep* rep, const CordzInfo* src, + MethodIdentifier method = MethodIdentifier::kUnknown); CordzInfo() = delete; CordzInfo(const CordzInfo&) = delete; @@ -73,17 +83,20 @@ class CordzInfo : public CordzHandle { // Retrieves the next oldest existing CordzInfo older than 'this' instance. CordzInfo* Next(const CordzSnapshot& snapshot) const; - // Returns a reference to the mutex guarding the `rep` property of this - // instance. CordzInfo instances hold a weak reference to the rep pointer of - // sampled cords, and rely on Cord logic to update the rep pointer when the - // underlying root tree or ring of the cord changes. - absl::Mutex& mutex() const { return mutex_; } + // Locks this instance for the update identified by `method`. + // Increases the count for `method` in `update_tracker`. + void Lock(MethodIdentifier method) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_); + + // Unlocks this instance. If the contained `rep` has been set to null + // indicating the Cord has been cleared or is otherwise no longer sampled, + // then this method will delete this CordzInfo instance. + void Unlock() ABSL_UNLOCK_FUNCTION(mutex_); // Updates the `rep' property of this instance. This methods is invoked by // Cord logic each time the root node of a sampled Cord changes, and before // the old root reference count is deleted. This guarantees that collection // code can always safely take a reference on the tracked cord. - // Requires `mutex()` to be held. + // Requires a lock to be held through the `Lock()` method. // TODO(b/117940323): annotate with ABSL_EXCLUSIVE_LOCKS_REQUIRED once all // Cord code is in a state where this can be proven true by the compiler. void SetCordRep(CordRep* rep); @@ -106,15 +119,10 @@ class CordzInfo : public CordzHandle { // from, or being assigned the value of an existing (sampled) cord. absl::Span<void* const> GetParentStack() const; - // Retrieve the CordzStatistics associated with this Cord. The statistics are - // only updated when a Cord goes through a mutation, such as an Append or - // RemovePrefix. The refcounts can change due to external events, so the - // reported refcount stats might be incorrect. - CordzStatistics GetCordzStatistics() const { - CordzStatistics stats; - stats.size = size_.load(std::memory_order_relaxed); - return stats; - } + // Retrieves the CordzStatistics associated with this Cord. The statistics + // are only updated when a Cord goes through a mutation, such as an Append + // or RemovePrefix. + CordzStatistics GetCordzStatistics() const; // Records size metric for this CordzInfo instance. void RecordMetrics(int64_t size) { @@ -124,9 +132,21 @@ class CordzInfo : public CordzHandle { private: static constexpr int kMaxStackDepth = 64; - explicit CordzInfo(CordRep* tree); + explicit CordzInfo(CordRep* rep, const CordzInfo* src, + MethodIdentifier method); ~CordzInfo() override; + // Returns the parent method from `src`, which is either `parent_method_` or + // `method_` depending on `parent_method_` being kUnknown. + // Returns kUnknown if `src` is null. + static MethodIdentifier GetParentMethod(const CordzInfo* src); + + // Fills the provided stack from `src`, copying either `parent_stack_` or + // `stack_` depending on `parent_stack_` being empty, returning the size of + // the parent stack. + // Returns 0 if `src` is null. + static int FillParentStack(const CordzInfo* src, void** stack); + void Track(); void Untrack(); @@ -149,12 +169,15 @@ class CordzInfo : public CordzHandle { std::atomic<CordzInfo*> ci_next_ ABSL_GUARDED_BY(ci_mutex_){nullptr}; mutable absl::Mutex mutex_; - CordRep* rep_ ABSL_GUARDED_BY(mutex()); + CordRep* rep_ ABSL_GUARDED_BY(mutex_); void* stack_[kMaxStackDepth]; void* parent_stack_[kMaxStackDepth]; const int stack_depth_; - int parent_stack_depth_; + const int parent_stack_depth_; + const MethodIdentifier method_; + const MethodIdentifier parent_method_; + CordzUpdateTracker update_tracker_; const absl::Time create_time_; // Last recorded size for the cord. diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc index be20e4a4..66acf9b6 100644 --- a/absl/strings/internal/cordz_info_test.cc +++ b/absl/strings/internal/cordz_info_test.cc @@ -23,6 +23,7 @@ #include "absl/debugging/symbolize.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/strings/str_cat.h" #include "absl/types/span.h" @@ -47,6 +48,12 @@ struct TestCordRep { ~TestCordRep() { CordRepFlat::Delete(rep); } }; +// Used test values +auto constexpr kUnknownMethod = CordzUpdateTracker::kUnknown; +auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; +auto constexpr kChildMethod = CordzUpdateTracker::kConstructorCord; +auto constexpr kUpdateMethod = CordzUpdateTracker::kAppendString; + // Local less verbose helper std::vector<const CordzHandle*> DeleteQueue() { return CordzHandle::DiagnosticsGetDeleteQueue(); @@ -90,15 +97,27 @@ TEST(CordzInfoTest, SetCordRep) { CordzInfo* info = CordzInfo::TrackCord(rep.rep); TestCordRep rep2; - { - absl::MutexLock lock(&info->mutex()); - info->SetCordRep(rep2.rep); - } + info->Lock(CordzUpdateTracker::kAppendCord); + info->SetCordRep(rep2.rep); + info->Unlock(); EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep2.rep)); CordzInfo::UntrackCord(info); } +TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) { + TestCordRep rep; + CordzInfo* info = CordzInfo::TrackCord(rep.rep); + + info->Lock(CordzUpdateTracker::kAppendString); + info->SetCordRep(nullptr); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(nullptr)); + EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info)); + + info->Unlock(); + EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr)); +} + #if GTEST_HAS_DEATH_TEST TEST(CordzInfoTest, SetCordRepRequiresMutex) { @@ -191,13 +210,41 @@ TEST(CordzInfoTest, StackV2) { // Local helper functions to get different stacks for child and parent. CordzInfo* TrackChildCord(CordRep* rep, const CordzInfo* parent) { - return CordzInfo::TrackCord(rep, parent); + return CordzInfo::TrackCord(rep, parent, kChildMethod); } CordzInfo* TrackParentCord(CordRep* rep) { - return CordzInfo::TrackCord(rep); + return CordzInfo::TrackCord(rep, kTrackCordMethod); } -TEST(CordzInfoTest, ParentStackV2) { +TEST(CordzInfoTest, GetStatistics) { + TestCordRep rep; + CordzInfo* info = TrackParentCord(rep.rep); + + CordzStatistics statistics = info->GetCordzStatistics(); + EXPECT_THAT(statistics.size, Eq(rep.rep->length)); + EXPECT_THAT(statistics.method, Eq(kTrackCordMethod)); + EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod)); + EXPECT_THAT(statistics.update_tracker.Value(kTrackCordMethod), Eq(1)); + + CordzInfo::UntrackCord(info); +} + +TEST(CordzInfoTest, LockCountsMethod) { + TestCordRep rep; + CordzInfo* info = TrackParentCord(rep.rep); + + info->Lock(kUpdateMethod); + info->Unlock(); + info->Lock(kUpdateMethod); + info->Unlock(); + + CordzStatistics statistics = info->GetCordzStatistics(); + EXPECT_THAT(statistics.update_tracker.Value(kUpdateMethod), Eq(2)); + + CordzInfo::UntrackCord(info); +} + +TEST(CordzInfoTest, FromParent) { TestCordRep rep; CordzInfo* info_parent = TrackParentCord(rep.rep); CordzInfo* info_child = TrackChildCord(rep.rep, info_parent); @@ -206,18 +253,29 @@ TEST(CordzInfoTest, ParentStackV2) { std::string parent_stack = FormatStack(info_child->GetParentStack()); EXPECT_THAT(stack, Eq(parent_stack)); + CordzStatistics statistics = info_child->GetCordzStatistics(); + EXPECT_THAT(statistics.size, Eq(rep.rep->length)); + EXPECT_THAT(statistics.method, Eq(kChildMethod)); + EXPECT_THAT(statistics.parent_method, Eq(kTrackCordMethod)); + EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1)); + CordzInfo::UntrackCord(info_parent); CordzInfo::UntrackCord(info_child); } -TEST(CordzInfoTest, ParentStackEmpty) { +TEST(CordzInfoTest, FromParentNullptr) { TestCordRep rep; CordzInfo* info = TrackChildCord(rep.rep, nullptr); EXPECT_TRUE(info->GetParentStack().empty()); + CordzStatistics statistics = info->GetCordzStatistics(); + EXPECT_THAT(statistics.size, Eq(rep.rep->length)); + EXPECT_THAT(statistics.method, Eq(kChildMethod)); + EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod)); + EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1)); CordzInfo::UntrackCord(info); } -TEST(CordzInfoTest, CordzStatisticsV2) { +TEST(CordzInfoTest, RecordMetrics) { TestCordRep rep; CordzInfo* info = TrackParentCord(rep.rep); diff --git a/absl/strings/internal/cordz_statistics.h b/absl/strings/internal/cordz_statistics.h index ce7c39aa..6e335c0b 100644 --- a/absl/strings/internal/cordz_statistics.h +++ b/absl/strings/internal/cordz_statistics.h @@ -18,6 +18,7 @@ #include <cstdint> #include "absl/base/config.h" +#include "absl/strings/internal/cordz_update_tracker.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -25,6 +26,8 @@ namespace cord_internal { // CordzStatistics captures some meta information about a Cord's shape. struct CordzStatistics { + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + // The size of the cord in bytes. This matches the result of Cord::size(). int64_t size = 0; @@ -46,6 +49,15 @@ struct CordzStatistics { // For ring buffer Cords, this includes the 'ring buffer' node. // A value of 0 implies the property has not been recorded. int64_t node_count = 0; + + // The cord method responsible for sampling the cord. + MethodIdentifier method = MethodIdentifier::kUnknown; + + // The cord method responsible for sampling the parent cord if applicable. + MethodIdentifier parent_method = MethodIdentifier::kUnknown; + + // Update tracker tracking invocation count per cord method. + CordzUpdateTracker update_tracker; }; } // namespace cord_internal diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h index 3c617e93..a090676d 100644 --- a/absl/strings/internal/cordz_update_tracker.h +++ b/absl/strings/internal/cordz_update_tracker.h @@ -38,20 +38,24 @@ class CordzUpdateTracker { public: // Tracked update methods. enum MethodIdentifier { - kAssignString, - kAssignCord, - kMoveAssignCord, - kAppendString, + kUnknown, kAppendCord, - kMoveAppendCord, - kPrependString, - kPrependCord, - kMovePrependCord, kAppendExternalMemory, + kAppendString, + kAssignCord, + kAssignString, + kClear, + kConstructorCord, + kConstructorString, kFlatten, kGetAppendRegion, + kMoveAppendCord, + kMoveAssignCord, + kMovePrependCord, + kPrependCord, + kPrependString, kRemovePrefix, - kRemoveSuffic, + kRemoveSuffix, kSubCord, // kNumMethods defines the number of entries: must be the last entry. diff --git a/absl/strings/internal/cordz_update_tracker_test.cc b/absl/strings/internal/cordz_update_tracker_test.cc index 45782046..eda662f2 100644 --- a/absl/strings/internal/cordz_update_tracker_test.cc +++ b/absl/strings/internal/cordz_update_tracker_test.cc @@ -36,13 +36,24 @@ using Methods = std::array<Method, Method::kNumMethods>; // Returns an array of all methods defined in `MethodIdentifier` Methods AllMethods() { - return Methods{Method::kAssignString, Method::kAssignCord, - Method::kMoveAssignCord, Method::kAppendString, - Method::kAppendCord, Method::kMoveAppendCord, - Method::kPrependString, Method::kPrependCord, - Method::kMovePrependCord, Method::kAppendExternalMemory, - Method::kFlatten, Method::kGetAppendRegion, - Method::kRemovePrefix, Method::kRemoveSuffic, + return Methods{Method::kUnknown, + Method::kAppendCord, + Method::kAppendExternalMemory, + Method::kAppendString, + Method::kAssignCord, + Method::kAssignString, + Method::kClear, + Method::kConstructorCord, + Method::kConstructorString, + Method::kFlatten, + Method::kGetAppendRegion, + Method::kMoveAppendCord, + Method::kMoveAssignCord, + Method::kMovePrependCord, + Method::kPrependCord, + Method::kPrependString, + Method::kRemovePrefix, + Method::kRemoveSuffix, Method::kSubCord}; } |