Merge "tp: fix performance of binder stdlib query"
diff --git a/CHANGELOG b/CHANGELOG
index ac77b20..c91f2b5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -8,6 +8,11 @@
   SDK:
     *
 
+v32.2 - 2023-02-16:
+  SDK:
+    * Fix MSVC warnings.
+
+
 v32.1 - 2023-02-01:
   Trace Processor:
     * Fix build on windows.
diff --git a/protos/perfetto/trace/interned_data/interned_data.proto b/protos/perfetto/trace/interned_data/interned_data.proto
index eddd7b1..59103a7 100644
--- a/protos/perfetto/trace/interned_data/interned_data.proto
+++ b/protos/perfetto/trace/interned_data/interned_data.proto
@@ -53,7 +53,7 @@
 // emitted proactively in advance of referring to them in later packets.
 //
 // Next reserved id: 8 (up to 15).
-// Next id: 29.
+// Next id: 30.
 message InternedData {
   // TODO(eseckler): Replace iid fields inside interned messages with
   // map<iid, message> type fields in InternedData.
@@ -111,4 +111,7 @@
   // This is is NOT the real address. This is to avoid disclosing KASLR through
   // traces.
   repeated InternedString kernel_symbols = 26;
+
+  // Interned string values in the DebugAnnotation proto.
+  repeated InternedString debug_annotation_string_values = 29;
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index fc47541..38b9f89 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -8656,7 +8656,7 @@
 //     }
 //   }
 //
-// Next ID: 17.
+// Next ID: 18.
 // Reserved ID: 15
 message DebugAnnotation {
   // Name fields are set only for dictionary entries.
@@ -8672,7 +8672,6 @@
     uint64 uint_value = 3;
     int64 int_value = 4;
     double double_value = 5;
-    string string_value = 6;
     // Pointers are stored in a separate type as the JSON output treats them
     // differently from other uint64 values.
     uint64 pointer_value = 7;
@@ -8683,6 +8682,11 @@
     // Legacy instrumentation may not support conversion of nested data to
     // NestedValue yet.
     string legacy_json_value = 9;
+
+    // interned and non-interned variants of strings.
+    string string_value = 6;
+    // Corresponds to |debug_annotation_string_values| field in InternedData.
+    uint64 string_value_iid = 17;
   }
 
   // Used to embed arbitrary proto messages (which are also typically used to
@@ -9850,7 +9854,7 @@
 // emitted proactively in advance of referring to them in later packets.
 //
 // Next reserved id: 8 (up to 15).
-// Next id: 29.
+// Next id: 30.
 message InternedData {
   // TODO(eseckler): Replace iid fields inside interned messages with
   // map<iid, message> type fields in InternedData.
@@ -9908,6 +9912,9 @@
   // This is is NOT the real address. This is to avoid disclosing KASLR through
   // traces.
   repeated InternedString kernel_symbols = 26;
+
+  // Interned string values in the DebugAnnotation proto.
+  repeated InternedString debug_annotation_string_values = 29;
 }
 
 // End of protos/perfetto/trace/interned_data/interned_data.proto
diff --git a/protos/perfetto/trace/track_event/debug_annotation.proto b/protos/perfetto/trace/track_event/debug_annotation.proto
index 35a041d..6dba2ab 100644
--- a/protos/perfetto/trace/track_event/debug_annotation.proto
+++ b/protos/perfetto/trace/track_event/debug_annotation.proto
@@ -57,7 +57,7 @@
 //     }
 //   }
 //
-// Next ID: 17.
+// Next ID: 18.
 // Reserved ID: 15
 message DebugAnnotation {
   // Name fields are set only for dictionary entries.
@@ -73,7 +73,6 @@
     uint64 uint_value = 3;
     int64 int_value = 4;
     double double_value = 5;
-    string string_value = 6;
     // Pointers are stored in a separate type as the JSON output treats them
     // differently from other uint64 values.
     uint64 pointer_value = 7;
@@ -84,6 +83,11 @@
     // Legacy instrumentation may not support conversion of nested data to
     // NestedValue yet.
     string legacy_json_value = 9;
+
+    // interned and non-interned variants of strings.
+    string string_value = 6;
+    // Corresponds to |debug_annotation_string_values| field in InternedData.
+    uint64 string_value_iid = 17;
   }
 
   // Used to embed arbitrary proto messages (which are also typically used to
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 25bdfbd..b5bad70 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -202,10 +202,10 @@
 
 PerfettoCmd::~PerfettoCmd() {
   PERFETTO_DCHECK(g_perfetto_cmd == this);
+  g_perfetto_cmd = nullptr;
   if (ctrl_c_handler_installed_) {
     task_runner_.RemoveFileDescriptorWatch(ctrl_c_evt_.fd());
   }
-  g_perfetto_cmd = nullptr;
 }
 
 void PerfettoCmd::PrintUsage(const char* argv0) {
@@ -881,11 +881,14 @@
     LogTriggerEvents(PerfettoTriggerAtom::kCmdTrigger, triggers_to_activate_);
 
     bool finished_with_success = false;
+    auto weak_this = weak_factory_.GetWeakPtr();
     TriggerProducer producer(
         &task_runner_,
-        [this, &finished_with_success](bool success) {
+        [weak_this, &finished_with_success](bool success) {
+          if (!weak_this)
+            return;
           finished_with_success = success;
-          task_runner_.Quit();
+          weak_this->task_runner_.Quit();
         },
         &triggers_to_activate_);
     task_runner_.Run();
@@ -1208,13 +1211,18 @@
       return;
     g_perfetto_cmd->SignalCtrlC();
   });
-  task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [this] {
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [weak_this] {
+    if (!weak_this)
+      return;
     PERFETTO_LOG("SIGINT/SIGTERM received: disabling tracing.");
-    ctrl_c_evt_.Clear();
-    consumer_endpoint_->Flush(0, [this](bool flush_success) {
+    weak_this->ctrl_c_evt_.Clear();
+    weak_this->consumer_endpoint_->Flush(0, [weak_this](bool flush_success) {
+      if (!weak_this)
+        return;
       if (!flush_success)
         PERFETTO_ELOG("Final flush unsuccessful.");
-      consumer_endpoint_->DisableTracing();
+      weak_this->consumer_endpoint_->DisableTracing();
     });
   });
 }
@@ -1249,10 +1257,13 @@
   PERFETTO_DCHECK(trace_config_->write_into_file());
 
   if (stop_trace_once_attached_) {
-    consumer_endpoint_->Flush(0, [this](bool flush_success) {
+    auto weak_this = weak_factory_.GetWeakPtr();
+    consumer_endpoint_->Flush(0, [weak_this](bool flush_success) {
+      if (!weak_this)
+        return;
       if (!flush_success)
         PERFETTO_ELOG("Final flush unsuccessful.");
-      consumer_endpoint_->DisableTracing();
+      weak_this->consumer_endpoint_->DisableTracing();
     });
   }
 }
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index b64159e..842ddcc 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -29,6 +29,7 @@
 #include "perfetto/ext/base/pipe.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/unix_task_runner.h"
+#include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/tracing/core/consumer.h"
 #include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
 #include "src/android_stats/perfetto_atoms.h"
@@ -162,6 +163,7 @@
   // How long we expect to trace for or 0 if the trace is indefinite.
   uint32_t expected_duration_ms_ = 0;
   bool trace_data_timeout_armed_ = false;
+  base::WeakPtrFactory<PerfettoCmd> weak_factory_{this};
 };
 
 }  // namespace perfetto
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index 98ff445..6613533 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -124,6 +124,7 @@
     "../../../gn:default_deps",
     "../../../protos/perfetto/common:zero",
     "../../../protos/perfetto/trace/interned_data:zero",
+    "../../../protos/perfetto/trace/profiling:zero",
     "../../../protos/perfetto/trace/track_event:zero",
     "../../../protos/perfetto/trace_processor:zero",
     "../../protozero",
@@ -243,10 +244,15 @@
     "../../../gn:gtest_and_gmock",
     "../../../protos/perfetto/common:zero",
     "../../../protos/perfetto/trace:non_minimal_zero",
+    "../../../protos/perfetto/trace/interned_data:zero",
+    "../../../protos/perfetto/trace/profiling:zero",
     "../../../protos/perfetto/trace/track_event:zero",
     "../../protozero",
     "../../protozero:testing_messages_zero",
     "../importers/proto:gen_cc_track_event_descriptor",
+    "../importers/proto:minimal",
+    "../storage",
+    "../types",
   ]
   if (enable_perfetto_zlib) {
     sources += [ "gzip_utils_unittest.cc" ]
diff --git a/src/trace_processor/util/debug_annotation_parser.cc b/src/trace_processor/util/debug_annotation_parser.cc
index 7b49d2d..0c2792e 100644
--- a/src/trace_processor/util/debug_annotation_parser.cc
+++ b/src/trace_processor/util/debug_annotation_parser.cc
@@ -15,10 +15,13 @@
  */
 
 #include "src/trace_processor/util/debug_annotation_parser.h"
+
 #include "perfetto/base/build_config.h"
-#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
 #include "src/trace_processor/util/interned_message_view.h"
 
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
+#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
+
 namespace perfetto {
 namespace trace_processor {
 namespace util {
@@ -81,6 +84,15 @@
     delegate.AddDouble(context_name, annotation.double_value());
   } else if (annotation.has_string_value()) {
     delegate.AddString(context_name, annotation.string_value());
+  } else if (annotation.has_string_value_iid()) {
+    auto* decoder = delegate.GetInternedMessage(
+        protos::pbzero::InternedData::kDebugAnnotationStringValues,
+        annotation.string_value_iid());
+    if (!decoder) {
+      return {base::ErrStatus("Debug annotation with invalid string_value_iid"),
+              false};
+    }
+    delegate.AddString(context_name, decoder->str().ToStdString());
   } else if (annotation.has_pointer_value()) {
     delegate.AddPointer(context_name, reinterpret_cast<const void*>(
                                           annotation.pointer_value()));
diff --git a/src/trace_processor/util/debug_annotation_parser_unittest.cc b/src/trace_processor/util/debug_annotation_parser_unittest.cc
index 7244d02..d9a4168 100644
--- a/src/trace_processor/util/debug_annotation_parser_unittest.cc
+++ b/src/trace_processor/util/debug_annotation_parser_unittest.cc
@@ -18,13 +18,19 @@
 
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
+#include "perfetto/trace_processor/trace_blob.h"
 #include "perfetto/trace_processor/trace_blob_view.h"
 #include "protos/perfetto/common/descriptor.pbzero.h"
+#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
 #include "protos/perfetto/trace/test_event.pbzero.h"
 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/test_messages.descriptor.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/util/interned_message_view.h"
 #include "src/trace_processor/util/proto_to_args_parser.h"
 #include "test/gtest_and_gmock.h"
@@ -48,10 +54,14 @@
 class DebugAnnotationParserTest : public ::testing::Test,
                                   public ProtoToArgsParser::Delegate {
  protected:
-  DebugAnnotationParserTest() {}
+  DebugAnnotationParserTest() : sequence_state_(&context_) {
+    context_.storage.reset(new TraceStorage());
+  }
 
   const std::vector<std::string>& args() const { return args_; }
 
+  PacketSequenceState* mutable_seq_state() { return &sequence_state_; }
+
  private:
   using Key = ProtoToArgsParser::Key;
 
@@ -120,14 +130,25 @@
     return ++array_indices_[array_key];
   }
 
-  InternedMessageView* GetInternedMessageView(uint32_t, uint64_t) override {
-    return nullptr;
+  InternedMessageView* GetInternedMessageView(uint32_t field_id,
+                                              uint64_t iid) override {
+    if (field_id !=
+        protos::pbzero::InternedData::kDebugAnnotationStringValuesFieldNumber) {
+      return nullptr;
+    }
+    return sequence_state_.current_generation()->GetInternedMessageView(
+        field_id, iid);
   }
 
-  PacketSequenceStateGeneration* seq_state() final { return nullptr; }
+  PacketSequenceStateGeneration* seq_state() final {
+    return sequence_state_.current_generation().get();
+  }
 
   std::vector<std::string> args_;
   std::map<std::string, size_t> array_indices_;
+
+  TraceProcessorContext context_;
+  PacketSequenceState sequence_state_;
 };
 
 // This test checks that in when an array is nested inside a dict which is
@@ -273,6 +294,33 @@
                           "root.field_string root.field_string value"));
 }
 
+TEST_F(DebugAnnotationParserTest, InternedString) {
+  protozero::HeapBuffered<protos::pbzero::DebugAnnotation> msg;
+  msg->set_name("root");
+
+  protozero::HeapBuffered<protos::pbzero::InternedString> string;
+  string->set_iid(1);
+  string->set_str("foo");
+  std::vector<uint8_t> data_serialized = string.SerializeAsArray();
+
+  mutable_seq_state()->InternMessage(
+      protos::pbzero::InternedData::kDebugAnnotationStringValuesFieldNumber,
+      TraceBlobView(
+          TraceBlob::CopyFrom(data_serialized.data(), data_serialized.size())));
+
+  msg->set_string_value_iid(1);
+
+  DescriptorPool pool;
+  ProtoToArgsParser args_parser(pool);
+  DebugAnnotationParser parser(args_parser);
+
+  auto status = ParseDebugAnnotation(parser, msg, *this);
+  EXPECT_TRUE(status.ok()) << "DebugAnnotationParser::Parse failed with error: "
+                           << status.message();
+
+  EXPECT_THAT(args(), testing::ElementsAre("root root foo"));
+}
+
 }  // namespace
 }  // namespace util
 }  // namespace trace_processor
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index d304ada..8cafc7d 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -836,10 +836,10 @@
 
   if (cfg.write_into_file()) {
     if (!fd ^ !cfg.output_path().empty()) {
-      tracing_sessions_.erase(tsid);
       MaybeLogUploadEvent(
           tracing_session->config, uuid,
           PerfettoStatsdAtom::kTracedEnableTracingInvalidFdOutputFile);
+      tracing_sessions_.erase(tsid);
       return PERFETTO_SVC_ERR(
           "When write_into_file==true either a FD needs to be passed or "
           "output_path must be populated (but not both)");
@@ -868,6 +868,7 @@
 
   // Initialize the log buffers.
   bool did_allocate_all_buffers = true;
+  bool invalid_buffer_config = false;
 
   // Allocate the trace buffers. Also create a map to translate a consumer
   // relative index (TraceConfig.DataSourceConfig.target_buffer) into the
@@ -884,14 +885,24 @@
       break;
     }
     tracing_session->buffers_index.push_back(global_id);
-    const size_t buf_size_bytes = buffer_cfg.size_kb() * 1024u;
-    total_buf_size_kb += buffer_cfg.size_kb();
+    // TraceBuffer size is limited to 32-bit.
+    const uint32_t buf_size_kb = buffer_cfg.size_kb();
+    const uint64_t buf_size_bytes = buf_size_kb * static_cast<uint64_t>(1024);
+    const size_t buf_size = static_cast<size_t>(buf_size_bytes);
+    if (buf_size_bytes == 0 ||
+        buf_size_bytes > std::numeric_limits<uint32_t>::max() ||
+        buf_size != buf_size_bytes) {
+      invalid_buffer_config = true;
+      did_allocate_all_buffers = false;
+      break;
+    }
+    total_buf_size_kb += buf_size_kb;
     TraceBuffer::OverwritePolicy policy =
         buffer_cfg.fill_policy() == TraceConfig::BufferConfig::DISCARD
             ? TraceBuffer::kDiscard
             : TraceBuffer::kOverwrite;
-    auto it_and_inserted = buffers_.emplace(
-        global_id, TraceBuffer::Create(buf_size_bytes, policy));
+    auto it_and_inserted =
+        buffers_.emplace(global_id, TraceBuffer::Create(buf_size, policy));
     PERFETTO_DCHECK(it_and_inserted.second);  // buffers_.count(global_id) == 0.
     std::unique_ptr<TraceBuffer>& trace_buffer = it_and_inserted.first->second;
     if (!trace_buffer) {
@@ -900,25 +911,29 @@
     }
   }
 
-  UpdateMemoryGuardrail();
-
   // This can happen if either:
   // - All the kMaxTraceBufferID slots are taken.
-  // - OOM, or, more relistically, we exhausted virtual memory.
+  // - OOM, or, more realistically, we exhausted virtual memory.
+  // - The buffer size in the config is invalid.
   // In any case, free all the previously allocated buffers and abort.
-  // TODO(fmayer): add a test to cover this case, this is quite subtle.
   if (!did_allocate_all_buffers) {
     for (BufferID global_id : tracing_session->buffers_index) {
       buffer_ids_.Free(global_id);
       buffers_.erase(global_id);
     }
-    tracing_sessions_.erase(tsid);
     MaybeLogUploadEvent(tracing_session->config, uuid,
                         PerfettoStatsdAtom::kTracedEnableTracingOom);
+    tracing_sessions_.erase(tsid);
+    if (invalid_buffer_config) {
+      return PERFETTO_SVC_ERR(
+          "Failed to allocate tracing buffers: Invalid buffer sizes");
+    }
     return PERFETTO_SVC_ERR(
         "Failed to allocate tracing buffers: OOM or too many buffers");
   }
 
+  UpdateMemoryGuardrail();
+
   consumer->tracing_session_id_ = tsid;
 
   // Setup the data sources on the producers without starting them.
diff --git a/src/tracing/core/tracing_service_impl_unittest.cc b/src/tracing/core/tracing_service_impl_unittest.cc
index 3a12a75..8b8c62c 100644
--- a/src/tracing/core/tracing_service_impl_unittest.cc
+++ b/src/tracing/core/tracing_service_impl_unittest.cc
@@ -54,10 +54,12 @@
 using ::testing::AssertionResult;
 using ::testing::AssertionSuccess;
 using ::testing::Contains;
+using ::testing::DoAll;
 using ::testing::Each;
 using ::testing::ElementsAreArray;
 using ::testing::Eq;
 using ::testing::ExplainMatchResult;
+using ::testing::HasSubstr;
 using ::testing::InSequence;
 using ::testing::Invoke;
 using ::testing::InvokeWithoutArgs;
@@ -66,6 +68,7 @@
 using ::testing::Ne;
 using ::testing::Not;
 using ::testing::Property;
+using ::testing::SaveArg;
 using ::testing::StrictMock;
 using ::testing::StringMatchResultListener;
 using ::testing::StrNe;
@@ -4026,4 +4029,25 @@
               Each(Property(&protos::gen::TracePacket::has_timestamp, false)));
 }
 
+TEST_F(TracingServiceImplTest, InvalidBufferSizes) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(128);
+  trace_config.add_buffers()->set_size_kb(256);
+  trace_config.add_buffers()->set_size_kb(4 * 1024 * 1024);
+  auto* ds = trace_config.add_data_sources();
+  auto* ds_config = ds->mutable_config();
+  ds_config->set_name("data_source");
+  consumer->EnableTracing(trace_config);
+
+  std::string error;
+  auto checkpoint = task_runner.CreateCheckpoint("tracing_disabled");
+  EXPECT_CALL(*consumer, OnTracingDisabled(_))
+      .WillOnce(DoAll(SaveArg<0>(&error), checkpoint));
+  task_runner.RunUntilCheckpoint("tracing_disabled");
+  EXPECT_THAT(error, HasSubstr("Invalid buffer sizes"));
+}
+
 }  // namespace perfetto
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 4367919..06500d8 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -51,6 +51,7 @@
 from diff_tests.memory.tests_metrics import MemoryMetrics
 from diff_tests.network.tests import Network
 from diff_tests.parsing.tests import Parsing
+from diff_tests.parsing.tests_debug_annotation import ParsingDebugAnnotation
 from diff_tests.parsing.tests_memory_counters import ParsingMemoryCounters
 from diff_tests.parsing.tests_rss_stats import ParsingRssStats
 from diff_tests.performance.tests import Performance
@@ -119,6 +120,8 @@
       *MemoryMetrics(index_path, 'memory', 'MemoryMetrics').fetch(),
       *Network(index_path, 'network', 'Network').fetch(),
       *Parsing(index_path, 'parsing', 'Parsing').fetch(),
+      *ParsingDebugAnnotation(index_path, 'parsing',
+                              'ParsingDebugAnnotation').fetch(),
       *ParsingRssStats(index_path, 'parsing', 'ParsingRssStats').fetch(),
       *ParsingMemoryCounters(index_path, 'parsing',
                              'ParsingMemoryCounters').fetch(),
diff --git a/test/trace_processor/diff_tests/parsing/tests_debug_annotation.py b/test/trace_processor/diff_tests/parsing/tests_debug_annotation.py
new file mode 100644
index 0000000..fab40cb
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/tests_debug_annotation.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# 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 a
+#
+#      http://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.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ParsingDebugAnnotation(TestSuite):
+  # Verify parsing of interned_string_value in DebugAnnotation proto.
+  def test_interned_string_value(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          trusted_packet_sequence_id: 1
+          incremental_state_cleared: true
+          track_descriptor {
+            uuid: 1
+            thread {
+              pid: 5
+              tid: 1
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 2000
+          interned_data {
+            debug_annotation_names {
+                iid: 1
+                name: "key"
+            }
+            debug_annotation_string_values {
+                iid: 1
+                str: "value"
+            }
+          }
+          track_event {
+            track_uuid: 1
+            type: TYPE_INSTANT
+            name: "slice1"
+            debug_annotations {
+              name_iid: 1
+              string_value_iid: 1
+            }
+          }
+        }
+        """),
+        query="""
+          SELECT EXTRACT_ARG(s.arg_set_id, 'debug.key') AS value
+          FROM slice s;
+        """,
+        out=Csv("""
+        "value"
+        "value"
+        """))