Merge "TraceProcessor RPC: add binary-pipe interface"
diff --git a/include/perfetto/tracing/event_context.h b/include/perfetto/tracing/event_context.h
index 21cb788..e0c1a16 100644
--- a/include/perfetto/tracing/event_context.h
+++ b/include/perfetto/tracing/event_context.h
@@ -72,7 +72,7 @@
     static_assert(std::is_base_of<protozero::Message, MessageType>::value,
                   "TracedProto can be used only with protozero messages");
 
-    return TracedProto<MessageType>(message, *this);
+    return TracedProto<MessageType>(message, this);
   }
 
  private:
diff --git a/include/perfetto/tracing/traced_proto.h b/include/perfetto/tracing/traced_proto.h
index a52972b..4298bcc 100644
--- a/include/perfetto/tracing/traced_proto.h
+++ b/include/perfetto/tracing/traced_proto.h
@@ -60,8 +60,6 @@
 
   MessageType* message() { return message_; }
 
-  EventContext& context() const { return context_; }
-
   // Write additional untyped values into the same context, which is useful
   // when a given C++ class has a typed representation, but also either has
   // members which can only be written into an untyped context (e.g. they are
@@ -87,14 +85,47 @@
     return TracedDictionary(message_, MessageType::kDebugAnnotations, nullptr);
   }
 
+  // Write a nested message into a field according to the provided metadata.
+  template <typename FieldMetadata>
+  TracedProto<typename FieldMetadata::cpp_field_type> WriteNestedMessage() {
+    static_assert(std::is_base_of<MessageType,
+                                  typename FieldMetadata::message_type>::value,
+                  "Field should belong to the current message");
+    return TracedProto<typename FieldMetadata::cpp_field_type>(
+        message_->template BeginNestedMessage<
+            typename FieldMetadata::cpp_field_type>(FieldMetadata::kFieldId),
+        context_);
+  }
+
+  template <typename FieldMetadata>
+  TracedProto<typename FieldMetadata::cpp_field_type> WriteNestedMessage(
+      protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>) {
+    return WriteNestedMessage<FieldMetadata>();
+  }
+
  private:
   friend class EventContext;
+  // Allow TracedProto<Foo> to create TracedProto<Bar>.
+  template <typename T>
+  friend class TracedProto;
 
-  TracedProto(MessageType* message, EventContext& context)
+  // Wraps a raw protozero message using the same context as the current object.
+  template <typename ChildMessageType>
+  TracedProto<ChildMessageType> Wrap(ChildMessageType* message) {
+    return TracedProto(message, context_);
+  }
+
+  // Context might be null here when writing typed message which is
+  // nested into untyped legacy trace event macro argument.
+  // TODO(altimin): Turn this into EventContext& when this case is eliminated
+  // and expose it in public API.
+  EventContext* context() const { return context_; }
+
+  TracedProto(MessageType* message, EventContext* context)
       : message_(message), context_(context) {}
 
   MessageType* const message_;
-  EventContext& context_;
+  EventContext* context_;
 };
 
 namespace internal {
@@ -157,11 +188,7 @@
       std::is_same<Check, void>::value>
   Write(TracedProto<Proto> context, ValueType&& value) {
     // TODO(altimin): support TraceFormatTraits here.
-    value.WriteIntoTrace(
-        context.context().Wrap(context.message()
-                                   ->template BeginNestedMessage<
-                                       typename FieldMetadata::cpp_field_type>(
-                                       FieldMetadata::kFieldId)));
+    value.WriteIntoTrace(context.template WriteNestedMessage<FieldMetadata>());
   }
 
   // Nested repeated non-packed field.
@@ -173,11 +200,7 @@
   Write(TracedProto<Proto> context, ValueType&& value) {
     // TODO(altimin): support TraceFormatTraits here.
     for (auto&& item : value) {
-      item.WriteIntoTrace(context.context().Wrap(
-          context.message()
-              ->template BeginNestedMessage<
-                  typename FieldMetadata::cpp_field_type>(
-                  FieldMetadata::kFieldId)));
+      item.WriteIntoTrace(context.template WriteNestedMessage<FieldMetadata>());
     }
   }
 };
diff --git a/src/android_stats/perfetto_atoms.h b/src/android_stats/perfetto_atoms.h
index 3731c2e..0a857b9 100644
--- a/src/android_stats/perfetto_atoms.h
+++ b/src/android_stats/perfetto_atoms.h
@@ -69,6 +69,7 @@
   kTracedEnableTracingUnknown = 35,
   kTracedStartTracingInvalidSessionState = 36,
   kTracedEnableTracingInvalidFilter = 47,
+  kTracedEnableTracingOobTargetBuffer = 48,
 
   // Checkpoints inside perfetto_cmd after tracing has finished.
   kOnTracingDisabled = 4,
diff --git a/src/profiling/memory/shared_ring_buffer_write_fuzzer.cc b/src/profiling/memory/shared_ring_buffer_write_fuzzer.cc
index 486033e..62b82eb 100644
--- a/src/profiling/memory/shared_ring_buffer_write_fuzzer.cc
+++ b/src/profiling/memory/shared_ring_buffer_write_fuzzer.cc
@@ -70,6 +70,7 @@
   memcpy(&header, data, sizeof(header));
   SharedRingBuffer::MetadataPage& metadata_page = header.metadata_page;
   metadata_page.spinlock.locked = false;
+  metadata_page.spinlock.poisoned = false;
 
   PERFETTO_CHECK(ftruncate(*fd, static_cast<off_t>(total_size_pages *
                                                    base::kPageSize)) == 0);
diff --git a/src/trace_processor/importers/proto/profiler_util.cc b/src/trace_processor/importers/proto/profiler_util.cc
index 175942f..c28f2fc 100644
--- a/src/trace_processor/importers/proto/profiler_util.cc
+++ b/src/trace_processor/importers/proto/profiler_util.cc
@@ -104,7 +104,8 @@
     return "com.google.android.gm";
   }
 
-  if (location.find("PrebuiltGmsCore") != std::string::npos) {
+  if (location.find("PrebuiltGmsCore") != std::string::npos ||
+      location.find("com.google.android.gms") != std::string::npos) {
     return "com.google.android.gms";
   }
 
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 7926bd3..6be84c5 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -110,6 +110,8 @@
   }
   bool AddJson(const Key& key, const protozero::ConstChars& value) final {
     auto json_value = json::ParseJsonString(value);
+    if (!json_value)
+      return false;
     return json::AddJsonValueToArgs(*json_value, base::StringView(key.flat_key),
                                     base::StringView(key.key), &storage_,
                                     &inserter_);
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 63839b0..1a5b43d 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -647,6 +647,21 @@
     return PERFETTO_SVC_ERR("Too many buffers configured (%d)",
                             cfg.buffers_size());
   }
+  // Check that the config specifies all buffers for its data sources. This
+  // is also checked in SetupDataSource, but it is simpler to return a proper
+  // error to the consumer from here (and there will be less state to undo).
+  for (const TraceConfig::DataSource& cfg_data_source : cfg.data_sources()) {
+    size_t num_buffers = static_cast<size_t>(cfg.buffers_size());
+    size_t target_buffer = cfg_data_source.config().target_buffer();
+    if (target_buffer >= num_buffers) {
+      MaybeLogUploadEvent(
+          cfg, PerfettoStatsdAtom::kTracedEnableTracingOobTargetBuffer);
+      return PERFETTO_SVC_ERR(
+          "Data source \"%s\" specified an out of bounds target_buffer (%zu >= "
+          "%zu)",
+          cfg_data_source.config().name().c_str(), target_buffer, num_buffers);
+    }
+  }
 
   if (!cfg.unique_session_name().empty()) {
     const std::string& name = cfg.unique_session_name();
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index d91a3bb..1b3580e 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -544,11 +544,13 @@
         let hasSliceName = false;
         let hasDur = false;
         let hasUpid = false;
+        let hasValue = false;
         for (let i = 0; i < slowlyCountRows(result); i++) {
           const name = result.columns[1].stringValues![i];
           hasSliceName = hasSliceName || name === 'slice_name';
           hasDur = hasDur || name === 'dur';
           hasUpid = hasUpid || name === 'upid';
+          hasValue = hasValue || name === 'value';
         }
 
         const upidColumnSelect = hasUpid ? 'upid' : '0 AS upid';
@@ -579,7 +581,6 @@
           `);
         }
 
-        const hasValue = result.columnDescriptors.some(x => x.name === 'value');
         if (hasValue) {
           const minMax = await engine.query(`
           SELECT MIN(value) as min_value, MAX(value) as max_value