Commit 079cf662 by Abseil Team Committed by Andy Getz

Export of internal Abseil changes

--
51e9291d9bc385082b4061fe760b236ba59c79c3 by Abseil Team <absl-team@google.com>:

Cast to uint64_t using braces instead of parentheses.

PiperOrigin-RevId: 372475909

--
939fc409855da2639dcaf2d1d4971ca0e0568d03 by Martijn Vels <mvels@google.com>:

Expand # flat nodes into size detailed counters.

PiperOrigin-RevId: 372474608

--
54b158c99b32f8a14821ce74fed0f9f836525ce7 by Martijn Vels <mvels@google.com>:

Make copies of sampled cords sampled as well

This applies to the following methods:
- Cord(const Cord&)
- operator=(const Cord&)
- Subcord(...)

PiperOrigin-RevId: 372468160

--
876c2581ce008871464e8b471efbb967d150f83b by Andy Getzendanner <durandal@google.com>:

Document the type of an ABSL_FLAGS.OnUpdate() callback.

PiperOrigin-RevId: 372406390
GitOrigin-RevId: 51e9291d9bc385082b4061fe760b236ba59c79c3
Change-Id: Ifb75122cae56b66c28128aee90a63bbb28d93817
parent 70b29fe5
...@@ -265,6 +265,8 @@ ABSL_NAMESPACE_END ...@@ -265,6 +265,8 @@ ABSL_NAMESPACE_END
// //
// ABSL_FLAG(T, name, default_value, help).OnUpdate(callback); // ABSL_FLAG(T, name, default_value, help).OnUpdate(callback);
// //
// `callback` should be convertible to `void (*)()`.
//
// After any setting of the flag value, the callback will be called at least // After any setting of the flag value, the callback will be called at least
// once. A rapid sequence of changes may be merged together into the same // once. A rapid sequence of changes may be merged together into the same
// callback. No concurrent calls to the callback will be made for the same // callback. No concurrent calls to the callback will be made for the same
...@@ -279,7 +281,6 @@ ABSL_NAMESPACE_END ...@@ -279,7 +281,6 @@ ABSL_NAMESPACE_END
// Note: ABSL_FLAG.OnUpdate() does not have a public definition. Hence, this // Note: ABSL_FLAG.OnUpdate() does not have a public definition. Hence, this
// comment serves as its API documentation. // comment serves as its API documentation.
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Implementation details below this section // Implementation details below this section
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
......
...@@ -111,7 +111,7 @@ struct FloatTraits<double> { ...@@ -111,7 +111,7 @@ struct FloatTraits<double> {
return sign ? -ldexp(mantissa, exponent) : ldexp(mantissa, exponent); return sign ? -ldexp(mantissa, exponent) : ldexp(mantissa, exponent);
#else #else
constexpr uint64_t kMantissaMask = constexpr uint64_t kMantissaMask =
(uint64_t(1) << (kTargetMantissaBits - 1)) - 1; (uint64_t{1} << (kTargetMantissaBits - 1)) - 1;
uint64_t dbl = static_cast<uint64_t>(sign) << 63; uint64_t dbl = static_cast<uint64_t>(sign) << 63;
if (mantissa > kMantissaMask) { if (mantissa > kMantissaMask) {
// Normal value. // Normal value.
...@@ -151,7 +151,7 @@ struct FloatTraits<float> { ...@@ -151,7 +151,7 @@ struct FloatTraits<float> {
return sign ? -ldexpf(mantissa, exponent) : ldexpf(mantissa, exponent); return sign ? -ldexpf(mantissa, exponent) : ldexpf(mantissa, exponent);
#else #else
constexpr uint32_t kMantissaMask = constexpr uint32_t kMantissaMask =
(uint32_t(1) << (kTargetMantissaBits - 1)) - 1; (uint32_t{1} << (kTargetMantissaBits - 1)) - 1;
uint32_t flt = static_cast<uint32_t>(sign) << 31; uint32_t flt = static_cast<uint32_t>(sign) << 31;
if (mantissa > kMantissaMask) { if (mantissa > kMantissaMask) {
// Normal value. // Normal value.
...@@ -499,7 +499,7 @@ bool MustRoundUp(uint64_t guess_mantissa, int guess_exponent, ...@@ -499,7 +499,7 @@ bool MustRoundUp(uint64_t guess_mantissa, int guess_exponent,
template <typename FloatType> template <typename FloatType>
CalculatedFloat CalculatedFloatFromRawValues(uint64_t mantissa, int exponent) { CalculatedFloat CalculatedFloatFromRawValues(uint64_t mantissa, int exponent) {
CalculatedFloat result; CalculatedFloat result;
if (mantissa == uint64_t(1) << FloatTraits<FloatType>::kTargetMantissaBits) { if (mantissa == uint64_t{1} << FloatTraits<FloatType>::kTargetMantissaBits) {
mantissa >>= 1; mantissa >>= 1;
exponent += 1; exponent += 1;
} }
......
...@@ -531,18 +531,19 @@ void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { ...@@ -531,18 +531,19 @@ void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) {
assert(&src != this); assert(&src != this);
assert(is_tree() || src.is_tree()); assert(is_tree() || src.is_tree());
auto constexpr method = CordzUpdateTracker::kAssignCord; auto constexpr method = CordzUpdateTracker::kAssignCord;
if (CordRep* tree = this->tree()) { if (ABSL_PREDICT_TRUE(!is_tree())) {
CordzUpdateScope scope(data_.cordz_info(), method); EmplaceTree(CordRep::Ref(src.as_tree()), src.data_, method);
CordRep::Unref(tree); return;
}
CordRep* tree = as_tree();
if (CordRep* src_tree = src.tree()) { if (CordRep* src_tree = src.tree()) {
SetTree(CordRep::Ref(src_tree), scope); data_.set_tree(CordRep::Ref(src_tree));
CordzInfo::MaybeTrackCord(data_, src.data_, method);
} else { } else {
scope.SetCordRep(nullptr); CordzInfo::MaybeUntrackCord(data_.cordz_info());
data_ = src.data_; data_ = src.data_;
} }
} else { CordRep::Unref(tree);
EmplaceTree(CordRep::Ref(src.as_tree()), method);
}
} }
void Cord::InlineRep::UnrefTree() { void Cord::InlineRep::UnrefTree() {
......
...@@ -1000,12 +1000,12 @@ constexpr Cord::InlineRep::InlineRep(cord_internal::InlineData data) ...@@ -1000,12 +1000,12 @@ constexpr Cord::InlineRep::InlineRep(cord_internal::InlineData data)
: data_(data) {} : data_(data) {}
inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src)
: data_(src.data_) { : data_(InlineData::kDefaultInit) {
if (is_tree()) { if (CordRep* tree = src.tree()) {
data_.clear_cordz_info(); EmplaceTree(CordRep::Ref(tree), src.data_,
absl::cord_internal::CordRep::Ref(as_tree());
CordzInfo::MaybeTrackCord(data_, src.data_,
CordzUpdateTracker::kConstructorCord); CordzUpdateTracker::kConstructorCord);
} else {
data_ = src.data_;
} }
} }
......
...@@ -67,6 +67,13 @@ absl::string_view MakeString(TestCordSize size) { ...@@ -67,6 +67,13 @@ absl::string_view MakeString(TestCordSize size) {
return MakeString(Length(size)); return MakeString(Length(size));
} }
// Returns a cord with a sampled method of kAppendString.
absl::Cord MakeAppendStringCord(TestCordSize size) {
absl::Cord cord;
cord.Append(MakeString(size));
return cord;
}
std::string TestParamToString(::testing::TestParamInfo<TestCordSize> size) { std::string TestParamToString(::testing::TestParamInfo<TestCordSize> size) {
return absl::StrCat("On", ToString(size.param), "Cord"); return absl::StrCat("On", ToString(size.param), "Cord");
} }
...@@ -136,6 +143,16 @@ TEST(CordzTest, CopyConstruct) { ...@@ -136,6 +143,16 @@ TEST(CordzTest, CopyConstruct) {
EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord)); EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord));
} }
TEST(CordzTest, CopyConstructFromSampled) {
CordzSamplingIntervalHelper sample_every{1};
Cord src(MakeString(TestCordSize::kLarge));
Cord cord(src);
ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord));
CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics();
EXPECT_THAT(stats.parent_method, Eq(Method::kConstructorString));
EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(1));
}
TEST(CordzTest, MoveConstruct) { TEST(CordzTest, MoveConstruct) {
CordzSamplingIntervalHelper sample_every{1}; CordzSamplingIntervalHelper sample_every{1};
Cord src(MakeString(TestCordSize::kLarge)); Cord src(MakeString(TestCordSize::kLarge));
...@@ -146,7 +163,43 @@ TEST(CordzTest, MoveConstruct) { ...@@ -146,7 +163,43 @@ TEST(CordzTest, MoveConstruct) {
TEST_P(CordzUpdateTest, AssignCord) { TEST_P(CordzUpdateTest, AssignCord) {
Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
cord() = src; cord() = src;
EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAssignCord))); EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignCord));
CordzStatistics stats = GetCordzInfoForTesting(cord())->GetCordzStatistics();
EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0));
}
TEST_P(CordzUpdateTest, AssignSampledCord) {
Cord src = MakeAppendStringCord(TestCordSize::kLarge);
cord() = src;
ASSERT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignCord));
CordzStatistics stats = GetCordzInfoForTesting(cord())->GetCordzStatistics();
EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString));
EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1));
EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0));
}
TEST(CordzUpdateTest, AssignSampledCordToUnsampledCord) {
CordzSamplingIntervalHelper sample_every{1};
Cord src = MakeAppendStringCord(TestCordSize::kLarge);
Cord cord = UnsampledCord(MakeString(TestCordSize::kLarge));
cord = src;
ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord));
CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics();
EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString));
EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1));
EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0));
}
TEST(CordzUpdateTest, AssignSampledCordToSampledCord) {
CordzSamplingIntervalHelper sample_every{1};
Cord src = MakeAppendStringCord(TestCordSize::kLarge);
Cord cord(MakeString(TestCordSize::kLarge));
cord = src;
ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord));
CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics();
EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString));
EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1));
EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0));
} }
TEST(CordzTest, AssignInlinedCord) { TEST(CordzTest, AssignInlinedCord) {
...@@ -160,7 +213,7 @@ TEST(CordzTest, AssignInlinedCord) { ...@@ -160,7 +213,7 @@ TEST(CordzTest, AssignInlinedCord) {
EXPECT_FALSE(CordzInfoIsListed(info)); EXPECT_FALSE(CordzInfoIsListed(info));
} }
TEST(CordzTest, MoveAssignCord) { TEST(CordzUpdateTest, MoveAssignCord) {
CordzSamplingIntervalHelper sample_every{1}; CordzSamplingIntervalHelper sample_every{1};
Cord cord; Cord cord;
Cord src(MakeString(TestCordSize::kLarge)); Cord src(MakeString(TestCordSize::kLarge));
...@@ -168,6 +221,11 @@ TEST(CordzTest, MoveAssignCord) { ...@@ -168,6 +221,11 @@ TEST(CordzTest, MoveAssignCord) {
EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
} }
TEST_P(CordzUpdateTest, AssignLargeArray) {
cord() = MakeString(TestCordSize::kSmall);
EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString));
}
TEST_P(CordzUpdateTest, AssignSmallArray) { TEST_P(CordzUpdateTest, AssignSmallArray) {
cord() = MakeString(TestCordSize::kSmall); cord() = MakeString(TestCordSize::kSmall);
EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString)); EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString));
...@@ -333,16 +391,26 @@ TEST(CordzTest, RemoveSuffix) { ...@@ -333,16 +391,26 @@ TEST(CordzTest, RemoveSuffix) {
TEST(CordzTest, SubCord) { TEST(CordzTest, SubCord) {
CordzSamplingIntervalHelper sample_every{1}; CordzSamplingIntervalHelper sample_every{1};
Cord src(MakeString(TestCordSize::kLarge)); Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
Cord cord = src.Subcord(10, src.size() / 2);
Cord cord1 = src.Subcord(10, src.size() / 2); EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord));
EXPECT_THAT(cord1, HasValidCordzInfoOf(Method::kSubCord)); }
Cord cord2 = src.Subcord(10, kMaxInline + 1); TEST(CordzTest, SmallSubCord) {
EXPECT_THAT(cord2, HasValidCordzInfoOf(Method::kSubCord)); CordzSamplingIntervalHelper sample_every{1};
Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
Cord cord = src.Subcord(10, kMaxInline + 1);
EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord));
}
Cord cord3 = src.Subcord(10, kMaxInline); TEST(CordzTest, SubCordFromSampledCord) {
EXPECT_THAT(GetCordzInfoForTesting(cord3), Eq(nullptr)); CordzSamplingIntervalHelper sample_every{1};
Cord src(MakeString(TestCordSize::kLarge));
Cord cord = src.Subcord(10, src.size() / 2);
EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord));
CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics();
EXPECT_THAT(stats.parent_method, Eq(Method::kConstructorString));
EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(1));
} }
} // namespace } // namespace
......
...@@ -52,7 +52,7 @@ static_assert(std::numeric_limits<double>::digits == 53, "IEEE double fact"); ...@@ -52,7 +52,7 @@ static_assert(std::numeric_limits<double>::digits == 53, "IEEE double fact");
// The lowest valued 19-digit decimal mantissa we can read still contains // The lowest valued 19-digit decimal mantissa we can read still contains
// sufficient information to reconstruct a binary mantissa. // sufficient information to reconstruct a binary mantissa.
static_assert(1000000000000000000u > (uint64_t(1) << (53 + 3)), "(b) above"); static_assert(1000000000000000000u > (uint64_t{1} << (53 + 3)), "(b) above");
// ParseFloat<16> will read the first 15 significant digits of the mantissa. // ParseFloat<16> will read the first 15 significant digits of the mantissa.
// //
......
...@@ -366,6 +366,16 @@ class InlineData { ...@@ -366,6 +366,16 @@ class InlineData {
return as_tree_.cordz_info != kNullCordzInfo; return as_tree_.cordz_info != kNullCordzInfo;
} }
// Returns true if either of the provided instances hold a cordz_info value.
// This method is more efficient than the equivalent `data1.is_profiled() ||
// data2.is_profiled()`. Requires both arguments to hold a tree.
static bool is_either_profiled(const InlineData& data1,
const InlineData& data2) {
assert(data1.is_tree() && data2.is_tree());
return (data1.as_tree_.cordz_info | data2.as_tree_.cordz_info) !=
kNullCordzInfo;
}
// Returns the cordz_info sampling instance for this instance, or nullptr // Returns the cordz_info sampling instance for this instance, or nullptr
// if the current instance is not sampled and does not have CordzInfo data. // if the current instance is not sampled and does not have CordzInfo data.
// Requires the current instance to hold a tree value. // Requires the current instance to hold a tree value.
......
...@@ -191,14 +191,21 @@ TEST(CordzHandleTest, MultiThreaded) { ...@@ -191,14 +191,21 @@ TEST(CordzHandleTest, MultiThreaded) {
// manipulating a CordzHandle that might be operated upon in another thread. // manipulating a CordzHandle that might be operated upon in another thread.
std::vector<std::atomic<CordzHandle*>> handles(kNumHandles); std::vector<std::atomic<CordzHandle*>> handles(kNumHandles);
// global bool which is set when any thread did get some 'safe to inspect'
// handles. On some platforms and OSS tests, we might risk that some pool
// threads are starved, stalled, or just got a few unlikely random 'handle'
// coin tosses, so we satisfy this test with simply observing 'some' thread
// did something meaningful, which should minimize the potential for flakes.
std::atomic<bool> found_safe_to_inspect(false);
{
absl::synchronization_internal::ThreadPool pool(kNumThreads); absl::synchronization_internal::ThreadPool pool(kNumThreads);
for (int i = 0; i < kNumThreads; ++i) { for (int i = 0; i < kNumThreads; ++i) {
pool.Schedule([&stop, &handles]() { pool.Schedule([&stop, &handles, &found_safe_to_inspect]() {
std::minstd_rand gen; std::minstd_rand gen;
std::uniform_int_distribution<int> dist_type(0, 2); std::uniform_int_distribution<int> dist_type(0, 2);
std::uniform_int_distribution<int> dist_handle(0, kNumHandles - 1); std::uniform_int_distribution<int> dist_handle(0, kNumHandles - 1);
size_t max_safe_to_inspect = 0;
while (!stop.HasBeenNotified()) { while (!stop.HasBeenNotified()) {
CordzHandle* handle; CordzHandle* handle;
switch (dist_type(gen)) { switch (dist_type(gen)) {
...@@ -217,27 +224,20 @@ TEST(CordzHandleTest, MultiThreaded) { ...@@ -217,27 +224,20 @@ TEST(CordzHandleTest, MultiThreaded) {
std::vector<const CordzHandle*> safe_to_inspect = std::vector<const CordzHandle*> safe_to_inspect =
old_handle->DiagnosticsGetSafeToInspectDeletedHandles(); old_handle->DiagnosticsGetSafeToInspectDeletedHandles();
for (const CordzHandle* handle : safe_to_inspect) { for (const CordzHandle* handle : safe_to_inspect) {
// We're in a tight loop, so don't generate too many error messages. // We're in a tight loop, so don't generate too many error
// messages.
ASSERT_FALSE(handle->is_snapshot()); ASSERT_FALSE(handle->is_snapshot());
} }
if (safe_to_inspect.size() > max_safe_to_inspect) { if (!safe_to_inspect.empty()) {
max_safe_to_inspect = safe_to_inspect.size(); found_safe_to_inspect.store(true);
} }
CordzHandle::Delete(old_handle); CordzHandle::Delete(old_handle);
} }
} }
// Confirm that the test did *something*. This check will be satisfied as
// long as this thread has delete a CordzSnapshot object and a
// non-snapshot CordzHandle was deleted after the CordzSnapshot was
// created. This max_safe_to_inspect count will often reach around 30
// (assuming 4 threads and 10 slots for live handles). Don't use a strict
// bound to avoid making this test flaky.
EXPECT_THAT(max_safe_to_inspect, Gt(0));
// Have each thread attempt to clean up everything. Some thread will be // Have each thread attempt to clean up everything. Some thread will be
// the last to reach this cleanup code, and it will be guaranteed to clean // the last to reach this cleanup code, and it will be guaranteed to
// up everything because nothing remains to create new handles. // clean up everything because nothing remains to create new handles.
for (auto& h : handles) { for (auto& h : handles) {
if (CordzHandle* handle = h.exchange(nullptr)) { if (CordzHandle* handle = h.exchange(nullptr)) {
CordzHandle::Delete(handle); CordzHandle::Delete(handle);
...@@ -250,6 +250,13 @@ TEST(CordzHandleTest, MultiThreaded) { ...@@ -250,6 +250,13 @@ TEST(CordzHandleTest, MultiThreaded) {
// spot errors. // spot errors.
absl::SleepFor(absl::Seconds(3)); absl::SleepFor(absl::Seconds(3));
stop.Notify(); stop.Notify();
}
// Confirm that the test did *something*. This check will be satisfied as
// long as any thread has deleted a CordzSnapshot object and a non-snapshot
// CordzHandle was deleted after the CordzSnapshot was created.
// See also comments on `found_safe_to_inspect`
EXPECT_TRUE(found_safe_to_inspect.load());
} }
} // namespace } // namespace
......
...@@ -135,6 +135,23 @@ class CordRepAnalyzer { ...@@ -135,6 +135,23 @@ class CordRepAnalyzer {
return (rep != nullptr && rep->tag == CONCAT) ? repref : RepRef{nullptr, 0}; return (rep != nullptr && rep->tag == CONCAT) ? repref : RepRef{nullptr, 0};
} }
// Counts a flat of the provide allocated size
void CountFlat(size_t size) {
statistics_.node_count++;
statistics_.node_counts.flat++;
if (size <= 64) {
statistics_.node_counts.flat_64++;
} else if (size <= 128) {
statistics_.node_counts.flat_128++;
} else if (size <= 256) {
statistics_.node_counts.flat_256++;
} else if (size <= 512) {
statistics_.node_counts.flat_512++;
} else if (size <= 1024) {
statistics_.node_counts.flat_1k++;
}
}
// Processes 'linear' reps (substring, flat, external) not requiring iteration // Processes 'linear' reps (substring, flat, external) not requiring iteration
// or recursion. Returns RefRep{null} if all reps were processed, else returns // or recursion. Returns RefRep{null} if all reps were processed, else returns
// the top-most non-linear concat or ring cordrep. // the top-most non-linear concat or ring cordrep.
...@@ -152,9 +169,9 @@ class CordRepAnalyzer { ...@@ -152,9 +169,9 @@ class CordRepAnalyzer {
// Consume possible FLAT // Consume possible FLAT
if (rep.rep->tag >= FLAT) { if (rep.rep->tag >= FLAT) {
statistics_.node_count++; size_t size = rep.rep->flat()->AllocatedSize();
statistics_.node_counts.flat++; CountFlat(size);
memory_usage.Add(rep.rep->flat()->AllocatedSize(), rep.refcount); memory_usage.Add(size, rep.refcount);
return RepRef{nullptr, 0}; return RepRef{nullptr, 0};
} }
...@@ -263,9 +280,15 @@ void CordzInfo::TrackCord(InlineData& cord, MethodIdentifier method) { ...@@ -263,9 +280,15 @@ void CordzInfo::TrackCord(InlineData& cord, MethodIdentifier method) {
void CordzInfo::TrackCord(InlineData& cord, const InlineData& src, void CordzInfo::TrackCord(InlineData& cord, const InlineData& src,
MethodIdentifier method) { MethodIdentifier method) {
assert(cord.is_tree()); assert(cord.is_tree());
assert(!cord.is_profiled()); assert(src.is_tree());
auto* info = src.is_tree() && src.is_profiled() ? src.cordz_info() : nullptr;
CordzInfo* cordz_info = new CordzInfo(cord.as_tree(), info, method); // Unsample current as we the current cord is being replaced with 'src',
// so any method history is no longer relevant.
CordzInfo* cordz_info = cord.cordz_info();
if (cordz_info != nullptr) cordz_info->Untrack();
// Start new cord sample
cordz_info = new CordzInfo(cord.as_tree(), src.cordz_info(), method);
cord.set_cordz_info(cordz_info); cord.set_cordz_info(cordz_info);
cordz_info->Track(); cordz_info->Track();
} }
...@@ -297,6 +320,10 @@ CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src, ...@@ -297,6 +320,10 @@ CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src,
parent_method_(GetParentMethod(src)), parent_method_(GetParentMethod(src)),
create_time_(absl::Now()) { create_time_(absl::Now()) {
update_tracker_.LossyAdd(method); update_tracker_.LossyAdd(method);
if (src) {
// Copy parent counters.
update_tracker_.LossyAdd(src->update_tracker_);
}
} }
CordzInfo::~CordzInfo() { CordzInfo::~CordzInfo() {
......
...@@ -65,7 +65,9 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { ...@@ -65,7 +65,9 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle {
// Identical to TrackCord(), except that this function fills the // Identical to TrackCord(), except that this function fills the
// `parent_stack` and `parent_method` properties of the returned CordzInfo // `parent_stack` and `parent_method` properties of the returned CordzInfo
// instance from the provided `src` instance if `src` is sampled. // instance from the provided `src` instance if `src` is sampled.
// This function should be used for sampling 'copy constructed' cords. // This function should be used for sampling 'copy constructed' and 'copy
// assigned' cords. This function allows 'cord` to be already sampled, in
// which case the CordzInfo will be newly created from `src`.
static void TrackCord(InlineData& cord, const InlineData& src, static void TrackCord(InlineData& cord, const InlineData& src,
MethodIdentifier method); MethodIdentifier method);
...@@ -73,6 +75,15 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { ...@@ -73,6 +75,15 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle {
// Uses `cordz_should_profile` to randomly pick cords to be sampled, and if // Uses `cordz_should_profile` to randomly pick cords to be sampled, and if
// so, invokes `TrackCord` to start sampling `cord`. // so, invokes `TrackCord` to start sampling `cord`.
static void MaybeTrackCord(InlineData& cord, MethodIdentifier method); static void MaybeTrackCord(InlineData& cord, MethodIdentifier method);
// Maybe sample the cord identified by 'cord' for method 'method'.
// `src` identifies a 'parent' cord which content is copied into the current
// cord, typically the input cord for an assign emthod or copy constructor.
// Invokes the corresponding `TrackCord` method if either cord is sampled, or
// if `cord` is randomly picked for sampling. Possible scenarios:
// * `src` is sampled: `cord` will be set to sampled if not already sampled.
// Parent stack and update stats of `src` are copied into `cord`
// * `src` is not sampled: `cord` may be randomly picked for sampling.
static void MaybeTrackCord(InlineData& cord, const InlineData& src, static void MaybeTrackCord(InlineData& cord, const InlineData& src,
MethodIdentifier method); MethodIdentifier method);
...@@ -221,7 +232,8 @@ inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( ...@@ -221,7 +232,8 @@ inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord(
inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord(
InlineData& cord, const InlineData& src, MethodIdentifier method) { InlineData& cord, const InlineData& src, MethodIdentifier method) {
if (ABSL_PREDICT_FALSE(cordz_should_profile())) { if (ABSL_PREDICT_FALSE(InlineData::is_either_profiled(cord, src)) ||
ABSL_PREDICT_FALSE(cordz_should_profile())) {
TrackCord(cord, src, method); TrackCord(cord, src, method);
} }
} }
......
...@@ -42,10 +42,18 @@ inline void PrintTo(const CordzStatistics& stats, std::ostream* s) { ...@@ -42,10 +42,18 @@ inline void PrintTo(const CordzStatistics& stats, std::ostream* s) {
namespace { namespace {
// Creates a flat of the specified length // Creates a flat of the specified allocated size
CordRepFlat* Flat(int length = 512) { CordRepFlat* Flat(size_t size) {
auto* flat = CordRepFlat::New(length); // Round up to a tag size, as we are going to poke an exact tag size back into
flat->length = length; // the allocated flat. 'size returning allocators' could grant us more than we
// wanted, but we are ok to poke the 'requested' size in the tag, even in the
// presence of sized deletes, so we need to make sure the size rounds
// perfectly to a tag value.
assert(size >= kMinFlatSize);
size = RoundUpForTag(size);
CordRepFlat* flat = CordRepFlat::New(size - kFlatOverhead);
flat->tag = AllocatedSizeToTag(size);
flat->length = size - kFlatOverhead;
return flat; return flat;
} }
...@@ -174,6 +182,11 @@ MATCHER_P(EqStatistics, stats, "Statistics equal expected values") { ...@@ -174,6 +182,11 @@ MATCHER_P(EqStatistics, stats, "Statistics equal expected values") {
STATS_MATCHER_EXPECT_EQ(size); STATS_MATCHER_EXPECT_EQ(size);
STATS_MATCHER_EXPECT_EQ(node_count); STATS_MATCHER_EXPECT_EQ(node_count);
STATS_MATCHER_EXPECT_EQ(node_counts.flat); STATS_MATCHER_EXPECT_EQ(node_counts.flat);
STATS_MATCHER_EXPECT_EQ(node_counts.flat_64);
STATS_MATCHER_EXPECT_EQ(node_counts.flat_128);
STATS_MATCHER_EXPECT_EQ(node_counts.flat_256);
STATS_MATCHER_EXPECT_EQ(node_counts.flat_512);
STATS_MATCHER_EXPECT_EQ(node_counts.flat_1k);
STATS_MATCHER_EXPECT_EQ(node_counts.external); STATS_MATCHER_EXPECT_EQ(node_counts.external);
STATS_MATCHER_EXPECT_EQ(node_counts.concat); STATS_MATCHER_EXPECT_EQ(node_counts.concat);
STATS_MATCHER_EXPECT_EQ(node_counts.substring); STATS_MATCHER_EXPECT_EQ(node_counts.substring);
...@@ -188,7 +201,7 @@ MATCHER_P(EqStatistics, stats, "Statistics equal expected values") { ...@@ -188,7 +201,7 @@ MATCHER_P(EqStatistics, stats, "Statistics equal expected values") {
TEST(CordzInfoStatisticsTest, Flat) { TEST(CordzInfoStatisticsTest, Flat) {
RefHelper ref; RefHelper ref;
auto* flat = ref.NeedsUnref(Flat()); auto* flat = ref.NeedsUnref(Flat(512));
CordzStatistics expected; CordzStatistics expected;
expected.size = flat->length; expected.size = flat->length;
...@@ -196,13 +209,14 @@ TEST(CordzInfoStatisticsTest, Flat) { ...@@ -196,13 +209,14 @@ TEST(CordzInfoStatisticsTest, Flat) {
expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
expected.node_count = 1; expected.node_count = 1;
expected.node_counts.flat = 1; expected.node_counts.flat = 1;
expected.node_counts.flat_512 = 1;
EXPECT_THAT(SampleCord(flat), EqStatistics(expected)); EXPECT_THAT(SampleCord(flat), EqStatistics(expected));
} }
TEST(CordzInfoStatisticsTest, SharedFlat) { TEST(CordzInfoStatisticsTest, SharedFlat) {
RefHelper ref; RefHelper ref;
auto* flat = ref.Ref(ref.NeedsUnref(Flat())); auto* flat = ref.Ref(ref.NeedsUnref(Flat(64)));
CordzStatistics expected; CordzStatistics expected;
expected.size = flat->length; expected.size = flat->length;
...@@ -210,6 +224,7 @@ TEST(CordzInfoStatisticsTest, SharedFlat) { ...@@ -210,6 +224,7 @@ TEST(CordzInfoStatisticsTest, SharedFlat) {
expected.estimated_fair_share_memory_usage = SizeOf(flat) / 2; expected.estimated_fair_share_memory_usage = SizeOf(flat) / 2;
expected.node_count = 1; expected.node_count = 1;
expected.node_counts.flat = 1; expected.node_counts.flat = 1;
expected.node_counts.flat_64 = 1;
EXPECT_THAT(SampleCord(flat), EqStatistics(expected)); EXPECT_THAT(SampleCord(flat), EqStatistics(expected));
} }
...@@ -244,7 +259,7 @@ TEST(CordzInfoStatisticsTest, SharedExternal) { ...@@ -244,7 +259,7 @@ TEST(CordzInfoStatisticsTest, SharedExternal) {
TEST(CordzInfoStatisticsTest, Substring) { TEST(CordzInfoStatisticsTest, Substring) {
RefHelper ref; RefHelper ref;
auto* flat = Flat(); auto* flat = Flat(1024);
auto* substring = ref.NeedsUnref(Substring(flat)); auto* substring = ref.NeedsUnref(Substring(flat));
CordzStatistics expected; CordzStatistics expected;
...@@ -253,6 +268,7 @@ TEST(CordzInfoStatisticsTest, Substring) { ...@@ -253,6 +268,7 @@ TEST(CordzInfoStatisticsTest, Substring) {
expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
expected.node_count = 2; expected.node_count = 2;
expected.node_counts.flat = 1; expected.node_counts.flat = 1;
expected.node_counts.flat_1k = 1;
expected.node_counts.substring = 1; expected.node_counts.substring = 1;
EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); EXPECT_THAT(SampleCord(substring), EqStatistics(expected));
...@@ -260,7 +276,7 @@ TEST(CordzInfoStatisticsTest, Substring) { ...@@ -260,7 +276,7 @@ TEST(CordzInfoStatisticsTest, Substring) {
TEST(CordzInfoStatisticsTest, SharedSubstring) { TEST(CordzInfoStatisticsTest, SharedSubstring) {
RefHelper ref; RefHelper ref;
auto* flat = ref.Ref(Flat(), 2); auto* flat = ref.Ref(Flat(511), 2);
auto* substring = ref.Ref(ref.NeedsUnref(Substring(flat))); auto* substring = ref.Ref(ref.NeedsUnref(Substring(flat)));
CordzStatistics expected; CordzStatistics expected;
...@@ -270,6 +286,7 @@ TEST(CordzInfoStatisticsTest, SharedSubstring) { ...@@ -270,6 +286,7 @@ TEST(CordzInfoStatisticsTest, SharedSubstring) {
SizeOf(substring) / 2 + SizeOf(flat) / 6; SizeOf(substring) / 2 + SizeOf(flat) / 6;
expected.node_count = 2; expected.node_count = 2;
expected.node_counts.flat = 1; expected.node_counts.flat = 1;
expected.node_counts.flat_512 = 1;
expected.node_counts.substring = 1; expected.node_counts.substring = 1;
EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); EXPECT_THAT(SampleCord(substring), EqStatistics(expected));
...@@ -277,7 +294,7 @@ TEST(CordzInfoStatisticsTest, SharedSubstring) { ...@@ -277,7 +294,7 @@ TEST(CordzInfoStatisticsTest, SharedSubstring) {
TEST(CordzInfoStatisticsTest, Concat) { TEST(CordzInfoStatisticsTest, Concat) {
RefHelper ref; RefHelper ref;
auto* flat1 = Flat(20); auto* flat1 = Flat(300);
auto* flat2 = Flat(2000); auto* flat2 = Flat(2000);
auto* concat = ref.NeedsUnref(Concat(flat1, flat2)); auto* concat = ref.NeedsUnref(Concat(flat1, flat2));
...@@ -288,6 +305,7 @@ TEST(CordzInfoStatisticsTest, Concat) { ...@@ -288,6 +305,7 @@ TEST(CordzInfoStatisticsTest, Concat) {
expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
expected.node_count = 3; expected.node_count = 3;
expected.node_counts.flat = 2; expected.node_counts.flat = 2;
expected.node_counts.flat_512 = 1;
expected.node_counts.concat = 1; expected.node_counts.concat = 1;
EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); EXPECT_THAT(SampleCord(concat), EqStatistics(expected));
...@@ -295,9 +313,9 @@ TEST(CordzInfoStatisticsTest, Concat) { ...@@ -295,9 +313,9 @@ TEST(CordzInfoStatisticsTest, Concat) {
TEST(CordzInfoStatisticsTest, DeepConcat) { TEST(CordzInfoStatisticsTest, DeepConcat) {
RefHelper ref; RefHelper ref;
auto* flat1 = Flat(20); auto* flat1 = Flat(300);
auto* flat2 = Flat(2000); auto* flat2 = Flat(2000);
auto* flat3 = Flat(30); auto* flat3 = Flat(400);
auto* external = External(3000); auto* external = External(3000);
auto* substring = Substring(external); auto* substring = Substring(external);
auto* concat1 = Concat(flat1, flat2); auto* concat1 = Concat(flat1, flat2);
...@@ -313,6 +331,7 @@ TEST(CordzInfoStatisticsTest, DeepConcat) { ...@@ -313,6 +331,7 @@ TEST(CordzInfoStatisticsTest, DeepConcat) {
expected.node_count = 8; expected.node_count = 8;
expected.node_counts.flat = 3; expected.node_counts.flat = 3;
expected.node_counts.flat_512 = 2;
expected.node_counts.external = 1; expected.node_counts.external = 1;
expected.node_counts.concat = 3; expected.node_counts.concat = 3;
expected.node_counts.substring = 1; expected.node_counts.substring = 1;
...@@ -322,9 +341,9 @@ TEST(CordzInfoStatisticsTest, DeepConcat) { ...@@ -322,9 +341,9 @@ TEST(CordzInfoStatisticsTest, DeepConcat) {
TEST(CordzInfoStatisticsTest, DeepSharedConcat) { TEST(CordzInfoStatisticsTest, DeepSharedConcat) {
RefHelper ref; RefHelper ref;
auto* flat1 = Flat(20); auto* flat1 = Flat(40);
auto* flat2 = ref.Ref(Flat(2000), 4); auto* flat2 = ref.Ref(Flat(2000), 4);
auto* flat3 = Flat(30); auto* flat3 = Flat(70);
auto* external = ref.Ref(External(3000)); auto* external = ref.Ref(External(3000));
auto* substring = ref.Ref(Substring(external), 3); auto* substring = ref.Ref(Substring(external), 3);
auto* concat1 = Concat(flat1, flat2); auto* concat1 = Concat(flat1, flat2);
...@@ -339,6 +358,8 @@ TEST(CordzInfoStatisticsTest, DeepSharedConcat) { ...@@ -339,6 +358,8 @@ TEST(CordzInfoStatisticsTest, DeepSharedConcat) {
expected.estimated_fair_share_memory_usage = FairShare(concat); expected.estimated_fair_share_memory_usage = FairShare(concat);
expected.node_count = 8; expected.node_count = 8;
expected.node_counts.flat = 3; expected.node_counts.flat = 3;
expected.node_counts.flat_64 = 1;
expected.node_counts.flat_128 = 1;
expected.node_counts.external = 1; expected.node_counts.external = 1;
expected.node_counts.concat = 3; expected.node_counts.concat = 3;
expected.node_counts.substring = 1; expected.node_counts.substring = 1;
...@@ -348,9 +369,9 @@ TEST(CordzInfoStatisticsTest, DeepSharedConcat) { ...@@ -348,9 +369,9 @@ TEST(CordzInfoStatisticsTest, DeepSharedConcat) {
TEST(CordzInfoStatisticsTest, Ring) { TEST(CordzInfoStatisticsTest, Ring) {
RefHelper ref; RefHelper ref;
auto* flat1 = Flat(20); auto* flat1 = Flat(240);
auto* flat2 = Flat(2000); auto* flat2 = Flat(2000);
auto* flat3 = Flat(30); auto* flat3 = Flat(70);
auto* external = External(3000); auto* external = External(3000);
CordRepRing* ring = CordRepRing::Create(flat1); CordRepRing* ring = CordRepRing::Create(flat1);
ring = CordRepRing::Append(ring, flat2); ring = CordRepRing::Append(ring, flat2);
...@@ -365,6 +386,8 @@ TEST(CordzInfoStatisticsTest, Ring) { ...@@ -365,6 +386,8 @@ TEST(CordzInfoStatisticsTest, Ring) {
expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
expected.node_count = 5; expected.node_count = 5;
expected.node_counts.flat = 3; expected.node_counts.flat = 3;
expected.node_counts.flat_128 = 1;
expected.node_counts.flat_256 = 1;
expected.node_counts.external = 1; expected.node_counts.external = 1;
expected.node_counts.ring = 1; expected.node_counts.ring = 1;
...@@ -373,9 +396,9 @@ TEST(CordzInfoStatisticsTest, Ring) { ...@@ -373,9 +396,9 @@ TEST(CordzInfoStatisticsTest, Ring) {
TEST(CordzInfoStatisticsTest, SharedSubstringRing) { TEST(CordzInfoStatisticsTest, SharedSubstringRing) {
RefHelper ref; RefHelper ref;
auto* flat1 = ref.Ref(Flat(20)); auto* flat1 = ref.Ref(Flat(240));
auto* flat2 = Flat(200); auto* flat2 = Flat(200);
auto* flat3 = Flat(30); auto* flat3 = Flat(70);
auto* external = ref.Ref(External(3000), 5); auto* external = ref.Ref(External(3000), 5);
CordRepRing* ring = CordRepRing::Create(flat1); CordRepRing* ring = CordRepRing::Create(flat1);
ring = CordRepRing::Append(ring, flat2); ring = CordRepRing::Append(ring, flat2);
...@@ -392,6 +415,8 @@ TEST(CordzInfoStatisticsTest, SharedSubstringRing) { ...@@ -392,6 +415,8 @@ TEST(CordzInfoStatisticsTest, SharedSubstringRing) {
expected.estimated_fair_share_memory_usage = FairShare(substring); expected.estimated_fair_share_memory_usage = FairShare(substring);
expected.node_count = 6; expected.node_count = 6;
expected.node_counts.flat = 3; expected.node_counts.flat = 3;
expected.node_counts.flat_128 = 1;
expected.node_counts.flat_256 = 2;
expected.node_counts.external = 1; expected.node_counts.external = 1;
expected.node_counts.ring = 1; expected.node_counts.ring = 1;
expected.node_counts.substring = 1; expected.node_counts.substring = 1;
...@@ -447,7 +472,7 @@ TEST(CordzInfoStatisticsTest, ThreadSafety) { ...@@ -447,7 +472,7 @@ TEST(CordzInfoStatisticsTest, ThreadSafety) {
cord.set_inline_size(0); cord.set_inline_size(0);
} else { } else {
// 50/50 Ring or Flat coin toss // 50/50 Ring or Flat coin toss
CordRep* rep = Flat(); CordRep* rep = Flat(256);
rep = (coin_toss(gen) != 0) ? CordRepRing::Create(rep) : rep; rep = (coin_toss(gen) != 0) ? CordRepRing::Create(rep) : rep;
cord.make_tree(rep); cord.make_tree(rep);
......
...@@ -74,6 +74,20 @@ TEST(CordzInfoTest, TrackCord) { ...@@ -74,6 +74,20 @@ TEST(CordzInfoTest, TrackCord) {
info->Untrack(); info->Untrack();
} }
TEST(CordzInfoTest, MaybeTrackCordOnSampledCord) {
TestCordData data1;
CordzInfo::TrackCord(data1.data, kTrackCordMethod);
CordzInfo* info1 = data1.data.cordz_info();
TestCordData data2;
CordzInfo::MaybeTrackCord(data2.data, data1.data, kTrackCordMethod);
CordzInfo* info2 = data2.data.cordz_info();
ASSERT_THAT(info2, Ne(nullptr));
EXPECT_THAT(info2->GetCordRepForTesting(), Eq(data2.rep.rep));
info2->Untrack();
info1->Untrack();
}
TEST(CordzInfoTest, UntrackCord) { TEST(CordzInfoTest, UntrackCord) {
TestCordData data; TestCordData data;
CordzInfo::TrackCord(data.data, kTrackCordMethod); CordzInfo::TrackCord(data.data, kTrackCordMethod);
...@@ -291,19 +305,6 @@ TEST(CordzInfoTest, FromParent) { ...@@ -291,19 +305,6 @@ TEST(CordzInfoTest, FromParent) {
info_child->Untrack(); info_child->Untrack();
} }
TEST(CordzInfoTest, FromParentInlined) {
InlineData parent;
TestCordData child;
CordzInfo* info = TrackChildCord(child.data, parent);
EXPECT_TRUE(info->GetParentStack().empty());
CordzStatistics statistics = info->GetCordzStatistics();
EXPECT_THAT(statistics.size, Eq(child.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));
info->Untrack();
}
} // namespace } // namespace
} // namespace cord_internal } // namespace cord_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
......
...@@ -30,11 +30,16 @@ struct CordzStatistics { ...@@ -30,11 +30,16 @@ struct CordzStatistics {
// Node counts information // Node counts information
struct NodeCounts { struct NodeCounts {
size_t flat = 0; size_t flat = 0; // #flats
size_t external = 0; size_t flat_64 = 0; // #flats up to 64 bytes
size_t substring = 0; size_t flat_128 = 0; // #flats up to 128 bytes
size_t concat = 0; size_t flat_256 = 0; // #flats up to 256 bytes
size_t ring = 0; size_t flat_512 = 0; // #flats up to 512 bytes
size_t flat_1k = 0; // #flats up to 1K bytes
size_t external = 0; // #external reps
size_t substring = 0; // #substring reps
size_t concat = 0; // #concat reps
size_t ring = 0; // #ring buffer reps
}; };
// The size of the cord in bytes. This matches the result of Cord::size(). // The size of the cord in bytes. This matches the result of Cord::size().
......
...@@ -91,6 +91,16 @@ class CordzUpdateTracker { ...@@ -91,6 +91,16 @@ class CordzUpdateTracker {
std::memory_order_relaxed); std::memory_order_relaxed);
} }
// Adds all the values from `src` to this instance
void LossyAdd(const CordzUpdateTracker& src) {
for (int i = 0; i < kNumMethods; ++i) {
MethodIdentifier method = static_cast<MethodIdentifier>(i);
if (int64_t value = src.Value(method)) {
LossyAdd(method, value);
}
}
}
private: private:
// Until C++20 std::atomic is not constexpr default-constructible, so we need // Until C++20 std::atomic is not constexpr default-constructible, so we need
// a wrapper for this class to be constexpr constructible. // a wrapper for this class to be constexpr constructible.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment