From eec6a77250aaf43c6907ede49dcf2296403a1a5d Mon Sep 17 00:00:00 2001 From: mrtarunarora Date: Mon, 21 Sep 2026 00:38:57 -0700 Subject: [PATCH] Introduce PrometheusLabelView in label_util to support zero-allocation hot-path Prometheus label formatting and lookup, previous FormatPrometheusLabels was using heap allocation even in cold path PiperOrigin-RevId: 985077989 --- tpu_sync/telemetry/BUILD | 1 + tpu_sync/telemetry/label_util.cc | 122 ++++++------ tpu_sync/telemetry/label_util.h | 43 +++- tpu_sync/telemetry/label_util_test.cc | 271 ++++++++++++++++++++++---- 4 files changed, 345 insertions(+), 92 deletions(-) diff --git a/tpu_sync/telemetry/BUILD b/tpu_sync/telemetry/BUILD index f6d9dc9f..3420981f 100644 --- a/tpu_sync/telemetry/BUILD +++ b/tpu_sync/telemetry/BUILD @@ -148,6 +148,7 @@ cc_library( deps = [ ":metrics_backend", "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:inlined_vector", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", diff --git a/tpu_sync/telemetry/label_util.cc b/tpu_sync/telemetry/label_util.cc index e4a53fce..0916fcbe 100644 --- a/tpu_sync/telemetry/label_util.cc +++ b/tpu_sync/telemetry/label_util.cc @@ -22,6 +22,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/base/attributes.h" #include "absl/container/inlined_vector.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" @@ -30,8 +31,6 @@ namespace tpu_raiden::telemetry { namespace { -constexpr size_t kDefaultPrometheusStackBufferSize = 256; - // Lightweight buffer writer that bounds-checks appends into a char span. class BufferWriter { public: @@ -92,12 +91,40 @@ class BufferWriter { size_t offset_ = 0; }; -// Common stack-allocated sorting helper for multi-label sets. -void SortLabels( - LabelSpan labels, - absl::InlinedVector& out) { - out.assign(labels.begin(), labels.end()); - absl::c_sort(out); +// Returns labels ordered by key. When `labels` is not already sorted, the +// sorted copy is stored in `scratch` and the returned span aliases it; +// otherwise the returned span aliases `labels`. Both arguments must outlive +// the returned span. +LabelSpan EnsureSortedLabels( + LabelSpan labels ABSL_ATTRIBUTE_LIFETIME_BOUND, + absl::InlinedVector& scratch + ABSL_ATTRIBUTE_LIFETIME_BOUND) { + if (absl::c_is_sorted(labels)) return labels; + scratch.assign(labels.begin(), labels.end()); + absl::c_sort(scratch); + return scratch; +} + +// Formats pre-sorted labels into Prometheus canonical format directly into +// output_buffer without checking sort order. +std::optional FormatSortedPrometheusLabelsToBuffer( + LabelSpan sorted_labels, absl::Span output_buffer) { + if (sorted_labels.empty()) { + return absl::string_view(output_buffer.data(), 0); + } + + BufferWriter writer(output_buffer); + if (!writer.Append('{')) return std::nullopt; + for (size_t i = 0; i < sorted_labels.size(); ++i) { + if (i > 0 && !writer.Append(',')) return std::nullopt; + const MetricLabel& label = sorted_labels[i]; + if (!writer.Append(label.key) || !writer.Append("=\"") || + !writer.AppendPrometheusEscaped(label.value) || !writer.Append('"')) { + return std::nullopt; + } + } + if (!writer.Append('}')) return std::nullopt; + return writer.view(); } // Returns the byte length of Prometheus label value after escaping ('\', '"', @@ -123,30 +150,6 @@ size_t ComputePrometheusLabelsSize(LabelSpan labels) { return total; } -// Generic 2-stage stack-to-heap allocation fallback orchestrator. -template -std::string FormatWithStackBufferFallback(LabelSpan labels, SizeFn size_fn, - BufferFormatFn format_fn) { - if (labels.empty()) return ""; - - char stack_buffer[StackBufferSize]; - if (std::optional formatted = - format_fn(labels, absl::MakeSpan(stack_buffer)); - formatted.has_value()) { - return std::string(*formatted); - } - - // Exact-size dynamic fallback for label sets exceeding stack buffer. - std::string result(size_fn(labels), '\0'); - std::optional formatted = - format_fn(labels, absl::MakeSpan(result)); - if (!formatted.has_value()) { - return ""; - } - result.resize(formatted->size()); - return result; -} - } // namespace std::optional FormatShmLabelsToBuffer( @@ -157,11 +160,8 @@ std::optional FormatShmLabelsToBuffer( BufferWriter writer(output_buffer); - absl::InlinedVector sorted; - if (!absl::c_is_sorted(labels)) { - SortLabels(labels, sorted); - labels = sorted; - } + absl::InlinedVector scratch; + labels = EnsureSortedLabels(labels, scratch); for (size_t i = 0; i < labels.size(); ++i) { if (i > 0 && !writer.Append(';')) return std::nullopt; @@ -233,30 +233,42 @@ std::optional FormatPrometheusLabelsToBuffer( return absl::string_view(output_buffer.data(), 0); } - BufferWriter writer(output_buffer); + absl::InlinedVector scratch; + labels = EnsureSortedLabels(labels, scratch); + return FormatSortedPrometheusLabelsToBuffer(labels, output_buffer); +} - absl::InlinedVector sorted; - if (!absl::c_is_sorted(labels)) { - SortLabels(labels, sorted); - labels = sorted; - } +PrometheusLabelView::PrometheusLabelView(LabelSpan labels) { + if (labels.empty()) return; - if (!writer.Append('{')) return std::nullopt; - for (size_t i = 0; i < labels.size(); ++i) { - if (i > 0 && !writer.Append(',')) return std::nullopt; - const MetricLabel& label = labels[i]; - if (!writer.Append(label.key) || !writer.Append("=\"") || - !writer.AppendPrometheusEscaped(label.value) || !writer.Append('"')) { - return std::nullopt; + absl::InlinedVector scratch; + labels = EnsureSortedLabels(labels, scratch); + + std::optional formatted = + FormatSortedPrometheusLabelsToBuffer(labels, absl::MakeSpan(stack_buf_)); + if (formatted.has_value()) { + view_ = *formatted; + } else { + heap_fallback_.resize(ComputePrometheusLabelsSize(labels)); + std::optional heap_formatted = + FormatSortedPrometheusLabelsToBuffer(labels, + absl::MakeSpan(heap_fallback_)); + if (heap_formatted.has_value()) { + heap_fallback_.resize(heap_formatted->size()); + view_ = heap_fallback_; + } else { + heap_fallback_.clear(); } } - if (!writer.Append('}')) return std::nullopt; - return writer.view(); } -std::string FormatPrometheusLabels(LabelSpan labels) { - return FormatWithStackBufferFallback( - labels, ComputePrometheusLabelsSize, FormatPrometheusLabelsToBuffer); +std::string PrometheusLabelView::ToOwned() { + const absl::string_view current_view = view_; + view_ = ""; + if (!heap_fallback_.empty()) { + return std::exchange(heap_fallback_, std::string()); + } + return std::string(current_view); } } // namespace tpu_raiden::telemetry diff --git a/tpu_sync/telemetry/label_util.h b/tpu_sync/telemetry/label_util.h index 7e5f2ba3..436e9bb1 100644 --- a/tpu_sync/telemetry/label_util.h +++ b/tpu_sync/telemetry/label_util.h @@ -22,6 +22,7 @@ #include #include +#include "absl/base/attributes.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "tpu_sync/telemetry/metrics_backend.h" @@ -66,10 +67,44 @@ std::vector> ParseShmLabels( std::optional FormatPrometheusLabelsToBuffer( LabelSpan labels, absl::Span output_buffer); -// Owning std::string wrapper for non-critical paths, testing, and series -// identification. Performs raw serialization without runtime key syntax -// validation. -std::string FormatPrometheusLabels(LabelSpan labels); +// Default stack buffer capacity (in bytes) for formatted Prometheus labels, +// sized to accommodate standard multi-label metric descriptors without heap +// allocation. +inline constexpr std::size_t kDefaultPrometheusStackBufferSize = 256; + +// Zero-allocation RAII stack buffer view for hot-path metric lookup. +// Formats labels directly into an internal 256-byte stack buffer and provides +// an absl::string_view for heterogeneous hash map lookup. Falls back to dynamic +// heap allocation only if the formatted string exceeds 256 bytes or if sorting +// requires more than 8 unsorted labels. +// +// view() is not guaranteed to be null-terminated (the stack path writes +// exactly the formatted bytes with no trailing '\0', and +// kDefaultPrometheusStackBufferSize reserves no byte for one). Never pass +// view().data() to C-style APIs. +class PrometheusLabelView { + public: + explicit PrometheusLabelView(LabelSpan labels); + + // Non-copyable and non-movable: holds internal pointers to stack_buf_ / + // heap_fallback_. + PrometheusLabelView(const PrometheusLabelView&) = delete; + PrometheusLabelView& operator=(const PrometheusLabelView&) = delete; + PrometheusLabelView(PrometheusLabelView&&) = delete; + PrometheusLabelView& operator=(PrometheusLabelView&&) = delete; + + absl::string_view view() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return view_; } + + // Returns an owned string, moving the heap fallback if allocated or + // constructing from the stack buffer. Invalidates view() and resets internal + // state. + std::string ToOwned(); + + private: + char stack_buf_[kDefaultPrometheusStackBufferSize]; + std::string heap_fallback_; + absl::string_view view_ = {}; +}; // ============================================================================ // Zero-Allocation Label Resolution for Fixed Schemas (Fixed-Arity) diff --git a/tpu_sync/telemetry/label_util_test.cc b/tpu_sync/telemetry/label_util_test.cc index 52caeeb8..8cd96cc4 100644 --- a/tpu_sync/telemetry/label_util_test.cc +++ b/tpu_sync/telemetry/label_util_test.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -226,9 +227,7 @@ TEST(LabelUtilTest, RoundTripInvariance) { // Prometheus Canonical Formatting Tests // ============================================================================ -TEST(LabelUtilTest, FormatPrometheusLabelsEmpty) { - EXPECT_EQ(FormatPrometheusLabels({}), ""); - +TEST(LabelUtilTest, FormatPrometheusLabelsToBufferEmpty) { char output_buffer[64]; std::optional result = FormatPrometheusLabelsToBuffer({}, absl::MakeSpan(output_buffer)); @@ -243,9 +242,8 @@ TEST(LabelUtilTest, FormatPrometheusLabelsEmpty) { EXPECT_EQ(*empty_buffer_result, ""); } -TEST(LabelUtilTest, FormatPrometheusLabelsSingleFastPath) { - MetricLabel labels[] = {{"direction", "pull"}}; - EXPECT_EQ(FormatPrometheusLabels(labels), "{direction=\"pull\"}"); +TEST(LabelUtilTest, FormatPrometheusLabelsToBufferSingleFastPath) { + const MetricLabel labels[] = {{"direction", "pull"}}; // Exact-fit buffer: "{direction=\"pull\"}" is 18 bytes. char exact_buffer[18]; @@ -260,23 +258,24 @@ TEST(LabelUtilTest, FormatPrometheusLabelsSingleFastPath) { FormatPrometheusLabelsToBuffer(labels, absl::MakeSpan(tight_buffer)) .has_value()); - // Very large label set exceeding 256 bytes to exercise heap fallback. + // Large label set exceeding 256 bytes. std::string long_value(300, 'y'); - MetricLabel large_labels[] = {{"long_key", long_value}}; - std::string formatted_large = FormatPrometheusLabels(large_labels); - EXPECT_EQ(formatted_large, absl::StrCat("{long_key=\"", long_value, "\"}")); + const MetricLabel large_labels[] = {{"long_key", long_value}}; + char large_buffer[350]; + std::optional large_result = + FormatPrometheusLabelsToBuffer(large_labels, + absl::MakeSpan(large_buffer)); + ASSERT_TRUE(large_result.has_value()); + EXPECT_EQ(*large_result, absl::StrCat("{long_key=\"", long_value, "\"}")); } -TEST(LabelUtilTest, FormatPrometheusLabelsMultiSorted) { +TEST(LabelUtilTest, FormatPrometheusLabelsToBufferMultiSorted) { // Input unsorted by key. - MetricLabel labels[] = { + const MetricLabel labels[] = { {"tag", "0"}, {"direction", "pull"}, {"mode", "direct"}, }; - EXPECT_EQ(FormatPrometheusLabels(labels), - "{direction=\"pull\",mode=\"direct\",tag=\"0\"}"); - char output_buffer[64]; std::optional result = FormatPrometheusLabelsToBuffer(labels, absl::MakeSpan(output_buffer)); @@ -284,21 +283,22 @@ TEST(LabelUtilTest, FormatPrometheusLabelsMultiSorted) { EXPECT_EQ(*result, "{direction=\"pull\",mode=\"direct\",tag=\"0\"}"); // Large multi-label set (> 8 labels). - MetricLabel ten_labels[] = { + const MetricLabel ten_labels[] = { {"k09", "v9"}, {"k08", "v8"}, {"k07", "v7"}, {"k06", "v6"}, {"k05", "v5"}, {"k04", "v4"}, {"k03", "v3"}, {"k02", "v2"}, {"k01", "v1"}, {"k00", "v0"}, }; + char large_output_buffer[256]; + std::optional ten_result = FormatPrometheusLabelsToBuffer( + ten_labels, absl::MakeSpan(large_output_buffer)); + ASSERT_TRUE(ten_result.has_value()); EXPECT_EQ( - FormatPrometheusLabels(ten_labels), + *ten_result, "{k00=\"v0\",k01=\"v1\",k02=\"v2\",k03=\"v3\",k04=\"v4\",k05=\"v5\"," "k06=\"v6\",k07=\"v7\",k08=\"v8\",k09=\"v9\"}"); } -TEST(LabelUtilTest, FormatPrometheusLabelsEscapingAndUtf8Value) { - MetricLabel labels[] = {{"query", "line1\nline2\"quoted\"with\\slash"}}; - EXPECT_EQ(FormatPrometheusLabels(labels), - "{query=\"line1\\nline2\\\"quoted\\\"with\\\\slash\"}"); - +TEST(LabelUtilTest, FormatPrometheusLabelsToBufferEscapingAndUtf8Value) { + const MetricLabel labels[] = {{"query", "line1\nline2\"quoted\"with\\slash"}}; char output_buffer[128]; std::optional result = FormatPrometheusLabelsToBuffer(labels, absl::MakeSpan(output_buffer)); @@ -306,23 +306,37 @@ TEST(LabelUtilTest, FormatPrometheusLabelsEscapingAndUtf8Value) { EXPECT_EQ(*result, "{query=\"line1\\nline2\\\"quoted\\\"with\\\\slash\"}"); // UTF-8 in value combined with newline and quotes. - MetricLabel utf8_labels[] = {{"error_detail", "失敗: \"IO\\Error\"\n詳細"}}; - EXPECT_EQ(FormatPrometheusLabels(utf8_labels), + const MetricLabel utf8_labels[] = { + {"error_detail", "失敗: \"IO\\Error\"\n詳細"}}; + char utf8_buffer[128]; + std::optional utf8_result = + FormatPrometheusLabelsToBuffer(utf8_labels, absl::MakeSpan(utf8_buffer)); + ASSERT_TRUE(utf8_result.has_value()); + EXPECT_EQ(*utf8_result, "{error_detail=\"失敗: \\\"IO\\\\Error\\\"\\n詳細\"}"); } -TEST(LabelUtilTest, FormatPrometheusLabelsRawKeys) { - // Verifies that FormatPrometheusLabels acts as a raw serializer without - // enforcing Prometheus key naming restrictions or duplicate key rejection. - MetricLabel raw_labels[] = { +TEST(LabelUtilTest, FormatPrometheusLabelsToBufferRawKeys) { + // Verifies that FormatPrometheusLabelsToBuffer acts as a raw serializer + // without enforcing Prometheus key naming restrictions or duplicate key + // rejection. + const MetricLabel raw_labels[] = { {"replica", "1"}, {"replica", "0"}, }; - EXPECT_EQ(FormatPrometheusLabels(raw_labels), - "{replica=\"0\",replica=\"1\"}"); - - MetricLabel special_keys[] = {{"bad-key", "value"}}; - EXPECT_EQ(FormatPrometheusLabels(special_keys), "{bad-key=\"value\"}"); + char raw_buffer[64]; + std::optional raw_result = + FormatPrometheusLabelsToBuffer(raw_labels, absl::MakeSpan(raw_buffer)); + ASSERT_TRUE(raw_result.has_value()); + EXPECT_EQ(*raw_result, "{replica=\"0\",replica=\"1\"}"); + + const MetricLabel special_keys[] = {{"bad-key", "value"}}; + char special_buffer[32]; + std::optional special_result = + FormatPrometheusLabelsToBuffer(special_keys, + absl::MakeSpan(special_buffer)); + ASSERT_TRUE(special_result.has_value()); + EXPECT_EQ(*special_result, "{bad-key=\"value\"}"); } TEST(LabelUtilTest, FormatPrometheusLabelsToBufferOverflow) { @@ -470,5 +484,196 @@ TEST(LabelUtilTest, ResolveLabelsConstexprEvaluation) { static_assert(kPartialResolved[1] == "val_b"); } +TEST(LabelUtilTest, PrometheusLabelViewEmpty) { + PrometheusLabelView view({}); + EXPECT_TRUE(view.view().empty()); + EXPECT_TRUE(PrometheusLabelView({}).ToOwned().empty()); +} + +TEST(LabelUtilTest, PrometheusLabelViewStackFit) { + const std::array labels = { + MetricLabel{.key = "direction", .value = "push"}, + MetricLabel{.key = "mode", .value = "direct"}, + }; + PrometheusLabelView view(labels); + EXPECT_EQ(view.view(), "{direction=\"push\",mode=\"direct\"}"); + EXPECT_EQ(PrometheusLabelView(labels).ToOwned(), + "{direction=\"push\",mode=\"direct\"}"); +} + +TEST(LabelUtilTest, PrometheusLabelViewDefaultCapacityOverflow) { + const std::string long_value(kDefaultPrometheusStackBufferSize + 64, 'x'); + const std::array labels = { + MetricLabel{.key = "key", .value = long_value}, + }; + PrometheusLabelView view(labels); + const std::string expected = absl::StrCat("{key=\"", long_value, "\"}"); + EXPECT_EQ(view.view(), expected); + const std::string owned = view.ToOwned(); + EXPECT_EQ(owned, expected); + EXPECT_TRUE(view.view().empty()); +} + +TEST(LabelUtilTest, PrometheusLabelViewExactStackBufferBoundaries) { + // "{k=\"...\"}" has 6 bytes of syntax overhead ('{', 'k', '=', '"', '"', + // '}'). 255 bytes (kDefaultPrometheusStackBufferSize - 1) + { + const std::string val_255(kDefaultPrometheusStackBufferSize - 7, 'a'); + const std::array labels = { + MetricLabel{.key = "k", .value = val_255}, + }; + const std::string expected = absl::StrCat("{k=\"", val_255, "\"}"); + ASSERT_EQ(expected.size(), kDefaultPrometheusStackBufferSize - 1); + PrometheusLabelView view(labels); + EXPECT_EQ(view.view(), expected); + EXPECT_EQ(view.ToOwned(), expected); + } + // 256 bytes (kDefaultPrometheusStackBufferSize, exact stack fit) + { + const std::string val_256(kDefaultPrometheusStackBufferSize - 6, 'b'); + const std::array labels = { + MetricLabel{.key = "k", .value = val_256}, + }; + const std::string expected = absl::StrCat("{k=\"", val_256, "\"}"); + ASSERT_EQ(expected.size(), kDefaultPrometheusStackBufferSize); + PrometheusLabelView view(labels); + EXPECT_EQ(view.view(), expected); + EXPECT_EQ(view.ToOwned(), expected); + } + // 257 bytes (kDefaultPrometheusStackBufferSize + 1, minimal heap fallback) + { + const std::string val_257(kDefaultPrometheusStackBufferSize - 5, 'c'); + const std::array labels = { + MetricLabel{.key = "k", .value = val_257}, + }; + const std::string expected = absl::StrCat("{k=\"", val_257, "\"}"); + ASSERT_EQ(expected.size(), kDefaultPrometheusStackBufferSize + 1); + PrometheusLabelView view(labels); + EXPECT_EQ(view.view(), expected); + EXPECT_EQ(view.ToOwned(), expected); + } +} + +TEST(LabelUtilTest, PrometheusLabelViewEmptyLabelValue) { + const std::array labels = { + MetricLabel{.key = "empty_key", .value = ""}, + }; + PrometheusLabelView view(labels); + EXPECT_EQ(view.view(), "{empty_key=\"\"}"); + EXPECT_EQ(view.ToOwned(), "{empty_key=\"\"}"); +} + +TEST(LabelUtilTest, PrometheusLabelViewRawKeys) { + const std::array raw_labels = { + MetricLabel{.key = "replica", .value = "1"}, + MetricLabel{.key = "replica", .value = "0"}, + }; + PrometheusLabelView view(raw_labels); + EXPECT_EQ(view.view(), "{replica=\"0\",replica=\"1\"}"); + + const std::array special_keys = { + MetricLabel{.key = "bad-key", .value = "value"}, + }; + PrometheusLabelView special_view(special_keys); + EXPECT_EQ(special_view.view(), "{bad-key=\"value\"}"); +} + +TEST(LabelUtilTest, PrometheusLabelViewUtf8Value) { + const std::array utf8_labels = { + MetricLabel{.key = "error_detail", .value = "失敗: \"IO\\Error\"\n詳細"}, + }; + PrometheusLabelView view(utf8_labels); + EXPECT_EQ(view.view(), "{error_detail=\"失敗: \\\"IO\\\\Error\\\"\\n詳細\"}"); +} + +TEST(LabelUtilTest, PrometheusLabelViewNonCopyableNonMovable) { + static_assert(!std::is_copy_constructible_v); + static_assert(!std::is_copy_assignable_v); + static_assert(!std::is_move_constructible_v); + static_assert(!std::is_move_assignable_v); +} + +TEST(LabelUtilTest, PrometheusLabelViewUnsortedLabels) { + const std::array labels = { + MetricLabel{.key = "z", .value = "1"}, + MetricLabel{.key = "a", .value = "2"}, + }; + PrometheusLabelView view(labels); + EXPECT_EQ(view.view(), "{a=\"2\",z=\"1\"}"); + EXPECT_EQ(PrometheusLabelView(labels).ToOwned(), "{a=\"2\",z=\"1\"}"); +} + +TEST(LabelUtilTest, PrometheusLabelViewUnsortedLabelsOverflow) { + const std::string long_value(kDefaultPrometheusStackBufferSize + 64, 'x'); + const std::array labels = { + MetricLabel{.key = "z", .value = long_value}, + MetricLabel{.key = "a", .value = "first"}, + }; + PrometheusLabelView view(labels); + const std::string expected = + absl::StrCat("{a=\"first\",z=\"", long_value, "\"}"); + EXPECT_EQ(view.view(), expected); + EXPECT_EQ(view.ToOwned(), expected); +} + +TEST(LabelUtilTest, + PrometheusLabelViewUnsortedExceedingInlinedCapacityAndStackBuffer) { + const std::string val(32, 'v'); + const MetricLabel labels[] = { + {"k09", val}, {"k08", val}, {"k07", val}, {"k06", val}, {"k05", val}, + {"k04", val}, {"k03", val}, {"k02", val}, {"k01", val}, {"k00", val}, + }; + const std::string expected = absl::StrCat( + "{k00=\"", val, "\",k01=\"", val, "\",k02=\"", val, "\",k03=\"", val, + "\",k04=\"", val, "\",k05=\"", val, "\",k06=\"", val, "\",k07=\"", val, + "\",k08=\"", val, "\",k09=\"", val, "\"}"); + ASSERT_GT(expected.size(), kDefaultPrometheusStackBufferSize); + PrometheusLabelView view(labels); + EXPECT_EQ(view.view(), expected); + EXPECT_EQ(view.ToOwned(), expected); +} + +TEST(LabelUtilTest, PrometheusLabelViewEscapedCharacters) { + const std::array labels = { + MetricLabel{.key = "msg", .value = "hello \"world\"\npath\\to"}, + }; + PrometheusLabelView view(labels); + EXPECT_EQ(view.view(), "{msg=\"hello \\\"world\\\"\\npath\\\\to\"}"); + EXPECT_EQ(PrometheusLabelView(labels).ToOwned(), + "{msg=\"hello \\\"world\\\"\\npath\\\\to\"}"); +} + +TEST(LabelUtilTest, PrometheusLabelViewEscapingOverflow) { + // 140 quotes: unescaped length is 140, but escaped length is 280 > 256. + const std::string quotes(140, '"'); + const std::array labels = { + MetricLabel{.key = "q", .value = quotes}, + }; + PrometheusLabelView view(labels); + std::string expected_val; + expected_val.reserve(280); + for (int i = 0; i < 140; ++i) { + expected_val += "\\\""; + } + const std::string expected = absl::StrCat("{q=\"", expected_val, "\"}"); + EXPECT_EQ(view.view(), expected); + EXPECT_EQ(view.ToOwned(), expected); +} + +TEST(LabelUtilTest, PrometheusLabelViewToOwnedStackAndHeap) { + // Stack path + const std::array stack_labels = { + MetricLabel{.key = "env", .value = "prod"}, + }; + EXPECT_EQ(PrometheusLabelView(stack_labels).ToOwned(), "{env=\"prod\"}"); + + // Heap path + const std::string long_value(kDefaultPrometheusStackBufferSize + 64, 'x'); + const std::array heap_labels = { + MetricLabel{.key = "env", .value = long_value}, + }; + const std::string expected = absl::StrCat("{env=\"", long_value, "\"}"); + EXPECT_EQ(PrometheusLabelView(heap_labels).ToOwned(), expected); +} } // namespace } // namespace tpu_raiden::telemetry