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"
+ """))