Merge "docs: Add doc for release process"
diff --git a/CHANGELOG b/CHANGELOG
index 79050a5..c01f292 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,9 +4,9 @@
   Trace Processor:
     *
   UI:
-    *
+    * Fixed ADB connection issues ("unable to reset device") on Windows and Mac.
   SDK:
-    *
+    * Added support for writing track events using custom clock timestamps.
 
 
 v16.0 - 2021-06-01:
diff --git a/docs/reference/checkpoint-atoms.md b/docs/design-docs/checkpoint-atoms.md
similarity index 100%
rename from docs/reference/checkpoint-atoms.md
rename to docs/design-docs/checkpoint-atoms.md
diff --git a/docs/toc.md b/docs/toc.md
index 6f09a13..b9f8b9c 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -74,3 +74,4 @@
     * [Perfetto CI](design-docs/continuous-integration.md)
     * [ProtoZero](design-docs/protozero.md)
     * [Security model](design-docs/security-model.md)
+    * [Statsd Checkpoint Atoms](design-docs/checkpoint-atoms.md)
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index 12a5a85..9229153 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -35,11 +35,6 @@
 
 namespace perfetto {
 
-struct TraceTimestamp {
-  protos::pbzero::BuiltinClock clock_id;
-  uint64_t nanoseconds;
-};
-
 // This template provides a way to convert an abstract timestamp into the trace
 // clock timebase in nanoseconds. By specialising this template and defining
 // static ConvertTimestampToTraceTimeNs function in it the user can register
@@ -60,6 +55,15 @@
   }
 };
 
+// A pass-through implementation for the trace timestamp structure.
+template <>
+struct TraceTimestampTraits<TraceTimestamp> {
+  static inline TraceTimestamp ConvertTimestampToTraceTimeNs(
+      const TraceTimestamp& timestamp) {
+    return timestamp;
+  }
+};
+
 namespace internal {
 namespace {
 
@@ -440,19 +444,16 @@
             return;
           }
 
-          // TODO(skyostil): Support additional clock ids.
           TraceTimestamp trace_timestamp = ::perfetto::TraceTimestampTraits<
               TimestampType>::ConvertTimestampToTraceTimeNs(timestamp);
-          PERFETTO_DCHECK(trace_timestamp.clock_id ==
-                          TrackEventInternal::GetClockId());
 
           // Make sure incremental state is valid.
           TraceWriterBase* trace_writer = ctx.tls_inst_->trace_writer.get();
           TrackEventIncrementalState* incr_state = ctx.GetIncrementalState();
           if (incr_state->was_cleared) {
             incr_state->was_cleared = false;
-            TrackEventInternal::ResetIncrementalState(
-                trace_writer, trace_timestamp.nanoseconds);
+            TrackEventInternal::ResetIncrementalState(trace_writer,
+                                                      trace_timestamp);
           }
 
           // Write the track descriptor before any event on the track.
@@ -465,7 +466,7 @@
           {
             auto event_ctx = TrackEventInternal::WriteEvent(
                 trace_writer, incr_state, static_category, event_name, type,
-                trace_timestamp.nanoseconds);
+                trace_timestamp);
             // Write dynamic categories (except for events that don't require
             // categories). For counter events, the counter name (and optional
             // category) is stored as part of the track descriptor instead being
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index d293039..afd874c 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -32,6 +32,13 @@
 #include <unordered_map>
 
 namespace perfetto {
+
+// Represents a point in time for the clock specified by |clock_id|.
+struct TraceTimestamp {
+  protos::pbzero::BuiltinClock clock_id;
+  uint64_t nanoseconds;
+};
+
 class EventContext;
 class TrackEventSessionObserver;
 struct Category;
@@ -139,9 +146,9 @@
       const Category* category,
       const char* name,
       perfetto::protos::pbzero::TrackEvent::Type,
-      uint64_t timestamp = GetTimeNs());
+      TraceTimestamp timestamp = {GetClockId(), GetTimeNs()});
 
-  static void ResetIncrementalState(TraceWriterBase*, uint64_t timestamp);
+  static void ResetIncrementalState(TraceWriterBase*, TraceTimestamp);
 
   template <typename T>
   static void AddDebugAnnotation(perfetto::EventContext* event_ctx,
@@ -171,7 +178,7 @@
   static void WriteTrackDescriptor(const TrackType& track,
                                    TraceWriterBase* trace_writer) {
     TrackRegistry::Get()->SerializeTrack(
-        track, NewTracePacket(trace_writer, GetTimeNs()));
+        track, NewTracePacket(trace_writer, {GetClockId(), GetTimeNs()}));
   }
 
   // Get the current time in nanoseconds in the trace clock timebase.
@@ -195,7 +202,7 @@
  private:
   static protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket(
       TraceWriterBase*,
-      uint64_t timestamp,
+      TraceTimestamp,
       uint32_t seq_flags =
           protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
   static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
diff --git a/include/perfetto/tracing/traced_proto.h b/include/perfetto/tracing/traced_proto.h
index 4298bcc..a887d96 100644
--- a/include/perfetto/tracing/traced_proto.h
+++ b/include/perfetto/tracing/traced_proto.h
@@ -130,6 +130,67 @@
 
 namespace internal {
 
+template <typename FieldMetadata,
+          bool is_message,
+          protozero::proto_utils::RepetitionType repetition_type>
+struct TypedProtoWriterImpl;
+
+// Simple non-repeated field.
+template <typename FieldMetadata>
+struct TypedProtoWriterImpl<
+    FieldMetadata,
+    /*is_message=*/false,
+    protozero::proto_utils::RepetitionType::kNotRepeated> {
+  template <typename Proto, typename ValueType>
+  static void Write(TracedProto<Proto> context, ValueType&& value) {
+    protozero::internal::FieldWriter<FieldMetadata::kProtoFieldType>::Append(
+        *context.message(), FieldMetadata::kFieldId, value);
+  }
+};
+
+// Simple repeated non-packed field.
+template <typename FieldMetadata>
+struct TypedProtoWriterImpl<
+    FieldMetadata,
+    /*is_message=*/false,
+    protozero::proto_utils::RepetitionType::kRepeatedNotPacked> {
+  template <typename Proto, typename ValueType>
+  static void Write(TracedProto<Proto> context, ValueType&& value) {
+    for (auto&& item : value) {
+      protozero::internal::FieldWriter<FieldMetadata::kProtoFieldType>::Append(
+          *context.message(), FieldMetadata::kFieldId, item);
+    }
+  }
+};
+
+// Nested repeated non-packed field.
+template <typename FieldMetadata>
+struct TypedProtoWriterImpl<
+    FieldMetadata,
+    /*is_message=*/true,
+    protozero::proto_utils::RepetitionType::kNotRepeated> {
+  template <typename Proto, typename ValueType>
+  static void Write(TracedProto<Proto> context, ValueType&& value) {
+    // TODO(altimin): support TraceFormatTraits here.
+    value.WriteIntoTrace(context.template WriteNestedMessage<FieldMetadata>());
+  }
+};
+
+// Nested repeated non-packed field.
+template <typename FieldMetadata>
+struct TypedProtoWriterImpl<
+    FieldMetadata,
+    /*is_message=*/true,
+    protozero::proto_utils::RepetitionType::kRepeatedNotPacked> {
+  template <typename Proto, typename ValueType>
+  static void Write(TracedProto<Proto> context, ValueType&& value) {
+    // TODO(altimin): support TraceFormatTraits here.
+    for (auto&& item : value) {
+      item.WriteIntoTrace(context.template WriteNestedMessage<FieldMetadata>());
+    }
+  }
+};
+
 // TypedProtoWriter takes the protozero message (TracedProto<MessageType>),
 // field description (FieldMetadata) and value and writes the given value
 // into the given field of the given protozero message.
@@ -150,58 +211,17 @@
                     RepetitionType::kRepeatedPacked,
                 "writing packed fields isn't supported yet");
 
+  template <bool is_message, RepetitionType repetition_type>
+  struct Writer;
+
  public:
-  // Implementation note: typename Check=void is used to ensure that SFINAE
-  // kicks in and the methods which do not match FieldMetadata do not fail
-  // to compile. std::is_same<Check,void> prevents early evaluation of the
-  // first enable_if_t argument.
-
-  // Simple non-repeated field.
-  template <typename Proto, typename ValueType, typename Check = void>
-  static typename base::enable_if_t<
-      FieldMetadata::kProtoFieldType != ProtoSchemaType::kMessage &&
-      FieldMetadata::kRepetitionType == RepetitionType::kNotRepeated &&
-      std::is_same<Check, void>::value>
-  Write(TracedProto<Proto> context, ValueType&& value) {
-    protozero::internal::FieldWriter<FieldMetadata::kProtoFieldType>::Append(
-        *context.message(), FieldMetadata::kFieldId, value);
-  }
-
-  // Simple repeated non-packed field.
-  template <typename Proto, typename ValueType, typename Check = void>
-  static typename base::enable_if_t<
-      FieldMetadata::kProtoFieldType != ProtoSchemaType::kMessage &&
-      FieldMetadata::kRepetitionType == RepetitionType::kRepeatedNotPacked &&
-      std::is_same<Check, void>::value>
-  Write(TracedProto<Proto> context, ValueType&& value) {
-    for (auto&& item : value) {
-      protozero::internal::FieldWriter<FieldMetadata::kProtoFieldType>::Append(
-          *context.message(), FieldMetadata::kFieldId, item);
-    }
-  }
-
-  // Nested non-repeated field.
-  template <typename Proto, typename ValueType, typename Check = void>
-  static typename base::enable_if_t<
-      FieldMetadata::kProtoFieldType == ProtoSchemaType::kMessage &&
-      FieldMetadata::kRepetitionType == RepetitionType::kNotRepeated &&
-      std::is_same<Check, void>::value>
-  Write(TracedProto<Proto> context, ValueType&& value) {
-    // TODO(altimin): support TraceFormatTraits here.
-    value.WriteIntoTrace(context.template WriteNestedMessage<FieldMetadata>());
-  }
-
-  // Nested repeated non-packed field.
-  template <typename Proto, typename ValueType, typename Check = void>
-  static typename base::enable_if_t<
-      FieldMetadata::kProtoFieldType == ProtoSchemaType::kMessage &&
-      FieldMetadata::kRepetitionType == RepetitionType::kRepeatedNotPacked &&
-      std::is_same<Check, void>::value>
-  Write(TracedProto<Proto> context, ValueType&& value) {
-    // TODO(altimin): support TraceFormatTraits here.
-    for (auto&& item : value) {
-      item.WriteIntoTrace(context.template WriteNestedMessage<FieldMetadata>());
-    }
+  template <typename Proto, typename ValueType>
+  static void Write(TracedProto<Proto> context, ValueType&& value) {
+    TypedProtoWriterImpl<
+        FieldMetadata,
+        FieldMetadata::kProtoFieldType == ProtoSchemaType::kMessage,
+        FieldMetadata::kRepetitionType>::Write(std::move(context),
+                                               std::forward<ValueType>(value));
   }
 };
 
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index b2eb293..ebf198c 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -160,14 +160,10 @@
       ":platform_impl",
     ]
 
-    # TODO(primiano/altimin): these sources build with clang-cl but fail to
-    # build with MSVC 2019. Investigate.
-    if (!is_win || is_clang) {
-      sources += [
-        "traced_proto_unittest.cc",
-        "traced_value_unittest.cc",
-      ]
-    }
+    sources += [
+      "traced_proto_unittest.cc",
+      "traced_value_unittest.cc",
+    ]
   }
 }
 
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index 736c312..5e4a154 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -309,7 +309,7 @@
 
 // static
 void TrackEventInternal::ResetIncrementalState(TraceWriterBase* trace_writer,
-                                               uint64_t timestamp) {
+                                               TraceTimestamp timestamp) {
   auto default_track = ThreadTrack::Current();
   {
     // Mark any incremental state before this point invalid. Also set up
@@ -337,14 +337,18 @@
 // static
 protozero::MessageHandle<protos::pbzero::TracePacket>
 TrackEventInternal::NewTracePacket(TraceWriterBase* trace_writer,
-                                   uint64_t timestamp,
+                                   TraceTimestamp timestamp,
                                    uint32_t seq_flags) {
   auto packet = trace_writer->NewTracePacket();
-  packet->set_timestamp(timestamp);
-  // TODO(skyostil): Stop emitting this for every event once the trace
-  // processor understands trace packet defaults.
-  if (GetClockId() != protos::pbzero::BUILTIN_CLOCK_BOOTTIME)
+  packet->set_timestamp(timestamp.nanoseconds);
+  if (timestamp.clock_id != GetClockId()) {
+    packet->set_timestamp_clock_id(static_cast<uint32_t>(timestamp.clock_id));
+  } else if (GetClockId() != protos::pbzero::BUILTIN_CLOCK_BOOTTIME) {
+    // TODO(skyostil): Stop emitting the clock id for the default trace clock
+    // for every event once the trace processor understands trace packet
+    // defaults.
     packet->set_timestamp_clock_id(GetClockId());
+  }
   packet->set_sequence_flags(seq_flags);
   return packet;
 }
@@ -356,7 +360,7 @@
     const Category* category,
     const char* name,
     perfetto::protos::pbzero::TrackEvent::Type type,
-    uint64_t timestamp) {
+    TraceTimestamp timestamp) {
   PERFETTO_DCHECK(g_main_thread);
   PERFETTO_DCHECK(!incr_state->was_cleared);
 
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 061e2fe..ed08a17 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -1473,6 +1473,65 @@
   perfetto::TrackEvent::EraseTrackDescriptor(track);
 }
 
+TEST_P(PerfettoApiTest, TrackEventCustomTimestampClock) {
+  // Create a new trace session.
+  auto* tracing_session = NewTraceWithCategories({"foo"});
+  tracing_session->get()->StartBlocking();
+
+  const perfetto::protos::pbzero::BuiltinClock kMyClockId =
+      static_cast<perfetto::protos::pbzero::BuiltinClock>(700);
+  const uint64_t kTimestamp = 12345678;
+
+  // First emit a clock snapshot that maps our custom clock to regular trace
+  // time. Note that the clock snapshot should come before any events
+  // referencing that clock.
+  perfetto::TrackEvent::Trace([](perfetto::TrackEvent::TraceContext ctx) {
+    auto packet = ctx.NewTracePacket();
+    packet->set_timestamp_clock_id(perfetto::TrackEvent::GetTraceClockId());
+    packet->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
+    auto* clock_snapshot = packet->set_clock_snapshot();
+    // First set the reference clock, i.e., the default trace clock in this
+    // case.
+    auto* clock = clock_snapshot->add_clocks();
+    clock->set_clock_id(perfetto::TrackEvent::GetTraceClockId());
+    clock->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
+    // Then set the value of our reference clock at the same point in time. We
+    // pretend our clock is one second behind trace time.
+    clock = clock_snapshot->add_clocks();
+    clock->set_clock_id(kMyClockId);
+    clock->set_timestamp(kTimestamp + 1000000000ull);
+  });
+
+  // Next emit a trace event with a custom timestamp and a custom clock.
+  TRACE_EVENT_INSTANT("foo", "EventWithCustomTime",
+                      perfetto::TraceTimestamp{kMyClockId, kTimestamp});
+  TRACE_EVENT_INSTANT("foo", "EventWithNormalTime");
+
+  perfetto::TrackEvent::Flush();
+  tracing_session->get()->StopBlocking();
+
+  std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+  perfetto::protos::gen::Trace trace;
+  ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+  // Check that both the clock id and the timestamp got written together with
+  // the packet. Note that we don't check the actual clock sync behavior here
+  // since that happens in the Trace Processor instead.
+  bool found_clock_snapshot = false;
+  bool found_event = false;
+  for (const auto& packet : trace.packet()) {
+    if (packet.has_clock_snapshot())
+      found_clock_snapshot = true;
+    if (!packet.has_track_event() || packet.timestamp() != kTimestamp)
+      continue;
+    found_event = true;
+    EXPECT_EQ(static_cast<uint32_t>(kMyClockId), packet.timestamp_clock_id());
+    EXPECT_EQ(kTimestamp, packet.timestamp());
+  }
+  EXPECT_TRUE(found_clock_snapshot);
+  EXPECT_TRUE(found_event);
+}
+
 TEST_P(PerfettoApiTest, LegacyEventWithThreadOverride) {
   // Create a new trace session.
   auto* tracing_session = NewTraceWithCategories({"cat"});
diff --git a/ui/release/channels.json b/ui/release/channels.json
index 526675f..04e189d 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -2,11 +2,11 @@
   "channels": [
     {
       "name": "stable",
-      "rev": "85ad7248ca898e29e71a6e76ea85f070c4c3e3a9"
+      "rev": "6dd6756ffbdff4f845c4db28e1fd5aed9ba77b56"
     },
     {
       "name": "canary",
-      "rev": "bae8193de6c017394901163b7817157342914679"
+      "rev": "3e21f613f20779c04b0bcc937f2605b9b05556ad"
     },
     {
       "name": "autopush",
diff --git a/ui/src/controller/adb.ts b/ui/src/controller/adb.ts
index 5ed6152..47783a6 100644
--- a/ui/src/controller/adb.ts
+++ b/ui/src/controller/adb.ts
@@ -114,8 +114,6 @@
     this.key = await AdbOverWebUsb.initKey();
 
     await this.dev.open();
-    await this.dev.reset();  // The reset is done so that we can claim the
-                             // device before adb server can.
 
     const {configValue, usbInterfaceNumber, endpoints} =
         this.findInterfaceAndEndpoint();