// Copyright 2025 The Abseil Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may // obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "absl/profiling/hashtable.h" #include #include #include #include #include #include #include "absl/base/config.h" #include "absl/container/internal/hashtablez_sampler.h" #include "absl/profiling/internal/profile_builder.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "absl/types/span.h" namespace absl { ABSL_NAMESPACE_BEGIN StatusOr MarshalHashtableProfile() { return debugging_internal::MarshalHashtableProfile( container_internal::GlobalHashtablezSampler(), Now()); } namespace debugging_internal { static void DroppedHashtableSample() {} StatusOr MarshalHashtableProfile( container_internal::HashtablezSampler& sampler, Time now) { static constexpr absl::string_view kDropFrames = "(::)?absl::container_internal::.*|" "(::)?absl::(flat|node)_hash_(map|set).*"; ProfileBuilder builder; StringId drop_frames_id = builder.InternString(kDropFrames); builder.set_drop_frames_id(drop_frames_id); builder.AddSampleType(builder.InternString("capacity"), builder.InternString("count")); builder.set_default_sample_type_id(builder.InternString("capacity")); const auto capacity_id = builder.InternString("capacity"); const auto size_id = builder.InternString("size"); const auto num_erases_id = builder.InternString("num_erases"); const auto num_rehashes_id = builder.InternString("num_rehashes"); const auto max_probe_length_id = builder.InternString("max_probe_length"); const auto total_probe_length_id = builder.InternString("total_probe_length"); const auto stuck_bits_id = builder.InternString("stuck_bits"); const auto inline_element_size_id = builder.InternString("inline_element_size"); const auto key_size_id = builder.InternString("key_size"); const auto value_size_id = builder.InternString("value_size"); const auto soo_capacity_id = builder.InternString("soo_capacity"); const auto table_age_id = builder.InternString("table_age"); const auto max_reserve_id = builder.InternString("max_reserve"); size_t dropped = sampler.Iterate([&](const container_internal::HashtablezInfo& info) { const size_t capacity = info.capacity.load(std::memory_order_relaxed); std::vector> labels; auto add_label = [&](StringId tag, uint64_t value) { if (value == 0) { return; } labels.emplace_back(tag, static_cast(value)); }; add_label(capacity_id, capacity); add_label(size_id, info.size.load(std::memory_order_relaxed)); add_label(num_erases_id, info.num_erases.load(std::memory_order_relaxed)); add_label(num_rehashes_id, info.num_rehashes.load(std::memory_order_relaxed)); add_label(max_probe_length_id, info.max_probe_length.load(std::memory_order_relaxed)); add_label(total_probe_length_id, info.total_probe_length.load(std::memory_order_relaxed)); add_label(stuck_bits_id, (info.hashes_bitwise_and.load(std::memory_order_relaxed) | ~info.hashes_bitwise_or.load(std::memory_order_relaxed))); add_label(inline_element_size_id, info.inline_element_size); add_label(key_size_id, info.key_size); add_label(value_size_id, info.value_size); add_label(soo_capacity_id, info.soo_capacity); add_label( table_age_id, static_cast(ToInt64Microseconds(now - info.create_time))); add_label(max_reserve_id, info.max_reserve.load(std::memory_order_relaxed)); builder.AddSample(static_cast(capacity) * info.weight, MakeSpan(info.stack, info.depth), labels); }); if (dropped > 0) { // If we dropped samples, we don't have information for them, including // their sizes. The non-zero weight allows it to be noticed in the profile // and examined more closely. // // We compensate for the fixup done by AddSample by adjusting the address // here. const void* kFakeStack[] = { absl::bit_cast( reinterpret_cast(&DroppedHashtableSample)+1)}; builder.AddSample(static_cast(dropped), kFakeStack, {}); } builder.AddCurrentMappings(); return std::move(builder).Emit(); } } // namespace debugging_internal ABSL_NAMESPACE_END } // namespace absl