Export of internal Abseil changes

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

Change Cord constructor(string_view) to explicit make_tree and Cordz tracking

This CL changes the ctor to use an easier to maintain model where Cord code explicitly invokes Cordz update or new / tree logic, which avoids the ambiguity of the 'branched' InlineRep::set_tree code. This removes the need to equip InlineRep with 'MethodIdentifier' or other necessary call info, and also is a cleaner model: InlineRep is carrying too much code now that should plainly sit in Cord, especially with all internal abstractions having moved to InlineData.

See child CL(s) for desired state

PiperOrigin-RevId: 369433619

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

Rename the 'Compare' template type to 'LessThan', as the passed-in function is expected to act like operator<. It is worth avoiding confusion with std::compare, which returns an int (-1/0/1), as due to implicit casting this can lead to hard-to-spot bugs.

PiperOrigin-RevId: 369391118

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

Integrate CordzUpdateTracker into CordzInfo

PiperOrigin-RevId: 369348824

--
771d81ed357496c117179e1daec76eba5155932d by Martijn Vels <mvels@google.com>:

Replace mutex() with Lock() / Unlock() function

Mini design future tracking of CordzInfo sampled cords: CordzInfo holds a CordRep* reference without a reference count. Cord is responsible for synchronizing updates for sampled cords such that the CordRep* contained in CordzInfo is at all times valid. This is done by scoping Lock() and Unlock() calls around the code modifying the code of a sampled cord. For example (using the future CL CordzUpdateScope()):

CordzInfo* cordz_info = get_cordz_info();
CordzUpdateScope scope(cordz_info, CordzUpdateTracker::kRemovePrefix);
CordRep* rep = RemovePrefixImpl(root);
set_tree(rep);
if (cordz_info) {
  cordz_info->SetCordRep(rep);
}

On CordzInfo::Unlock(), if the internal rep is null, the cord is no longer sampled, and CordzInfo will be deleted. Thus any update resulting in the Cord being inlined will automatically no longer be sampled.

PiperOrigin-RevId: 369338802

--
5563c12df04a1e965a03b50bdd032739c55c0706 by Martijn Vels <mvels@google.com>:

Add UpdateTracker to CordzStatistics

PiperOrigin-RevId: 369318178

--
6b4d8463722a3e55a3e8f6cb3741a41055e7f83e by Martijn Vels <mvels@google.com>:

Add kClear, kConstructor* and kUnknown values and fix typo

PiperOrigin-RevId: 369297163

--
041adcbc929789d6d53371a8236840fc350e1eeb by Derek Mauro <dmauro@google.com>:

Switch from malloc to operator new in pool_urbg.cc
so it can only fail by throwing/aborting

PiperOrigin-RevId: 369274087

--
5d97a5f43e3f2d02d0a5bbe586d93b5751812981 by Benjamin Barenblat <bbaren@google.com>:

Correct Thumb function bound computation in the symbolizer

On 32-bit ARM, all functions are aligned to multiples of two bytes, and
the lowest-order bit in a function’s address is ignored by the CPU when
computing branch targets. That bit is still present in instructions and
ELF symbol tables, though; it’s repurposed to indicate whether the
function contains ARM or Thumb code. If the symbolizer doesn’t ignore
that bit, it will believe Thumb functions have boundaries that are off
by one byte, so instruct the symbolizer to null out the lowest-order bit
after retrieving it from the symbol table.

PiperOrigin-RevId: 369254082

--
462bb307c6cc332c1e2c3adb5f0cad51804bf937 by Derek Mauro <dmauro@google.com>:

Add a check for malloc failure in pool_urbg.cc
GitHub #940

PiperOrigin-RevId: 369238100
GitOrigin-RevId: ac1df60490c9583e475e22de7adfc40023196fbf
Change-Id: Ic6ec91c62cd3a0031f6a75a43a83da959ece2d25
pull/946/head
Abseil Team 4 years ago committed by Dino Radaković
parent 732c6540c1
commit 1ae9b71c47
  1. 180
      absl/algorithm/container.h
  2. 10
      absl/debugging/symbolize_elf.inc
  3. 43
      absl/debugging/symbolize_test.cc
  4. 7
      absl/random/internal/pool_urbg.cc
  5. 4
      absl/strings/BUILD.bazel
  6. 4
      absl/strings/CMakeLists.txt
  7. 6
      absl/strings/cord.cc
  8. 3
      absl/strings/cord.h
  9. 78
      absl/strings/internal/cordz_info.cc
  10. 67
      absl/strings/internal/cordz_info.h
  11. 76
      absl/strings/internal/cordz_info_test.cc
  12. 12
      absl/strings/internal/cordz_statistics.h
  13. 22
      absl/strings/internal/cordz_update_tracker.h
  14. 25
      absl/strings/internal/cordz_update_tracker_test.cc

@ -905,11 +905,11 @@ void c_sort(C& c) {
// Overload of c_sort() for performing a `comp` comparison other than the // Overload of c_sort() for performing a `comp` comparison other than the
// default `operator<`. // default `operator<`.
template <typename C, typename Compare> template <typename C, typename LessThan>
void c_sort(C& c, Compare&& comp) { void c_sort(C& c, LessThan&& comp) {
std::sort(container_algorithm_internal::c_begin(c), std::sort(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c), container_algorithm_internal::c_end(c),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_stable_sort() // c_stable_sort()
@ -925,11 +925,11 @@ void c_stable_sort(C& c) {
// Overload of c_stable_sort() for performing a `comp` comparison other than the // Overload of c_stable_sort() for performing a `comp` comparison other than the
// default `operator<`. // default `operator<`.
template <typename C, typename Compare> template <typename C, typename LessThan>
void c_stable_sort(C& c, Compare&& comp) { void c_stable_sort(C& c, LessThan&& comp) {
std::stable_sort(container_algorithm_internal::c_begin(c), std::stable_sort(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c), container_algorithm_internal::c_end(c),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_is_sorted() // c_is_sorted()
@ -944,11 +944,11 @@ bool c_is_sorted(const C& c) {
// c_is_sorted() overload for performing a `comp` comparison other than the // c_is_sorted() overload for performing a `comp` comparison other than the
// default `operator<`. // default `operator<`.
template <typename C, typename Compare> template <typename C, typename LessThan>
bool c_is_sorted(const C& c, Compare&& comp) { bool c_is_sorted(const C& c, LessThan&& comp) {
return std::is_sorted(container_algorithm_internal::c_begin(c), return std::is_sorted(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c), container_algorithm_internal::c_end(c),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_partial_sort() // c_partial_sort()
@ -966,14 +966,14 @@ void c_partial_sort(
// Overload of c_partial_sort() for performing a `comp` comparison other than // Overload of c_partial_sort() for performing a `comp` comparison other than
// the default `operator<`. // the default `operator<`.
template <typename RandomAccessContainer, typename Compare> template <typename RandomAccessContainer, typename LessThan>
void c_partial_sort( void c_partial_sort(
RandomAccessContainer& sequence, RandomAccessContainer& sequence,
container_algorithm_internal::ContainerIter<RandomAccessContainer> middle, container_algorithm_internal::ContainerIter<RandomAccessContainer> middle,
Compare&& comp) { LessThan&& comp) {
std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, std::partial_sort(container_algorithm_internal::c_begin(sequence), middle,
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_partial_sort_copy() // c_partial_sort_copy()
@ -994,15 +994,15 @@ c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) {
// Overload of c_partial_sort_copy() for performing a `comp` comparison other // Overload of c_partial_sort_copy() for performing a `comp` comparison other
// than the default `operator<`. // than the default `operator<`.
template <typename C, typename RandomAccessContainer, typename Compare> template <typename C, typename RandomAccessContainer, typename LessThan>
container_algorithm_internal::ContainerIter<RandomAccessContainer> container_algorithm_internal::ContainerIter<RandomAccessContainer>
c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, c_partial_sort_copy(const C& sequence, RandomAccessContainer& result,
Compare&& comp) { LessThan&& comp) {
return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
container_algorithm_internal::c_begin(result), container_algorithm_internal::c_begin(result),
container_algorithm_internal::c_end(result), container_algorithm_internal::c_end(result),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_is_sorted_until() // c_is_sorted_until()
@ -1018,12 +1018,12 @@ container_algorithm_internal::ContainerIter<C> c_is_sorted_until(C& c) {
// Overload of c_is_sorted_until() for performing a `comp` comparison other than // Overload of c_is_sorted_until() for performing a `comp` comparison other than
// the default `operator<`. // the default `operator<`.
template <typename C, typename Compare> template <typename C, typename LessThan>
container_algorithm_internal::ContainerIter<C> c_is_sorted_until( container_algorithm_internal::ContainerIter<C> c_is_sorted_until(
C& c, Compare&& comp) { C& c, LessThan&& comp) {
return std::is_sorted_until(container_algorithm_internal::c_begin(c), return std::is_sorted_until(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c), container_algorithm_internal::c_end(c),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_nth_element() // c_nth_element()
@ -1043,14 +1043,14 @@ void c_nth_element(
// Overload of c_nth_element() for performing a `comp` comparison other than // Overload of c_nth_element() for performing a `comp` comparison other than
// the default `operator<`. // the default `operator<`.
template <typename RandomAccessContainer, typename Compare> template <typename RandomAccessContainer, typename LessThan>
void c_nth_element( void c_nth_element(
RandomAccessContainer& sequence, RandomAccessContainer& sequence,
container_algorithm_internal::ContainerIter<RandomAccessContainer> nth, container_algorithm_internal::ContainerIter<RandomAccessContainer> nth,
Compare&& comp) { LessThan&& comp) {
std::nth_element(container_algorithm_internal::c_begin(sequence), nth, std::nth_element(container_algorithm_internal::c_begin(sequence), nth,
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -1072,12 +1072,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_lower_bound(
// Overload of c_lower_bound() for performing a `comp` comparison other than // Overload of c_lower_bound() for performing a `comp` comparison other than
// the default `operator<`. // the default `operator<`.
template <typename Sequence, typename T, typename Compare> template <typename Sequence, typename T, typename LessThan>
container_algorithm_internal::ContainerIter<Sequence> c_lower_bound( container_algorithm_internal::ContainerIter<Sequence> c_lower_bound(
Sequence& sequence, T&& value, Compare&& comp) { Sequence& sequence, T&& value, LessThan&& comp) {
return std::lower_bound(container_algorithm_internal::c_begin(sequence), return std::lower_bound(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<T>(value), std::forward<Compare>(comp)); std::forward<T>(value), std::forward<LessThan>(comp));
} }
// c_upper_bound() // c_upper_bound()
@ -1095,12 +1095,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_upper_bound(
// Overload of c_upper_bound() for performing a `comp` comparison other than // Overload of c_upper_bound() for performing a `comp` comparison other than
// the default `operator<`. // the default `operator<`.
template <typename Sequence, typename T, typename Compare> template <typename Sequence, typename T, typename LessThan>
container_algorithm_internal::ContainerIter<Sequence> c_upper_bound( container_algorithm_internal::ContainerIter<Sequence> c_upper_bound(
Sequence& sequence, T&& value, Compare&& comp) { Sequence& sequence, T&& value, LessThan&& comp) {
return std::upper_bound(container_algorithm_internal::c_begin(sequence), return std::upper_bound(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<T>(value), std::forward<Compare>(comp)); std::forward<T>(value), std::forward<LessThan>(comp));
} }
// c_equal_range() // c_equal_range()
@ -1118,12 +1118,12 @@ c_equal_range(Sequence& sequence, T&& value) {
// Overload of c_equal_range() for performing a `comp` comparison other than // Overload of c_equal_range() for performing a `comp` comparison other than
// the default `operator<`. // the default `operator<`.
template <typename Sequence, typename T, typename Compare> template <typename Sequence, typename T, typename LessThan>
container_algorithm_internal::ContainerIterPairType<Sequence, Sequence> container_algorithm_internal::ContainerIterPairType<Sequence, Sequence>
c_equal_range(Sequence& sequence, T&& value, Compare&& comp) { c_equal_range(Sequence& sequence, T&& value, LessThan&& comp) {
return std::equal_range(container_algorithm_internal::c_begin(sequence), return std::equal_range(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<T>(value), std::forward<Compare>(comp)); std::forward<T>(value), std::forward<LessThan>(comp));
} }
// c_binary_search() // c_binary_search()
@ -1140,12 +1140,12 @@ bool c_binary_search(Sequence&& sequence, T&& value) {
// Overload of c_binary_search() for performing a `comp` comparison other than // Overload of c_binary_search() for performing a `comp` comparison other than
// the default `operator<`. // the default `operator<`.
template <typename Sequence, typename T, typename Compare> template <typename Sequence, typename T, typename LessThan>
bool c_binary_search(Sequence&& sequence, T&& value, Compare&& comp) { bool c_binary_search(Sequence&& sequence, T&& value, LessThan&& comp) {
return std::binary_search(container_algorithm_internal::c_begin(sequence), return std::binary_search(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<T>(value), std::forward<T>(value),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -1166,14 +1166,14 @@ OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) {
// Overload of c_merge() for performing a `comp` comparison other than // Overload of c_merge() for performing a `comp` comparison other than
// the default `operator<`. // the default `operator<`.
template <typename C1, typename C2, typename OutputIterator, typename Compare> template <typename C1, typename C2, typename OutputIterator, typename LessThan>
OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result, OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result,
Compare&& comp) { LessThan&& comp) {
return std::merge(container_algorithm_internal::c_begin(c1), return std::merge(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1), container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), result, container_algorithm_internal::c_end(c2), result,
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_inplace_merge() // c_inplace_merge()
@ -1189,13 +1189,13 @@ void c_inplace_merge(C& c,
// Overload of c_inplace_merge() for performing a merge using a `comp` other // Overload of c_inplace_merge() for performing a merge using a `comp` other
// than `operator<`. // than `operator<`.
template <typename C, typename Compare> template <typename C, typename LessThan>
void c_inplace_merge(C& c, void c_inplace_merge(C& c,
container_algorithm_internal::ContainerIter<C> middle, container_algorithm_internal::ContainerIter<C> middle,
Compare&& comp) { LessThan&& comp) {
std::inplace_merge(container_algorithm_internal::c_begin(c), middle, std::inplace_merge(container_algorithm_internal::c_begin(c), middle,
container_algorithm_internal::c_end(c), container_algorithm_internal::c_end(c),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_includes() // c_includes()
@ -1213,13 +1213,13 @@ bool c_includes(const C1& c1, const C2& c2) {
// Overload of c_includes() for performing a merge using a `comp` other than // Overload of c_includes() for performing a merge using a `comp` other than
// `operator<`. // `operator<`.
template <typename C1, typename C2, typename Compare> template <typename C1, typename C2, typename LessThan>
bool c_includes(const C1& c1, const C2& c2, Compare&& comp) { bool c_includes(const C1& c1, const C2& c2, LessThan&& comp) {
return std::includes(container_algorithm_internal::c_begin(c1), return std::includes(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1), container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), container_algorithm_internal::c_end(c2),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_set_union() // c_set_union()
@ -1243,7 +1243,7 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) {
// Overload of c_set_union() for performing a merge using a `comp` other than // Overload of c_set_union() for performing a merge using a `comp` other than
// `operator<`. // `operator<`.
template <typename C1, typename C2, typename OutputIterator, typename Compare, template <typename C1, typename C2, typename OutputIterator, typename LessThan,
typename = typename std::enable_if< typename = typename std::enable_if<
!container_algorithm_internal::IsUnorderedContainer<C1>::value, !container_algorithm_internal::IsUnorderedContainer<C1>::value,
void>::type, void>::type,
@ -1251,12 +1251,12 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare,
!container_algorithm_internal::IsUnorderedContainer<C2>::value, !container_algorithm_internal::IsUnorderedContainer<C2>::value,
void>::type> void>::type>
OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output,
Compare&& comp) { LessThan&& comp) {
return std::set_union(container_algorithm_internal::c_begin(c1), return std::set_union(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1), container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), output, container_algorithm_internal::c_end(c2), output,
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_set_intersection() // c_set_intersection()
@ -1280,7 +1280,7 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2,
// Overload of c_set_intersection() for performing a merge using a `comp` other // Overload of c_set_intersection() for performing a merge using a `comp` other
// than `operator<`. // than `operator<`.
template <typename C1, typename C2, typename OutputIterator, typename Compare, template <typename C1, typename C2, typename OutputIterator, typename LessThan,
typename = typename std::enable_if< typename = typename std::enable_if<
!container_algorithm_internal::IsUnorderedContainer<C1>::value, !container_algorithm_internal::IsUnorderedContainer<C1>::value,
void>::type, void>::type,
@ -1288,12 +1288,12 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare,
!container_algorithm_internal::IsUnorderedContainer<C2>::value, !container_algorithm_internal::IsUnorderedContainer<C2>::value,
void>::type> void>::type>
OutputIterator c_set_intersection(const C1& c1, const C2& c2, OutputIterator c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) { OutputIterator output, LessThan&& comp) {
return std::set_intersection(container_algorithm_internal::c_begin(c1), return std::set_intersection(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1), container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), output, container_algorithm_internal::c_end(c2), output,
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_set_difference() // c_set_difference()
@ -1318,7 +1318,7 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2,
// Overload of c_set_difference() for performing a merge using a `comp` other // Overload of c_set_difference() for performing a merge using a `comp` other
// than `operator<`. // than `operator<`.
template <typename C1, typename C2, typename OutputIterator, typename Compare, template <typename C1, typename C2, typename OutputIterator, typename LessThan,
typename = typename std::enable_if< typename = typename std::enable_if<
!container_algorithm_internal::IsUnorderedContainer<C1>::value, !container_algorithm_internal::IsUnorderedContainer<C1>::value,
void>::type, void>::type,
@ -1326,12 +1326,12 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare,
!container_algorithm_internal::IsUnorderedContainer<C2>::value, !container_algorithm_internal::IsUnorderedContainer<C2>::value,
void>::type> void>::type>
OutputIterator c_set_difference(const C1& c1, const C2& c2, OutputIterator c_set_difference(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) { OutputIterator output, LessThan&& comp) {
return std::set_difference(container_algorithm_internal::c_begin(c1), return std::set_difference(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1), container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), output, container_algorithm_internal::c_end(c2), output,
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_set_symmetric_difference() // c_set_symmetric_difference()
@ -1357,7 +1357,7 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
// Overload of c_set_symmetric_difference() for performing a merge using a // Overload of c_set_symmetric_difference() for performing a merge using a
// `comp` other than `operator<`. // `comp` other than `operator<`.
template <typename C1, typename C2, typename OutputIterator, typename Compare, template <typename C1, typename C2, typename OutputIterator, typename LessThan,
typename = typename std::enable_if< typename = typename std::enable_if<
!container_algorithm_internal::IsUnorderedContainer<C1>::value, !container_algorithm_internal::IsUnorderedContainer<C1>::value,
void>::type, void>::type,
@ -1366,13 +1366,13 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare,
void>::type> void>::type>
OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
OutputIterator output, OutputIterator output,
Compare&& comp) { LessThan&& comp) {
return std::set_symmetric_difference( return std::set_symmetric_difference(
container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1), container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), output, container_algorithm_internal::c_end(c2), output,
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -1391,11 +1391,11 @@ void c_push_heap(RandomAccessContainer& sequence) {
// Overload of c_push_heap() for performing a push operation on a heap using a // Overload of c_push_heap() for performing a push operation on a heap using a
// `comp` other than `operator<`. // `comp` other than `operator<`.
template <typename RandomAccessContainer, typename Compare> template <typename RandomAccessContainer, typename LessThan>
void c_push_heap(RandomAccessContainer& sequence, Compare&& comp) { void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) {
std::push_heap(container_algorithm_internal::c_begin(sequence), std::push_heap(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_pop_heap() // c_pop_heap()
@ -1410,11 +1410,11 @@ void c_pop_heap(RandomAccessContainer& sequence) {
// Overload of c_pop_heap() for performing a pop operation on a heap using a // Overload of c_pop_heap() for performing a pop operation on a heap using a
// `comp` other than `operator<`. // `comp` other than `operator<`.
template <typename RandomAccessContainer, typename Compare> template <typename RandomAccessContainer, typename LessThan>
void c_pop_heap(RandomAccessContainer& sequence, Compare&& comp) { void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) {
std::pop_heap(container_algorithm_internal::c_begin(sequence), std::pop_heap(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_make_heap() // c_make_heap()
@ -1429,11 +1429,11 @@ void c_make_heap(RandomAccessContainer& sequence) {
// Overload of c_make_heap() for performing heap comparisons using a // Overload of c_make_heap() for performing heap comparisons using a
// `comp` other than `operator<` // `comp` other than `operator<`
template <typename RandomAccessContainer, typename Compare> template <typename RandomAccessContainer, typename LessThan>
void c_make_heap(RandomAccessContainer& sequence, Compare&& comp) { void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) {
std::make_heap(container_algorithm_internal::c_begin(sequence), std::make_heap(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_sort_heap() // c_sort_heap()
@ -1448,11 +1448,11 @@ void c_sort_heap(RandomAccessContainer& sequence) {
// Overload of c_sort_heap() for performing heap comparisons using a // Overload of c_sort_heap() for performing heap comparisons using a
// `comp` other than `operator<` // `comp` other than `operator<`
template <typename RandomAccessContainer, typename Compare> template <typename RandomAccessContainer, typename LessThan>
void c_sort_heap(RandomAccessContainer& sequence, Compare&& comp) { void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) {
std::sort_heap(container_algorithm_internal::c_begin(sequence), std::sort_heap(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_is_heap() // c_is_heap()
@ -1467,11 +1467,11 @@ bool c_is_heap(const RandomAccessContainer& sequence) {
// Overload of c_is_heap() for performing heap comparisons using a // Overload of c_is_heap() for performing heap comparisons using a
// `comp` other than `operator<` // `comp` other than `operator<`
template <typename RandomAccessContainer, typename Compare> template <typename RandomAccessContainer, typename LessThan>
bool c_is_heap(const RandomAccessContainer& sequence, Compare&& comp) { bool c_is_heap(const RandomAccessContainer& sequence, LessThan&& comp) {
return std::is_heap(container_algorithm_internal::c_begin(sequence), return std::is_heap(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_is_heap_until() // c_is_heap_until()
@ -1487,12 +1487,12 @@ c_is_heap_until(RandomAccessContainer& sequence) {
// Overload of c_is_heap_until() for performing heap comparisons using a // Overload of c_is_heap_until() for performing heap comparisons using a
// `comp` other than `operator<` // `comp` other than `operator<`
template <typename RandomAccessContainer, typename Compare> template <typename RandomAccessContainer, typename LessThan>
container_algorithm_internal::ContainerIter<RandomAccessContainer> container_algorithm_internal::ContainerIter<RandomAccessContainer>
c_is_heap_until(RandomAccessContainer& sequence, Compare&& comp) { c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) {
return std::is_heap_until(container_algorithm_internal::c_begin(sequence), return std::is_heap_until(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -1513,12 +1513,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_min_element(
// Overload of c_min_element() for performing a `comp` comparison other than // Overload of c_min_element() for performing a `comp` comparison other than
// `operator<`. // `operator<`.
template <typename Sequence, typename Compare> template <typename Sequence, typename LessThan>
container_algorithm_internal::ContainerIter<Sequence> c_min_element( container_algorithm_internal::ContainerIter<Sequence> c_min_element(
Sequence& sequence, Compare&& comp) { Sequence& sequence, LessThan&& comp) {
return std::min_element(container_algorithm_internal::c_begin(sequence), return std::min_element(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_max_element() // c_max_element()
@ -1535,12 +1535,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_max_element(
// Overload of c_max_element() for performing a `comp` comparison other than // Overload of c_max_element() for performing a `comp` comparison other than
// `operator<`. // `operator<`.
template <typename Sequence, typename Compare> template <typename Sequence, typename LessThan>
container_algorithm_internal::ContainerIter<Sequence> c_max_element( container_algorithm_internal::ContainerIter<Sequence> c_max_element(
Sequence& sequence, Compare&& comp) { Sequence& sequence, LessThan&& comp) {
return std::max_element(container_algorithm_internal::c_begin(sequence), return std::max_element(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_minmax_element() // c_minmax_element()
@ -1558,12 +1558,12 @@ c_minmax_element(C& c) {
// Overload of c_minmax_element() for performing `comp` comparisons other than // Overload of c_minmax_element() for performing `comp` comparisons other than
// `operator<`. // `operator<`.
template <typename C, typename Compare> template <typename C, typename LessThan>
container_algorithm_internal::ContainerIterPairType<C, C> container_algorithm_internal::ContainerIterPairType<C, C>
c_minmax_element(C& c, Compare&& comp) { c_minmax_element(C& c, LessThan&& comp) {
return std::minmax_element(container_algorithm_internal::c_begin(c), return std::minmax_element(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c), container_algorithm_internal::c_end(c),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -1588,15 +1588,15 @@ bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) {
// Overload of c_lexicographical_compare() for performing a lexicographical // Overload of c_lexicographical_compare() for performing a lexicographical
// comparison using a `comp` operator instead of `operator<`. // comparison using a `comp` operator instead of `operator<`.
template <typename Sequence1, typename Sequence2, typename Compare> template <typename Sequence1, typename Sequence2, typename LessThan>
bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2, bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2,
Compare&& comp) { LessThan&& comp) {
return std::lexicographical_compare( return std::lexicographical_compare(
container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_begin(sequence1),
container_algorithm_internal::c_end(sequence1), container_algorithm_internal::c_end(sequence1),
container_algorithm_internal::c_begin(sequence2), container_algorithm_internal::c_begin(sequence2),
container_algorithm_internal::c_end(sequence2), container_algorithm_internal::c_end(sequence2),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_next_permutation() // c_next_permutation()
@ -1612,11 +1612,11 @@ bool c_next_permutation(C& c) {
// Overload of c_next_permutation() for performing a lexicographical // Overload of c_next_permutation() for performing a lexicographical
// comparison using a `comp` operator instead of `operator<`. // comparison using a `comp` operator instead of `operator<`.
template <typename C, typename Compare> template <typename C, typename LessThan>
bool c_next_permutation(C& c, Compare&& comp) { bool c_next_permutation(C& c, LessThan&& comp) {
return std::next_permutation(container_algorithm_internal::c_begin(c), return std::next_permutation(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c), container_algorithm_internal::c_end(c),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
// c_prev_permutation() // c_prev_permutation()
@ -1632,11 +1632,11 @@ bool c_prev_permutation(C& c) {
// Overload of c_prev_permutation() for performing a lexicographical // Overload of c_prev_permutation() for performing a lexicographical
// comparison using a `comp` operator instead of `operator<`. // comparison using a `comp` operator instead of `operator<`.
template <typename C, typename Compare> template <typename C, typename LessThan>
bool c_prev_permutation(C& c, Compare&& comp) { bool c_prev_permutation(C& c, LessThan&& comp) {
return std::prev_permutation(container_algorithm_internal::c_begin(c), return std::prev_permutation(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c), container_algorithm_internal::c_end(c),
std::forward<Compare>(comp)); std::forward<LessThan>(comp));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

@ -701,6 +701,16 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
const char *start_address = const char *start_address =
ComputeOffset(original_start_address, relocation); ComputeOffset(original_start_address, relocation);
#ifdef __arm__
// ARM functions are always aligned to multiples of two bytes; the
// lowest-order bit in start_address is ignored by the CPU and indicates
// whether the function contains ARM (0) or Thumb (1) code. We don't care
// about what encoding is being used; we just want the real start address
// of the function.
start_address = reinterpret_cast<const char *>(
reinterpret_cast<uintptr_t>(start_address) & ~1);
#endif
if (deref_function_descriptor_pointer && if (deref_function_descriptor_pointer &&
InSection(original_start_address, opd)) { InSection(original_start_address, opd)) {
// The opd section is mapped into memory. Just dereference // The opd section is mapped into memory. Just dereference

@ -477,6 +477,46 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() {
#endif #endif
} }
#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
// Test that we correctly identify bounds of Thumb functions on ARM.
//
// Thumb functions have the lowest-order bit set in their addresses in the ELF
// symbol table. This requires some extra logic to properly compute function
// bounds. To test this logic, nudge a Thumb function right up against an ARM
// function and try to symbolize the ARM function.
//
// A naive implementation will simply use the Thumb function's entry point as
// written in the symbol table and will therefore treat the Thumb function as
// extending one byte further in the instruction stream than it actually does.
// When asked to symbolize the start of the ARM function, it will identify an
// overlap between the Thumb and ARM functions, and it will return the name of
// the Thumb function.
//
// A correct implementation, on the other hand, will null out the lowest-order
// bit in the Thumb function's entry point. It will correctly compute the end of
// the Thumb function, it will find no overlap between the Thumb and ARM
// functions, and it will return the name of the ARM function.
__attribute__((target("thumb"))) int ArmThumbOverlapThumb(int x) {
return x * x * x;
}
__attribute__((target("arm"))) int ArmThumbOverlapArm(int x) {
return x * x * x;
}
void ABSL_ATTRIBUTE_NOINLINE TestArmThumbOverlap() {
#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE)
const char *symbol = TrySymbolize((void *)&ArmThumbOverlapArm);
ABSL_RAW_CHECK(symbol != nullptr, "TestArmThumbOverlap failed");
ABSL_RAW_CHECK(strcmp("ArmThumbOverlapArm()", symbol) == 0,
"TestArmThumbOverlap failed");
std::cout << "TestArmThumbOverlap passed" << std::endl;
#endif
}
#endif // defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
#elif defined(_WIN32) #elif defined(_WIN32)
#if !defined(ABSL_CONSUME_DLL) #if !defined(ABSL_CONSUME_DLL)
@ -551,6 +591,9 @@ int main(int argc, char **argv) {
TestWithPCInsideInlineFunction(); TestWithPCInsideInlineFunction();
TestWithPCInsideNonInlineFunction(); TestWithPCInsideNonInlineFunction();
TestWithReturnAddress(); TestWithReturnAddress();
#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
TestArmThumbOverlap();
#endif
#endif #endif
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();

@ -194,11 +194,10 @@ RandenPoolEntry* PoolAlignedAlloc() {
// Not all the platforms that we build for have std::aligned_alloc, however // Not all the platforms that we build for have std::aligned_alloc, however
// since we never free these objects, we can over allocate and munge the // since we never free these objects, we can over allocate and munge the
// pointers to the correct alignment. // pointers to the correct alignment.
void* memory = std::malloc(sizeof(RandenPoolEntry) + kAlignment); intptr_t x = reinterpret_cast<intptr_t>(
auto x = reinterpret_cast<intptr_t>(memory); new char[sizeof(RandenPoolEntry) + kAlignment]);
auto y = x % kAlignment; auto y = x % kAlignment;
void* aligned = void* aligned = reinterpret_cast<void*>(y == 0 ? x : (x + kAlignment - y));
(y == 0) ? memory : reinterpret_cast<void*>(x + kAlignment - y);
return new (aligned) RandenPoolEntry(); return new (aligned) RandenPoolEntry();
} }

@ -368,6 +368,7 @@ cc_library(
":cord_internal", ":cord_internal",
":cordz_handle", ":cordz_handle",
":cordz_statistics", ":cordz_statistics",
":cordz_update_tracker",
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/debugging:stacktrace", "//absl/debugging:stacktrace",
@ -406,6 +407,7 @@ cc_library(
hdrs = ["internal/cordz_statistics.h"], hdrs = ["internal/cordz_statistics.h"],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
deps = [ deps = [
":cordz_update_tracker",
"//absl/base:config", "//absl/base:config",
], ],
) )
@ -450,6 +452,8 @@ cc_test(
":cord_internal", ":cord_internal",
":cordz_handle", ":cordz_handle",
":cordz_info", ":cordz_info",
":cordz_statistics",
":cordz_update_tracker",
":strings", ":strings",
"//absl/base:config", "//absl/base:config",
"//absl/debugging:stacktrace", "//absl/debugging:stacktrace",

@ -641,6 +641,7 @@ absl_cc_library(
DEPS DEPS
absl::config absl::config
absl::core_headers absl::core_headers
absl::cordz_update_tracker
absl::synchronization absl::synchronization
) )
@ -690,6 +691,7 @@ absl_cc_library(
absl::cord_internal absl::cord_internal
absl::cordz_handle absl::cordz_handle
absl::cordz_statistics absl::cordz_statistics
absl::cordz_update_tracker
absl::core_headers absl::core_headers
absl::span absl::span
absl::stacktrace absl::stacktrace
@ -707,6 +709,8 @@ absl_cc_test(
absl::cord_test_helpers absl::cord_test_helpers
absl::cordz_handle absl::cordz_handle
absl::cordz_info absl::cordz_info
absl::cordz_statistics
absl::cordz_update_tracker
absl::span absl::span
absl::stacktrace absl::stacktrace
absl::symbolize absl::symbolize

@ -39,6 +39,7 @@
#include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_flat.h"
#include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cord_rep_ring.h"
#include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/cordz_statistics.h"
#include "absl/strings/internal/cordz_update_tracker.h"
#include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h" #include "absl/strings/str_format.h"
@ -544,7 +545,10 @@ Cord::Cord(absl::string_view src) {
if (n <= InlineRep::kMaxInline) { if (n <= InlineRep::kMaxInline) {
contents_.set_data(src.data(), n, false); contents_.set_data(src.data(), n, false);
} else { } else {
contents_.set_tree(NewTree(src.data(), n, 0)); contents_.data_.make_tree(NewTree(src.data(), n, 0));
if (ABSL_PREDICT_FALSE(absl::cord_internal::cordz_should_profile())) {
contents_.StartProfiling();
}
} }
} }

@ -84,6 +84,7 @@
#include "absl/strings/internal/cordz_functions.h" #include "absl/strings/internal/cordz_functions.h"
#include "absl/strings/internal/cordz_info.h" #include "absl/strings/internal/cordz_info.h"
#include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/cordz_statistics.h"
#include "absl/strings/internal/cordz_update_tracker.h"
#include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/internal/string_constant.h" #include "absl/strings/internal/string_constant.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
@ -668,6 +669,8 @@ class Cord {
explicit constexpr Cord(strings_internal::StringConstant<T>); explicit constexpr Cord(strings_internal::StringConstant<T>);
private: private:
using CordzUpdateTracker = cord_internal::CordzUpdateTracker;
friend class CordTestPeer; friend class CordTestPeer;
friend bool operator==(const Cord& lhs, const Cord& rhs); friend bool operator==(const Cord& lhs, const Cord& rhs);
friend bool operator==(const Cord& lhs, absl::string_view rhs); friend bool operator==(const Cord& lhs, absl::string_view rhs);

@ -19,6 +19,7 @@
#include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_internal.h"
#include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_handle.h"
#include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/cordz_statistics.h"
#include "absl/strings/internal/cordz_update_tracker.h"
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "absl/types/span.h" #include "absl/types/span.h"
@ -44,18 +45,15 @@ CordzInfo* CordzInfo::Next(const CordzSnapshot& snapshot) const {
return ci_next_unsafe(); return ci_next_unsafe();
} }
CordzInfo* CordzInfo::TrackCord(CordRep* rep, const CordzInfo* src) { CordzInfo* CordzInfo::TrackCord(CordRep* rep, const CordzInfo* src,
CordzInfo* ci = new CordzInfo(rep); MethodIdentifier method) {
if (src) { CordzInfo* ci = new CordzInfo(rep, src, method);
ci->parent_stack_depth_ = src->stack_depth_;
memcpy(ci->parent_stack_, src->stack_, sizeof(void*) * src->stack_depth_);
}
ci->Track(); ci->Track();
return ci; return ci;
} }
CordzInfo* CordzInfo::TrackCord(CordRep* rep) { CordzInfo* CordzInfo::TrackCord(CordRep* rep, MethodIdentifier method) {
return TrackCord(rep, nullptr); return TrackCord(rep, nullptr, method);
} }
void CordzInfo::UntrackCord(CordzInfo* cordz_info) { 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), : rep_(rep),
stack_depth_(absl::GetStackTrace(stack_, /*max_depth=*/kMaxStackDepth, stack_depth_(absl::GetStackTrace(stack_, /*max_depth=*/kMaxStackDepth,
/*skip_count=*/1)), /*skip_count=*/1)),
parent_stack_depth_(0), parent_stack_depth_(FillParentStack(src, parent_stack_)),
create_time_(absl::Now()) {} method_(method),
parent_method_(GetParentMethod(src)),
create_time_(absl::Now()),
size_(rep->length) {
update_tracker_.LossyAdd(method);
}
CordzInfo::~CordzInfo() { CordzInfo::~CordzInfo() {
// `rep_` is potentially kept alive if CordzInfo is included // `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 // TODO(b/117940323): change this to assuming ownership instead once all
// Cord logic is properly keeping `rep_` in sync with the Cord root rep. // Cord logic is properly keeping `rep_` in sync with the Cord root rep.
absl::MutexLock lock(&mutex()); absl::MutexLock lock(&mutex_);
rep_ = nullptr; 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) { void CordzInfo::SetCordRep(CordRep* rep) {
mutex().AssertHeld(); mutex_.AssertHeld();
rep_ = rep; rep_ = rep;
if (rep) {
size_.store(rep->length);
}
} }
absl::Span<void* const> CordzInfo::GetStack() const { 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_); 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 } // namespace cord_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl

@ -24,6 +24,7 @@
#include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_internal.h"
#include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_handle.h"
#include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/cordz_statistics.h"
#include "absl/strings/internal/cordz_update_tracker.h"
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "absl/types/span.h" #include "absl/types/span.h"
@ -42,13 +43,20 @@ namespace cord_internal {
// the destructor of a CordzSampleToken object. // the destructor of a CordzSampleToken object.
class CordzInfo : public CordzHandle { class CordzInfo : public CordzHandle {
public: public:
using MethodIdentifier = CordzUpdateTracker::MethodIdentifier;
// All profiled Cords should be accompanied by a call to TrackCord. // All profiled Cords should be accompanied by a call to TrackCord.
// TrackCord creates a CordzInfo instance which tracks important metrics of // TrackCord creates a CordzInfo instance which tracks important metrics of
// the sampled cord. CordzInfo instances are placed in a global list which is // the sampled cord. CordzInfo instances are placed in a global list which is
// used to discover and snapshot all actively tracked cords. // used to discover and snapshot all actively tracked cords.
// Callers are responsible for calling UntrackCord() before the tracked Cord // Callers are responsible for calling UntrackCord() before the tracked Cord
// instance is deleted, or to stop tracking the sampled 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. // Stops tracking changes for a sampled cord, and deletes the provided info.
// This function must be called before the sampled cord instance is deleted, // 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); static void UntrackCord(CordzInfo* cordz_info);
// Identical to TrackCord(), except that this function fills the // Identical to TrackCord(), except that this function fills the
// 'parent_stack' property of the returned CordzInfo instance from the // `parent_stack` and `parent_method` properties of the returned CordzInfo
// provided `src` instance if `src` is not null. // instance from the provided `src` instance if `src` is not null.
// This function should be used for sampling 'copy constructed' cords. // 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() = delete;
CordzInfo(const CordzInfo&) = delete; CordzInfo(const CordzInfo&) = delete;
@ -73,17 +83,20 @@ class CordzInfo : public CordzHandle {
// Retrieves the next oldest existing CordzInfo older than 'this' instance. // Retrieves the next oldest existing CordzInfo older than 'this' instance.
CordzInfo* Next(const CordzSnapshot& snapshot) const; CordzInfo* Next(const CordzSnapshot& snapshot) const;
// Returns a reference to the mutex guarding the `rep` property of this // Locks this instance for the update identified by `method`.
// instance. CordzInfo instances hold a weak reference to the rep pointer of // Increases the count for `method` in `update_tracker`.
// sampled cords, and rely on Cord logic to update the rep pointer when the void Lock(MethodIdentifier method) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_);
// underlying root tree or ring of the cord changes.
absl::Mutex& mutex() const { return 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 // 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 // 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 // the old root reference count is deleted. This guarantees that collection
// code can always safely take a reference on the tracked cord. // 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 // 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. // Cord code is in a state where this can be proven true by the compiler.
void SetCordRep(CordRep* rep); void SetCordRep(CordRep* rep);
@ -106,15 +119,10 @@ class CordzInfo : public CordzHandle {
// from, or being assigned the value of an existing (sampled) cord. // from, or being assigned the value of an existing (sampled) cord.
absl::Span<void* const> GetParentStack() const; absl::Span<void* const> GetParentStack() const;
// Retrieve the CordzStatistics associated with this Cord. The statistics are // Retrieves the CordzStatistics associated with this Cord. The statistics
// only updated when a Cord goes through a mutation, such as an Append or // are only updated when a Cord goes through a mutation, such as an Append
// RemovePrefix. The refcounts can change due to external events, so the // or RemovePrefix.
// reported refcount stats might be incorrect. CordzStatistics GetCordzStatistics() const;
CordzStatistics GetCordzStatistics() const {
CordzStatistics stats;
stats.size = size_.load(std::memory_order_relaxed);
return stats;
}
// Records size metric for this CordzInfo instance. // Records size metric for this CordzInfo instance.
void RecordMetrics(int64_t size) { void RecordMetrics(int64_t size) {
@ -124,9 +132,21 @@ class CordzInfo : public CordzHandle {
private: private:
static constexpr int kMaxStackDepth = 64; static constexpr int kMaxStackDepth = 64;
explicit CordzInfo(CordRep* tree); explicit CordzInfo(CordRep* rep, const CordzInfo* src,
MethodIdentifier method);
~CordzInfo() override; ~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 Track();
void Untrack(); void Untrack();
@ -149,12 +169,15 @@ class CordzInfo : public CordzHandle {
std::atomic<CordzInfo*> ci_next_ ABSL_GUARDED_BY(ci_mutex_){nullptr}; std::atomic<CordzInfo*> ci_next_ ABSL_GUARDED_BY(ci_mutex_){nullptr};
mutable absl::Mutex mutex_; mutable absl::Mutex mutex_;
CordRep* rep_ ABSL_GUARDED_BY(mutex()); CordRep* rep_ ABSL_GUARDED_BY(mutex_);
void* stack_[kMaxStackDepth]; void* stack_[kMaxStackDepth];
void* parent_stack_[kMaxStackDepth]; void* parent_stack_[kMaxStackDepth];
const int stack_depth_; 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_; const absl::Time create_time_;
// Last recorded size for the cord. // Last recorded size for the cord.

@ -23,6 +23,7 @@
#include "absl/debugging/symbolize.h" #include "absl/debugging/symbolize.h"
#include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_flat.h"
#include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_handle.h"
#include "absl/strings/internal/cordz_update_tracker.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/types/span.h" #include "absl/types/span.h"
@ -47,6 +48,12 @@ struct TestCordRep {
~TestCordRep() { CordRepFlat::Delete(rep); } ~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 // Local less verbose helper
std::vector<const CordzHandle*> DeleteQueue() { std::vector<const CordzHandle*> DeleteQueue() {
return CordzHandle::DiagnosticsGetDeleteQueue(); return CordzHandle::DiagnosticsGetDeleteQueue();
@ -90,15 +97,27 @@ TEST(CordzInfoTest, SetCordRep) {
CordzInfo* info = CordzInfo::TrackCord(rep.rep); CordzInfo* info = CordzInfo::TrackCord(rep.rep);
TestCordRep rep2; TestCordRep rep2;
{ info->Lock(CordzUpdateTracker::kAppendCord);
absl::MutexLock lock(&info->mutex()); info->SetCordRep(rep2.rep);
info->SetCordRep(rep2.rep); info->Unlock();
}
EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep2.rep)); EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep2.rep));
CordzInfo::UntrackCord(info); 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 #if GTEST_HAS_DEATH_TEST
TEST(CordzInfoTest, SetCordRepRequiresMutex) { TEST(CordzInfoTest, SetCordRepRequiresMutex) {
@ -191,13 +210,41 @@ TEST(CordzInfoTest, StackV2) {
// Local helper functions to get different stacks for child and parent. // Local helper functions to get different stacks for child and parent.
CordzInfo* TrackChildCord(CordRep* rep, const CordzInfo* parent) { CordzInfo* TrackChildCord(CordRep* rep, const CordzInfo* parent) {
return CordzInfo::TrackCord(rep, parent); return CordzInfo::TrackCord(rep, parent, kChildMethod);
} }
CordzInfo* TrackParentCord(CordRep* rep) { 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; TestCordRep rep;
CordzInfo* info_parent = TrackParentCord(rep.rep); CordzInfo* info_parent = TrackParentCord(rep.rep);
CordzInfo* info_child = TrackChildCord(rep.rep, info_parent); CordzInfo* info_child = TrackChildCord(rep.rep, info_parent);
@ -206,18 +253,29 @@ TEST(CordzInfoTest, ParentStackV2) {
std::string parent_stack = FormatStack(info_child->GetParentStack()); std::string parent_stack = FormatStack(info_child->GetParentStack());
EXPECT_THAT(stack, Eq(parent_stack)); 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_parent);
CordzInfo::UntrackCord(info_child); CordzInfo::UntrackCord(info_child);
} }
TEST(CordzInfoTest, ParentStackEmpty) { TEST(CordzInfoTest, FromParentNullptr) {
TestCordRep rep; TestCordRep rep;
CordzInfo* info = TrackChildCord(rep.rep, nullptr); CordzInfo* info = TrackChildCord(rep.rep, nullptr);
EXPECT_TRUE(info->GetParentStack().empty()); 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); CordzInfo::UntrackCord(info);
} }
TEST(CordzInfoTest, CordzStatisticsV2) { TEST(CordzInfoTest, RecordMetrics) {
TestCordRep rep; TestCordRep rep;
CordzInfo* info = TrackParentCord(rep.rep); CordzInfo* info = TrackParentCord(rep.rep);

@ -18,6 +18,7 @@
#include <cstdint> #include <cstdint>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/strings/internal/cordz_update_tracker.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
@ -25,6 +26,8 @@ namespace cord_internal {
// CordzStatistics captures some meta information about a Cord's shape. // CordzStatistics captures some meta information about a Cord's shape.
struct CordzStatistics { struct CordzStatistics {
using MethodIdentifier = CordzUpdateTracker::MethodIdentifier;
// 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().
int64_t size = 0; int64_t size = 0;
@ -46,6 +49,15 @@ struct CordzStatistics {
// For ring buffer Cords, this includes the 'ring buffer' node. // For ring buffer Cords, this includes the 'ring buffer' node.
// A value of 0 implies the property has not been recorded. // A value of 0 implies the property has not been recorded.
int64_t node_count = 0; 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 } // namespace cord_internal

@ -38,20 +38,24 @@ class CordzUpdateTracker {
public: public:
// Tracked update methods. // Tracked update methods.
enum MethodIdentifier { enum MethodIdentifier {
kAssignString, kUnknown,
kAssignCord,
kMoveAssignCord,
kAppendString,
kAppendCord, kAppendCord,
kMoveAppendCord,
kPrependString,
kPrependCord,
kMovePrependCord,
kAppendExternalMemory, kAppendExternalMemory,
kAppendString,
kAssignCord,
kAssignString,
kClear,
kConstructorCord,
kConstructorString,
kFlatten, kFlatten,
kGetAppendRegion, kGetAppendRegion,
kMoveAppendCord,
kMoveAssignCord,
kMovePrependCord,
kPrependCord,
kPrependString,
kRemovePrefix, kRemovePrefix,
kRemoveSuffic, kRemoveSuffix,
kSubCord, kSubCord,
// kNumMethods defines the number of entries: must be the last entry. // kNumMethods defines the number of entries: must be the last entry.

@ -36,13 +36,24 @@ using Methods = std::array<Method, Method::kNumMethods>;
// Returns an array of all methods defined in `MethodIdentifier` // Returns an array of all methods defined in `MethodIdentifier`
Methods AllMethods() { Methods AllMethods() {
return Methods{Method::kAssignString, Method::kAssignCord, return Methods{Method::kUnknown,
Method::kMoveAssignCord, Method::kAppendString, Method::kAppendCord,
Method::kAppendCord, Method::kMoveAppendCord, Method::kAppendExternalMemory,
Method::kPrependString, Method::kPrependCord, Method::kAppendString,
Method::kMovePrependCord, Method::kAppendExternalMemory, Method::kAssignCord,
Method::kFlatten, Method::kGetAppendRegion, Method::kAssignString,
Method::kRemovePrefix, Method::kRemoveSuffic, 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}; Method::kSubCord};
} }

Loading…
Cancel
Save