Skip to content

Commit b2dc72c

Browse files
Abseil Teamderekmauro
authored andcommitted
Export of internal Abseil changes
-- ca3a0009e675b699b5d6dd41f00ebac0e7d1935c by Derek Mauro <[email protected]>: Internal change PiperOrigin-RevId: 396475923 -- 04d9fff79085bb18612af3da49007907394ae0b6 by Abseil Team <[email protected]>: Move HastablezSampler from container/internal to profiling/internal. PiperOrigin-RevId: 396362093 GitOrigin-RevId: ca3a0009e675b699b5d6dd41f00ebac0e7d1935c Change-Id: I42d6d2944786afa24259fde002fed5e611f4e1f9
1 parent 669184b commit b2dc72c

File tree

11 files changed

+482
-176
lines changed

11 files changed

+482
-176
lines changed

CMake/AbseilDll.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ set(ABSL_INTERNAL_DLL_FILES
133133
"numeric/int128.h"
134134
"numeric/internal/bits.h"
135135
"numeric/internal/representation.h"
136+
"profiling/internal/sample_recorder.h"
136137
"random/bernoulli_distribution.h"
137138
"random/beta_distribution.h"
138139
"random/bit_gen_ref.h"
@@ -457,6 +458,7 @@ set(ABSL_INTERNAL_DLL_TARGETS
457458
"raw_hash_set"
458459
"layout"
459460
"tracked"
461+
"sample_recorder"
460462
)
461463

462464
function(absl_internal_dll_contains)

absl/container/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ cc_library(
513513
"//absl/base:exponential_biased",
514514
"//absl/debugging:stacktrace",
515515
"//absl/memory",
516+
"//absl/profiling:sample_recorder",
516517
"//absl/synchronization",
517518
"//absl/utility",
518519
],
@@ -526,6 +527,7 @@ cc_test(
526527
":hashtablez_sampler",
527528
":have_sse",
528529
"//absl/base:core_headers",
530+
"//absl/profiling:sample_recorder",
529531
"//absl/synchronization",
530532
"//absl/synchronization:thread_pool",
531533
"//absl/time",

absl/container/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ absl_cc_library(
548548
absl::base
549549
absl::exponential_biased
550550
absl::have_sse
551+
absl::sample_recorder
551552
absl::synchronization
552553
)
553554

absl/container/internal/hashtablez_sampler.cc

Lines changed: 6 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "absl/container/internal/have_sse.h"
2626
#include "absl/debugging/stacktrace.h"
2727
#include "absl/memory/memory.h"
28+
#include "absl/profiling/internal/sample_recorder.h"
2829
#include "absl/synchronization/mutex.h"
2930

3031
namespace absl {
@@ -37,7 +38,6 @@ ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{
3738
false
3839
};
3940
ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10};
40-
ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_max_samples{1 << 20};
4141

4242
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
4343
ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased
@@ -50,16 +50,11 @@ ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased
5050
ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0;
5151
#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
5252

53-
HashtablezSampler& HashtablezSampler::Global() {
53+
HashtablezSampler& GlobalHashtablezSampler() {
5454
static auto* sampler = new HashtablezSampler();
5555
return *sampler;
5656
}
5757

58-
HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback(
59-
DisposeCallback f) {
60-
return dispose_.exchange(f, std::memory_order_relaxed);
61-
}
62-
6358
HashtablezInfo::HashtablezInfo() { PrepareForSampling(); }
6459
HashtablezInfo::~HashtablezInfo() = default;
6560

@@ -80,93 +75,6 @@ void HashtablezInfo::PrepareForSampling() {
8075
// instead.
8176
depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth,
8277
/* skip_count= */ 0);
83-
dead = nullptr;
84-
}
85-
86-
HashtablezSampler::HashtablezSampler()
87-
: dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) {
88-
absl::MutexLock l(&graveyard_.init_mu);
89-
graveyard_.dead = &graveyard_;
90-
}
91-
92-
HashtablezSampler::~HashtablezSampler() {
93-
HashtablezInfo* s = all_.load(std::memory_order_acquire);
94-
while (s != nullptr) {
95-
HashtablezInfo* next = s->next;
96-
delete s;
97-
s = next;
98-
}
99-
}
100-
101-
void HashtablezSampler::PushNew(HashtablezInfo* sample) {
102-
sample->next = all_.load(std::memory_order_relaxed);
103-
while (!all_.compare_exchange_weak(sample->next, sample,
104-
std::memory_order_release,
105-
std::memory_order_relaxed)) {
106-
}
107-
}
108-
109-
void HashtablezSampler::PushDead(HashtablezInfo* sample) {
110-
if (auto* dispose = dispose_.load(std::memory_order_relaxed)) {
111-
dispose(*sample);
112-
}
113-
114-
absl::MutexLock graveyard_lock(&graveyard_.init_mu);
115-
absl::MutexLock sample_lock(&sample->init_mu);
116-
sample->dead = graveyard_.dead;
117-
graveyard_.dead = sample;
118-
}
119-
120-
HashtablezInfo* HashtablezSampler::PopDead() {
121-
absl::MutexLock graveyard_lock(&graveyard_.init_mu);
122-
123-
// The list is circular, so eventually it collapses down to
124-
// graveyard_.dead == &graveyard_
125-
// when it is empty.
126-
HashtablezInfo* sample = graveyard_.dead;
127-
if (sample == &graveyard_) return nullptr;
128-
129-
absl::MutexLock sample_lock(&sample->init_mu);
130-
graveyard_.dead = sample->dead;
131-
sample->PrepareForSampling();
132-
return sample;
133-
}
134-
135-
HashtablezInfo* HashtablezSampler::Register() {
136-
int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed);
137-
if (size > g_hashtablez_max_samples.load(std::memory_order_relaxed)) {
138-
size_estimate_.fetch_sub(1, std::memory_order_relaxed);
139-
dropped_samples_.fetch_add(1, std::memory_order_relaxed);
140-
return nullptr;
141-
}
142-
143-
HashtablezInfo* sample = PopDead();
144-
if (sample == nullptr) {
145-
// Resurrection failed. Hire a new warlock.
146-
sample = new HashtablezInfo();
147-
PushNew(sample);
148-
}
149-
150-
return sample;
151-
}
152-
153-
void HashtablezSampler::Unregister(HashtablezInfo* sample) {
154-
PushDead(sample);
155-
size_estimate_.fetch_sub(1, std::memory_order_relaxed);
156-
}
157-
158-
int64_t HashtablezSampler::Iterate(
159-
const std::function<void(const HashtablezInfo& stack)>& f) {
160-
HashtablezInfo* s = all_.load(std::memory_order_acquire);
161-
while (s != nullptr) {
162-
absl::MutexLock l(&s->init_mu);
163-
if (s->dead == nullptr) {
164-
f(*s);
165-
}
166-
s = s->next;
167-
}
168-
169-
return dropped_samples_.load(std::memory_order_relaxed);
17078
}
17179

17280
static bool ShouldForceSampling() {
@@ -192,7 +100,7 @@ static bool ShouldForceSampling() {
192100
HashtablezInfo* SampleSlow(int64_t* next_sample) {
193101
if (ABSL_PREDICT_FALSE(ShouldForceSampling())) {
194102
*next_sample = 1;
195-
return HashtablezSampler::Global().Register();
103+
return GlobalHashtablezSampler().Register();
196104
}
197105

198106
#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@@ -217,12 +125,12 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) {
217125
return SampleSlow(next_sample);
218126
}
219127

220-
return HashtablezSampler::Global().Register();
128+
return GlobalHashtablezSampler().Register();
221129
#endif
222130
}
223131

224132
void UnsampleSlow(HashtablezInfo* info) {
225-
HashtablezSampler::Global().Unregister(info);
133+
GlobalHashtablezSampler().Unregister(info);
226134
}
227135

228136
void RecordInsertSlow(HashtablezInfo* info, size_t hash,
@@ -262,7 +170,7 @@ void SetHashtablezSampleParameter(int32_t rate) {
262170

263171
void SetHashtablezMaxSamples(int32_t max) {
264172
if (max > 0) {
265-
g_hashtablez_max_samples.store(max, std::memory_order_release);
173+
GlobalHashtablezSampler().SetMaxSamples(max);
266174
} else {
267175
ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld",
268176
static_cast<long long>(max)); // NOLINT(runtime/int)

absl/container/internal/hashtablez_sampler.h

Lines changed: 6 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "absl/base/internal/per_thread_tls.h"
4848
#include "absl/base/optimization.h"
4949
#include "absl/container/internal/have_sse.h"
50+
#include "absl/profiling/internal/sample_recorder.h"
5051
#include "absl/synchronization/mutex.h"
5152
#include "absl/utility/utility.h"
5253

@@ -57,7 +58,7 @@ namespace container_internal {
5758
// Stores information about a sampled hashtable. All mutations to this *must*
5859
// be made through `Record*` functions below. All reads from this *must* only
5960
// occur in the callback to `HashtablezSampler::Iterate`.
60-
struct HashtablezInfo {
61+
struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> {
6162
// Constructs the object but does not fill in any fields.
6263
HashtablezInfo();
6364
~HashtablezInfo();
@@ -80,14 +81,6 @@ struct HashtablezInfo {
8081
std::atomic<size_t> hashes_bitwise_and;
8182
std::atomic<size_t> hashes_bitwise_xor;
8283

83-
// `HashtablezSampler` maintains intrusive linked lists for all samples. See
84-
// comments on `HashtablezSampler::all_` for details on these. `init_mu`
85-
// guards the ability to restore the sample to a pristine state. This
86-
// prevents races with sampling and resurrecting an object.
87-
absl::Mutex init_mu;
88-
HashtablezInfo* next;
89-
HashtablezInfo* dead ABSL_GUARDED_BY(init_mu);
90-
9184
// All of the fields below are set by `PrepareForSampling`, they must not be
9285
// mutated in `Record*` functions. They are logically `const` in that sense.
9386
// These are guarded by init_mu, but that is not externalized to clients, who
@@ -231,73 +224,11 @@ inline HashtablezInfoHandle Sample() {
231224
#endif // !ABSL_PER_THREAD_TLS
232225
}
233226

234-
// Holds samples and their associated stack traces with a soft limit of
235-
// `SetHashtablezMaxSamples()`.
236-
//
237-
// Thread safe.
238-
class HashtablezSampler {
239-
public:
240-
// Returns a global Sampler.
241-
static HashtablezSampler& Global();
242-
243-
HashtablezSampler();
244-
~HashtablezSampler();
245-
246-
// Registers for sampling. Returns an opaque registration info.
247-
HashtablezInfo* Register();
227+
using HashtablezSampler =
228+
::absl::profiling_internal::SampleRecorder<HashtablezInfo>;
248229

249-
// Unregisters the sample.
250-
void Unregister(HashtablezInfo* sample);
251-
252-
// The dispose callback will be called on all samples the moment they are
253-
// being unregistered. Only affects samples that are unregistered after the
254-
// callback has been set.
255-
// Returns the previous callback.
256-
using DisposeCallback = void (*)(const HashtablezInfo&);
257-
DisposeCallback SetDisposeCallback(DisposeCallback f);
258-
259-
// Iterates over all the registered `StackInfo`s. Returning the number of
260-
// samples that have been dropped.
261-
int64_t Iterate(const std::function<void(const HashtablezInfo& stack)>& f);
262-
263-
private:
264-
void PushNew(HashtablezInfo* sample);
265-
void PushDead(HashtablezInfo* sample);
266-
HashtablezInfo* PopDead();
267-
268-
std::atomic<size_t> dropped_samples_;
269-
std::atomic<size_t> size_estimate_;
270-
271-
// Intrusive lock free linked lists for tracking samples.
272-
//
273-
// `all_` records all samples (they are never removed from this list) and is
274-
// terminated with a `nullptr`.
275-
//
276-
// `graveyard_.dead` is a circular linked list. When it is empty,
277-
// `graveyard_.dead == &graveyard`. The list is circular so that
278-
// every item on it (even the last) has a non-null dead pointer. This allows
279-
// `Iterate` to determine if a given sample is live or dead using only
280-
// information on the sample itself.
281-
//
282-
// For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead
283-
// looks like this (G is the Graveyard):
284-
//
285-
// +---+ +---+ +---+ +---+ +---+
286-
// all -->| A |--->| B |--->| C |--->| D |--->| E |
287-
// | | | | | | | | | |
288-
// +---+ | | +->| |-+ | | +->| |-+ | |
289-
// | G | +---+ | +---+ | +---+ | +---+ | +---+
290-
// | | | | | |
291-
// | | --------+ +--------+ |
292-
// +---+ |
293-
// ^ |
294-
// +--------------------------------------+
295-
//
296-
std::atomic<HashtablezInfo*> all_;
297-
HashtablezInfo graveyard_;
298-
299-
std::atomic<DisposeCallback> dispose_;
300-
};
230+
// Returns a global Sampler.
231+
HashtablezSampler& GlobalHashtablezSampler();
301232

302233
// Enables or disables sampling for Swiss tables.
303234
void SetHashtablezEnabled(bool enabled);

absl/container/internal/hashtablez_sampler_test.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "gtest/gtest.h"
2323
#include "absl/base/attributes.h"
2424
#include "absl/container/internal/have_sse.h"
25+
#include "absl/profiling/internal/sample_recorder.h"
2526
#include "absl/synchronization/blocking_counter.h"
2627
#include "absl/synchronization/internal/thread_pool.h"
2728
#include "absl/synchronization/mutex.h"
@@ -232,7 +233,7 @@ TEST(HashtablezSamplerTest, Sample) {
232233
}
233234

234235
TEST(HashtablezSamplerTest, Handle) {
235-
auto& sampler = HashtablezSampler::Global();
236+
auto& sampler = GlobalHashtablezSampler();
236237
HashtablezInfoHandle h(sampler.Register());
237238
auto* info = HashtablezInfoHandlePeer::GetInfo(&h);
238239
info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed);

absl/container/internal/raw_hash_set_test.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,7 +2038,7 @@ TEST(RawHashSamplerTest, Sample) {
20382038
SetHashtablezEnabled(true);
20392039
SetHashtablezSampleParameter(100);
20402040

2041-
auto& sampler = HashtablezSampler::Global();
2041+
auto& sampler = GlobalHashtablezSampler();
20422042
size_t start_size = 0;
20432043
std::unordered_set<const HashtablezInfo*> preexisting_info;
20442044
start_size += sampler.Iterate([&](const HashtablezInfo& info) {
@@ -2076,7 +2076,7 @@ TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) {
20762076
SetHashtablezEnabled(true);
20772077
SetHashtablezSampleParameter(100);
20782078

2079-
auto& sampler = HashtablezSampler::Global();
2079+
auto& sampler = GlobalHashtablezSampler();
20802080
size_t start_size = 0;
20812081
start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
20822082

absl/profiling/BUILD.bazel

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,40 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
load(
16+
"//absl:copts/configure_copts.bzl",
17+
"ABSL_DEFAULT_COPTS",
18+
"ABSL_DEFAULT_LINKOPTS",
19+
)
20+
1521
package(default_visibility = ["//visibility:private"])
1622

1723
licenses(["notice"])
24+
25+
cc_library(
26+
name = "sample_recorder",
27+
hdrs = ["internal/sample_recorder.h"],
28+
copts = ABSL_DEFAULT_COPTS,
29+
linkopts = ABSL_DEFAULT_LINKOPTS,
30+
visibility = ["//absl:__subpackages__"],
31+
deps = [
32+
"//absl/base:config",
33+
"//absl/base:core_headers",
34+
"//absl/synchronization",
35+
"//absl/time",
36+
],
37+
)
38+
39+
cc_test(
40+
name = "sample_recorder_test",
41+
srcs = ["internal/sample_recorder_test.cc"],
42+
linkopts = ABSL_DEFAULT_LINKOPTS,
43+
deps = [
44+
":sample_recorder",
45+
"//absl/base:core_headers",
46+
"//absl/synchronization",
47+
"//absl/synchronization:thread_pool",
48+
"//absl/time",
49+
"@com_google_googletest//:gtest_main",
50+
],
51+
)

0 commit comments

Comments
 (0)