cordz_info.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright 2019 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef ABSL_STRINGS_CORDZ_INFO_H_
  15. #define ABSL_STRINGS_CORDZ_INFO_H_
  16. #include <atomic>
  17. #include <cstdint>
  18. #include <functional>
  19. #include "absl/base/config.h"
  20. #include "absl/base/internal/raw_logging.h"
  21. #include "absl/base/internal/spinlock.h"
  22. #include "absl/base/thread_annotations.h"
  23. #include "absl/strings/internal/cord_internal.h"
  24. #include "absl/strings/internal/cordz_functions.h"
  25. #include "absl/strings/internal/cordz_handle.h"
  26. #include "absl/strings/internal/cordz_statistics.h"
  27. #include "absl/strings/internal/cordz_update_tracker.h"
  28. #include "absl/synchronization/mutex.h"
  29. #include "absl/types/span.h"
  30. namespace absl {
  31. ABSL_NAMESPACE_BEGIN
  32. namespace cord_internal {
  33. // CordzInfo tracks a profiled Cord. Each of these objects can be in two places.
  34. // If a Cord is alive, the CordzInfo will be in the global_cordz_infos map, and
  35. // can also be retrieved via the linked list starting with
  36. // global_cordz_infos_head and continued via the cordz_info_next() method. When
  37. // a Cord has reached the end of its lifespan, the CordzInfo object will be
  38. // migrated out of the global_cordz_infos list and the global_cordz_infos_map,
  39. // and will either be deleted or appended to the global_delete_queue. If it is
  40. // placed on the global_delete_queue, the CordzInfo object will be cleaned in
  41. // the destructor of a CordzSampleToken object.
  42. class ABSL_LOCKABLE CordzInfo : public CordzHandle {
  43. public:
  44. using MethodIdentifier = CordzUpdateTracker::MethodIdentifier;
  45. // TrackCord creates a CordzInfo instance which tracks important metrics of
  46. // a sampled cord, and stores the created CordzInfo instance into `cord'. All
  47. // CordzInfo instances are placed in a global list which is used to discover
  48. // and snapshot all actively tracked cords. Callers are responsible for
  49. // calling UntrackCord() before the tracked Cord instance is deleted, or to
  50. // stop tracking the sampled Cord. Callers are also responsible for guarding
  51. // changes to the 'tree' value of a Cord (InlineData.tree) through the Lock()
  52. // and Unlock() calls. Any change resulting in a new tree value for the cord
  53. // requires a call to SetCordRep() before the old tree has been unreffed
  54. // and/or deleted. `method` identifies the Cord public API method initiating
  55. // the cord to be sampled.
  56. // Requires `cord` to hold a tree, and `cord.cordz_info()` to be null.
  57. static void TrackCord(InlineData& cord, MethodIdentifier method);
  58. // Identical to TrackCord(), except that this function fills the
  59. // `parent_stack` and `parent_method` properties of the returned CordzInfo
  60. // instance from the provided `src` instance if `src` is sampled.
  61. // This function should be used for sampling 'copy constructed' and 'copy
  62. // assigned' cords. This function allows 'cord` to be already sampled, in
  63. // which case the CordzInfo will be newly created from `src`.
  64. static void TrackCord(InlineData& cord, const InlineData& src,
  65. MethodIdentifier method);
  66. // Maybe sample the cord identified by 'cord' for method 'method'.
  67. // Uses `cordz_should_profile` to randomly pick cords to be sampled, and if
  68. // so, invokes `TrackCord` to start sampling `cord`.
  69. static void MaybeTrackCord(InlineData& cord, MethodIdentifier method);
  70. // Maybe sample the cord identified by 'cord' for method 'method'.
  71. // `src` identifies a 'parent' cord which is assigned to `cord`, typically the
  72. // input cord for a copy constructor, or an assign method such as `operator=`
  73. // `cord` will be sampled if (and only if) `src` is sampled.
  74. // If `cord` is currently being sampled and `src` is not being sampled, then
  75. // this function will stop sampling the cord and reset the cord's cordz_info.
  76. //
  77. // Previously this function defined that `cord` will be sampled if either
  78. // `src` is sampled, or if `cord` is randomly picked for sampling. However,
  79. // this can cause issues, as there may be paths where some cord is assigned an
  80. // indirect copy of it's own value. As such a 'string of copies' would then
  81. // remain sampled (`src.is_profiled`), then assigning such a cord back to
  82. // 'itself' creates a cycle where the cord will converge to 'always sampled`.
  83. //
  84. // For example:
  85. //
  86. // Cord x;
  87. // for (...) {
  88. // // Copy ctor --> y.is_profiled := x.is_profiled | random(...)
  89. // Cord y = x;
  90. // ...
  91. // // Assign x = y --> x.is_profiled = y.is_profiled | random(...)
  92. // // ==> x.is_profiled |= random(...)
  93. // // ==> x converges to 'always profiled'
  94. // x = y;
  95. // }
  96. static void MaybeTrackCord(InlineData& cord, const InlineData& src,
  97. MethodIdentifier method);
  98. // Stops tracking changes for a sampled cord, and deletes the provided info.
  99. // This function must be called before the sampled cord instance is deleted,
  100. // and before the root cordrep of the sampled cord is unreffed.
  101. // This function may extend the lifetime of the cordrep in cases where the
  102. // CordInfo instance is being held by a concurrent collection thread.
  103. void Untrack();
  104. // Invokes UntrackCord() on `info` if `info` is not null.
  105. static void MaybeUntrackCord(CordzInfo* info);
  106. CordzInfo() = delete;
  107. CordzInfo(const CordzInfo&) = delete;
  108. CordzInfo& operator=(const CordzInfo&) = delete;
  109. // Retrieves the oldest existing CordzInfo.
  110. static CordzInfo* Head(const CordzSnapshot& snapshot)
  111. ABSL_NO_THREAD_SAFETY_ANALYSIS;
  112. // Retrieves the next oldest existing CordzInfo older than 'this' instance.
  113. CordzInfo* Next(const CordzSnapshot& snapshot) const
  114. ABSL_NO_THREAD_SAFETY_ANALYSIS;
  115. // Locks this instance for the update identified by `method`.
  116. // Increases the count for `method` in `update_tracker`.
  117. void Lock(MethodIdentifier method) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_);
  118. // Unlocks this instance. If the contained `rep` has been set to null
  119. // indicating the Cord has been cleared or is otherwise no longer sampled,
  120. // then this method will delete this CordzInfo instance.
  121. void Unlock() ABSL_UNLOCK_FUNCTION(mutex_);
  122. // Asserts that this CordzInfo instance is locked.
  123. void AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_);
  124. // Updates the `rep` property of this instance. This methods is invoked by
  125. // Cord logic each time the root node of a sampled Cord changes, and before
  126. // the old root reference count is deleted. This guarantees that collection
  127. // code can always safely take a reference on the tracked cord.
  128. // Requires a lock to be held through the `Lock()` method.
  129. // TODO(b/117940323): annotate with ABSL_EXCLUSIVE_LOCKS_REQUIRED once all
  130. // Cord code is in a state where this can be proven true by the compiler.
  131. void SetCordRep(CordRep* rep);
  132. // Returns the current `rep` property of this instance with a reference
  133. // added, or null if this instance represents a cord that has since been
  134. // deleted or untracked.
  135. CordRep* RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_);
  136. // Returns the current value of `rep_` for testing purposes only.
  137. CordRep* GetCordRepForTesting() const ABSL_NO_THREAD_SAFETY_ANALYSIS {
  138. return rep_;
  139. }
  140. // Sets the current value of `rep_` for testing purposes only.
  141. void SetCordRepForTesting(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS {
  142. rep_ = rep;
  143. }
  144. // Returns the stack trace for where the cord was first sampled. Cords are
  145. // potentially sampled when they promote from an inlined cord to a tree or
  146. // ring representation, which is not necessarily the location where the cord
  147. // was first created. Some cords are created as inlined cords, and only as
  148. // data is added do they become a non-inlined cord. However, typically the
  149. // location represents reasonably well where the cord is 'created'.
  150. absl::Span<void* const> GetStack() const;
  151. // Returns the stack trace for a sampled cord's 'parent stack trace'. This
  152. // value may be set if the cord is sampled (promoted) after being created
  153. // from, or being assigned the value of an existing (sampled) cord.
  154. absl::Span<void* const> GetParentStack() const;
  155. // Retrieves the CordzStatistics associated with this Cord. The statistics
  156. // are only updated when a Cord goes through a mutation, such as an Append
  157. // or RemovePrefix.
  158. CordzStatistics GetCordzStatistics() const;
  159. private:
  160. using SpinLock = absl::base_internal::SpinLock;
  161. using SpinLockHolder = ::absl::base_internal::SpinLockHolder;
  162. // Global cordz info list. CordzInfo stores a pointer to the global list
  163. // instance to harden against ODR violations.
  164. struct List {
  165. constexpr explicit List(absl::ConstInitType)
  166. : mutex(absl::kConstInit,
  167. absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {}
  168. SpinLock mutex;
  169. std::atomic<CordzInfo*> head ABSL_GUARDED_BY(mutex){nullptr};
  170. };
  171. static constexpr int kMaxStackDepth = 64;
  172. explicit CordzInfo(CordRep* rep, const CordzInfo* src,
  173. MethodIdentifier method);
  174. ~CordzInfo() override;
  175. // Sets `rep_` without holding a lock.
  176. void UnsafeSetCordRep(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS;
  177. void Track();
  178. // Returns the parent method from `src`, which is either `parent_method_` or
  179. // `method_` depending on `parent_method_` being kUnknown.
  180. // Returns kUnknown if `src` is null.
  181. static MethodIdentifier GetParentMethod(const CordzInfo* src);
  182. // Fills the provided stack from `src`, copying either `parent_stack_` or
  183. // `stack_` depending on `parent_stack_` being empty, returning the size of
  184. // the parent stack.
  185. // Returns 0 if `src` is null.
  186. static int FillParentStack(const CordzInfo* src, void** stack);
  187. void ODRCheck() const {
  188. #ifndef NDEBUG
  189. ABSL_RAW_CHECK(list_ == &global_list_, "ODR violation in Cord");
  190. #endif
  191. }
  192. // Non-inlined implementation of `MaybeTrackCord`, which is executed if
  193. // either `src` is sampled or `cord` is sampled, and either untracks or
  194. // tracks `cord` as documented per `MaybeTrackCord`.
  195. static void MaybeTrackCordImpl(InlineData& cord, const InlineData& src,
  196. MethodIdentifier method);
  197. ABSL_CONST_INIT static List global_list_;
  198. List* const list_ = &global_list_;
  199. // ci_prev_ and ci_next_ require the global list mutex to be held.
  200. // Unfortunately we can't use thread annotations such that the thread safety
  201. // analysis understands that list_ and global_list_ are one and the same.
  202. std::atomic<CordzInfo*> ci_prev_{nullptr};
  203. std::atomic<CordzInfo*> ci_next_{nullptr};
  204. mutable absl::Mutex mutex_;
  205. CordRep* rep_ ABSL_GUARDED_BY(mutex_);
  206. void* stack_[kMaxStackDepth];
  207. void* parent_stack_[kMaxStackDepth];
  208. const int stack_depth_;
  209. const int parent_stack_depth_;
  210. const MethodIdentifier method_;
  211. const MethodIdentifier parent_method_;
  212. CordzUpdateTracker update_tracker_;
  213. const absl::Time create_time_;
  214. };
  215. inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord(
  216. InlineData& cord, MethodIdentifier method) {
  217. if (ABSL_PREDICT_FALSE(cordz_should_profile())) {
  218. TrackCord(cord, method);
  219. }
  220. }
  221. inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord(
  222. InlineData& cord, const InlineData& src, MethodIdentifier method) {
  223. if (ABSL_PREDICT_FALSE(InlineData::is_either_profiled(cord, src))) {
  224. MaybeTrackCordImpl(cord, src, method);
  225. }
  226. }
  227. inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeUntrackCord(
  228. CordzInfo* info) {
  229. if (ABSL_PREDICT_FALSE(info)) {
  230. info->Untrack();
  231. }
  232. }
  233. inline void CordzInfo::AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_) {
  234. #ifndef NDEBUG
  235. mutex_.AssertHeld();
  236. #endif
  237. }
  238. inline void CordzInfo::SetCordRep(CordRep* rep) {
  239. AssertHeld();
  240. rep_ = rep;
  241. }
  242. inline void CordzInfo::UnsafeSetCordRep(CordRep* rep) { rep_ = rep; }
  243. inline CordRep* CordzInfo::RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_) {
  244. MutexLock lock(&mutex_);
  245. return rep_ ? CordRep::Ref(rep_) : nullptr;
  246. }
  247. } // namespace cord_internal
  248. ABSL_NAMESPACE_END
  249. } // namespace absl
  250. #endif // ABSL_STRINGS_CORDZ_INFO_H_