Merge "trace_processor: Expose deltas of ftrace stats"
diff --git a/Android.bp b/Android.bp
index 372249e..c7f2447 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8116,6 +8116,8 @@
         "src/trace_processor/metrics/sql/android/span_view_stats.sql",
         "src/trace_processor/metrics/sql/android/startup/hsc.sql",
         "src/trace_processor/metrics/sql/android/startup/launches.sql",
+        "src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql",
+        "src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql",
         "src/trace_processor/metrics/sql/android/thread_counter_span_view.sql",
         "src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
         "src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
diff --git a/BUILD b/BUILD
index 94521a4..ccc51d8 100644
--- a/BUILD
+++ b/BUILD
@@ -1088,6 +1088,8 @@
         "src/trace_processor/metrics/sql/android/span_view_stats.sql",
         "src/trace_processor/metrics/sql/android/startup/hsc.sql",
         "src/trace_processor/metrics/sql/android/startup/launches.sql",
+        "src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql",
+        "src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql",
         "src/trace_processor/metrics/sql/android/thread_counter_span_view.sql",
         "src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
         "src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index 7ccbad4..8f998dd 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -237,15 +237,14 @@
     if (glob_path.length() + 1 > MAX_PATH)
       return base::ErrStatus("Directory path %s is too long", dir_path.c_str());
     WIN32_FIND_DATAA ffd;
-    // We do not use a ScopedResource for the HANDLE from FindFirstFile because
-    // the invalid value INVALID_HANDLE_VALUE is not a constexpr under some
-    // compile configurations, and thus cannot be used as a template argument.
-    HANDLE hFind = FindFirstFileA(glob_path.c_str(), &ffd);
-    if (hFind == INVALID_HANDLE_VALUE) {
+
+    base::ScopedResource<HANDLE, FindClose, nullptr, false,
+                         base::PlatformHandleChecker>
+        hFind(FindFirstFileA(glob_path.c_str(), &ffd));
+    if (!hFind) {
       // For empty directories, there should be at least one entry '.'.
       // If FindFirstFileA returns INVALID_HANDLE_VALUE, this means directory
       // couldn't be accessed.
-      FindClose(hFind);
       return base::ErrStatus("Failed to open directory %s", cur_dir.c_str());
     }
     do {
@@ -254,13 +253,12 @@
       if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
         std::string subdir_path = cur_dir + ffd.cFileName + '/';
         dir_queue.push_back(subdir_path);
-      } else if (ffd.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) {
+      } else {
         const std::string full_path = cur_dir + ffd.cFileName;
         PERFETTO_CHECK(full_path.length() > root_dir_path.length());
         output.push_back(full_path.substr(root_dir_path.length()));
       }
-    } while (FindNextFileA(hFind, &ffd));
-    FindClose(hFind);
+    } while (FindNextFileA(*hFind, &ffd));
 #else
     ScopedDir dir = ScopedDir(opendir(cur_dir.c_str()));
     if (!dir) {
diff --git a/src/base/http/http_server.cc b/src/base/http/http_server.cc
index 17a1652..1a90339 100644
--- a/src/base/http/http_server.cc
+++ b/src/base/http/http_server.cc
@@ -464,11 +464,12 @@
   append("HTTP/1.1 ");
   append(http_code);
   append("\r\n");
-  for (const char* hdr : headers) {
-    if (strlen(hdr) == 0)
+  for (const char* hdr_cstr : headers) {
+    StringView hdr = (hdr_cstr);
+    if (hdr.empty())
       continue;
-    has_connection_header |= strncasecmp(hdr, "connection:", 11) == 0;
-    append(hdr);
+    has_connection_header |= hdr.substr(0, 11).CaseInsensitiveEq("connection:");
+    append(hdr_cstr);
     append("\r\n");
   }
   content_len_actual_ = 0;
diff --git a/src/base/http/http_server_unittest.cc b/src/base/http/http_server_unittest.cc
index 9726ab6..be65198 100644
--- a/src/base/http/http_server_unittest.cc
+++ b/src/base/http/http_server_unittest.cc
@@ -66,16 +66,17 @@
     auto checkpoint = task_runner_->CreateCheckpoint(checkpoint_name);
     std::string rxbuf;
     sock.SetBlocking(false);
-    task_runner_->AddFileDescriptorWatch(sock.fd(), [&] {
+    task_runner_->AddFileDescriptorWatch(sock.watch_handle(), [&] {
       char buf[1024]{};
       auto rsize = PERFETTO_EINTR(sock.Receive(buf, sizeof(buf)));
-      ASSERT_GE(rsize, 0);
+      if (rsize < 0)
+        return;
       rxbuf.append(buf, static_cast<size_t>(rsize));
       if (rsize == 0 || (min_bytes && rxbuf.length() >= min_bytes))
         checkpoint();
     });
     task_runner_->RunUntilCheckpoint(checkpoint_name);
-    task_runner_->RemoveFileDescriptorWatch(sock.fd());
+    task_runner_->RemoveFileDescriptorWatch(sock.watch_handle());
     return rxbuf;
   }
 
@@ -289,7 +290,9 @@
   srv_.AddAllowedOrigin("notallowed.com");
 
   HttpCli cli(&task_runner_);
-  EXPECT_CALL(handler_, OnHttpConnectionClosed(_));
+  auto close_checkpoint = task_runner_.CreateCheckpoint("close");
+  EXPECT_CALL(handler_, OnHttpConnectionClosed(_))
+      .WillOnce(InvokeWithoutArgs(close_checkpoint));
   EXPECT_CALL(handler_, OnHttpRequest(_))
       .WillOnce(Invoke([&](const HttpRequest& req) {
         EXPECT_EQ(req.origin.ToStdString(), "http://notallowed.com");
@@ -314,6 +317,7 @@
 
   EXPECT_EQ(cli.Recv(expected_resp.size()), expected_resp);
   cli.sock.Shutdown();
+  task_runner_.RunUntilCheckpoint("close");
 }
 
 }  // namespace
diff --git a/src/base/http/sha1.cc b/src/base/http/sha1.cc
index da3f753..f3e1e5d 100644
--- a/src/base/http/sha1.cc
+++ b/src/base/http/sha1.cc
@@ -30,7 +30,7 @@
 #if defined(__GNUC__)
   return __builtin_bswap32(x);
 #elif defined(_MSC_VER)
-  return _byteswap_ulong(val);
+  return _byteswap_ulong(x);
 #else
   return (((x & 0xff000000u) >> 24) | ((x & 0x00ff0000u) >> 8) |
           ((x & 0x0000ff00u) << 8) | ((x & 0x000000ffu) << 24));
diff --git a/src/base/unix_socket_unittest.cc b/src/base/unix_socket_unittest.cc
index c838663..45c09d5 100644
--- a/src/base/unix_socket_unittest.cc
+++ b/src/base/unix_socket_unittest.cc
@@ -332,53 +332,6 @@
   tx_thread.join();
 }
 
-// Regression test for b/193234818. SO_SNDTIMEO is unreliable on most systems.
-// It doesn't guarantee that the whole send() call blocks for at most X, as the
-// kernel rearms the timeout if the send buffers frees up and allows a partial
-// send. This test reproduces the issue 100% on Mac. Unfortunately on Linux the
-// repro seem to happen only when a suspend happens in the middle.
-TEST_F(UnixSocketTest, BlockingSendTimeout) {
-  TestTaskRunner ttr;
-  UnixSocketRaw send_sock;
-  UnixSocketRaw recv_sock;
-  std::tie(send_sock, recv_sock) =
-      UnixSocketRaw::CreatePairPosix(kTestSocket.family(), SockType::kStream);
-
-  auto blocking_send_done = ttr.CreateCheckpoint("blocking_send_done");
-
-  std::thread tx_thread([&] {
-    // Fill the tx buffer in non-blocking mode.
-    send_sock.SetBlocking(false);
-    char buf[1024 * 16]{};
-    while (send_sock.Send(buf, sizeof(buf)) > 0) {
-    }
-
-    // Then do a blocking send. It should return a partial value within the tx
-    // timeout.
-    send_sock.SetBlocking(true);
-    send_sock.SetTxTimeout(10);
-    ASSERT_LT(send_sock.Send(buf, sizeof(buf)),
-              static_cast<ssize_t>(sizeof(buf)));
-    ttr.PostTask(blocking_send_done);
-  });
-
-  // This task needs to be slow enough so that doesn't unblock the send, but
-  // fast enough so that within a blocking cycle, the send re-attempts and
-  // re-arms the timeout.
-  PeriodicTask read_slowly_task(&ttr);
-  PeriodicTask::Args args;
-  args.period_ms = 1;  // Read 1 byte every ms (1 KiB/s).
-  args.task = [&] {
-    char rxbuf[1]{};
-    recv_sock.Receive(rxbuf, sizeof(rxbuf));
-  };
-  read_slowly_task.Start(args);
-
-  ttr.RunUntilCheckpoint("blocking_send_done");
-  read_slowly_task.Reset();
-  tx_thread.join();
-}
-
 // Regression test for b/76155349 . If the receiver end disconnects while the
 // sender is in the middle of a large send(), the socket should gracefully give
 // up (i.e. Shutdown()) but not crash.
@@ -939,6 +892,53 @@
   ASSERT_EQ(hdr.msg_iov, nullptr);
   ASSERT_EQ(memcmp(&send_buf[0], &recv_buf[0], send_buf.size()), 0);
 }
+
+// Regression test for b/193234818. SO_SNDTIMEO is unreliable on most systems.
+// It doesn't guarantee that the whole send() call blocks for at most X, as the
+// kernel rearms the timeout if the send buffers frees up and allows a partial
+// send. This test reproduces the issue 100% on Mac. Unfortunately on Linux the
+// repro seem to happen only when a suspend happens in the middle.
+TEST_F(UnixSocketTest, BlockingSendTimeout) {
+  TestTaskRunner ttr;
+  UnixSocketRaw send_sock;
+  UnixSocketRaw recv_sock;
+  std::tie(send_sock, recv_sock) =
+      UnixSocketRaw::CreatePairPosix(kTestSocket.family(), SockType::kStream);
+
+  auto blocking_send_done = ttr.CreateCheckpoint("blocking_send_done");
+
+  std::thread tx_thread([&] {
+    // Fill the tx buffer in non-blocking mode.
+    send_sock.SetBlocking(false);
+    char buf[1024 * 16]{};
+    while (send_sock.Send(buf, sizeof(buf)) > 0) {
+    }
+
+    // Then do a blocking send. It should return a partial value within the tx
+    // timeout.
+    send_sock.SetBlocking(true);
+    send_sock.SetTxTimeout(10);
+    ASSERT_LT(send_sock.Send(buf, sizeof(buf)),
+              static_cast<ssize_t>(sizeof(buf)));
+    ttr.PostTask(blocking_send_done);
+  });
+
+  // This task needs to be slow enough so that doesn't unblock the send, but
+  // fast enough so that within a blocking cycle, the send re-attempts and
+  // re-arms the timeout.
+  PeriodicTask read_slowly_task(&ttr);
+  PeriodicTask::Args args;
+  args.period_ms = 1;  // Read 1 byte every ms (1 KiB/s).
+  args.task = [&] {
+    char rxbuf[1]{};
+    recv_sock.Receive(rxbuf, sizeof(rxbuf));
+  };
+  read_slowly_task.Start(args);
+
+  ttr.RunUntilCheckpoint("blocking_send_done");
+  read_slowly_task.Reset();
+  tx_thread.join();
+}
 #endif  // !OS_WIN
 
 }  // namespace
diff --git a/src/trace_processor/metrics/metrics.cc b/src/trace_processor/metrics/metrics.cc
index 6926cff..6c2536e 100644
--- a/src/trace_processor/metrics/metrics.cc
+++ b/src/trace_processor/metrics/metrics.cc
@@ -620,8 +620,9 @@
   auto metric_it = std::find_if(
       ctx->metrics->begin(), ctx->metrics->end(),
       [path](const SqlMetricFile& metric) { return metric.path == path; });
-  if (metric_it == ctx->metrics->end())
-    return base::ErrStatus("RUN_METRIC: Unknown filename provided");
+  if (metric_it == ctx->metrics->end()) {
+    return base::ErrStatus("RUN_METRIC: Unknown filename provided %s", path);
+  }
   const auto& sql = metric_it->sql;
 
   std::unordered_map<std::string, std::string> substitutions;
diff --git a/src/trace_processor/metrics/sql/BUILD.gn b/src/trace_processor/metrics/sql/BUILD.gn
index 064630b..7ddbf2a 100644
--- a/src/trace_processor/metrics/sql/BUILD.gn
+++ b/src/trace_processor/metrics/sql/BUILD.gn
@@ -71,6 +71,8 @@
   "android/gpu_counter_span_view.sql",
   "android/thread_counter_span_view.sql",
   "android/unsymbolized_frames.sql",
+  "android/startup/launches_maxsdk28.sql",
+  "android/startup/launches_minsdk29.sql",
   "android/startup/launches.sql",
   "android/startup/hsc.sql",
   "chrome/actual_power_by_category.sql",
diff --git a/src/trace_processor/metrics/sql/android/startup/launches.sql b/src/trace_processor/metrics/sql/android/startup/launches.sql
index c3be64c..492bac5 100644
--- a/src/trace_processor/metrics/sql/android/startup/launches.sql
+++ b/src/trace_processor/metrics/sql/android/startup/launches.sql
@@ -12,29 +12,6 @@
 -- 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.
---
-
--- Marks the beginning of the trace and is equivalent to when the statsd launch
--- logging begins.
-DROP VIEW IF EXISTS activity_intent_received;
-CREATE VIEW activity_intent_received AS
-SELECT ts FROM slice
-WHERE name = 'MetricsLogger:launchObserverNotifyIntentStarted';
-
--- We partition the trace into spans based on posted activity intents.
--- We will refine these progressively in the next steps to only encompass
--- activity starts.
-DROP TABLE IF EXISTS activity_intent_recv_spans;
-CREATE TABLE activity_intent_recv_spans(id INT, ts BIG INT, dur BIG INT);
-
-INSERT INTO activity_intent_recv_spans
-SELECT
-  ROW_NUMBER()
-    OVER(ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS id,
-  ts,
-  LEAD(ts, 1, (SELECT end_ts FROM trace_bounds)) OVER(ORDER BY ts) - ts AS dur
-FROM activity_intent_received
-ORDER BY ts;
 
 -- The start of the launching event corresponds to the end of the AM handling
 -- the startActivity intent, whereas the end corresponds to the first frame drawn.
@@ -52,64 +29,66 @@
 WHERE s.name GLOB 'launching: *'
 AND (process.name IS NULL OR process.name = 'system_server');
 
--- Filter activity_intent_recv_spans, keeping only the ones that triggered
--- a launch.
-DROP VIEW IF EXISTS launch_partitions;
-CREATE VIEW launch_partitions AS
-SELECT * FROM activity_intent_recv_spans AS spans
-WHERE 1 = (
-  SELECT COUNT(1)
-  FROM launching_events
-  WHERE launching_events.ts BETWEEN spans.ts AND spans.ts + spans.dur);
+SELECT CREATE_FUNCTION(
+  'ANDROID_SDK_LEVEL()',
+  'INT', "
+    SELECT int_value
+    FROM metadata
+    WHERE name = 'android_sdk_version'
+  ");
 
--- Successful activity launch. The end of the 'launching' event is not related
--- to whether it actually succeeded or not.
-DROP VIEW IF EXISTS activity_intent_launch_successful;
-CREATE VIEW activity_intent_launch_successful AS
-SELECT ts FROM slice
-WHERE name = 'MetricsLogger:launchObserverNotifyActivityLaunchFinished';
-
--- All activity launches in the trace, keyed by ID.
-DROP TABLE IF EXISTS launches;
-CREATE TABLE launches(
-  ts BIG INT,
-  ts_end BIG INT,
-  dur BIG INT,
-  id INT,
-  package STRING);
-
--- Use the starting event package name. The finish event package name
--- is not reliable in the case of failed launches.
-INSERT INTO launches
-SELECT
-  lpart.ts AS ts,
-  launching_events.ts_end AS ts_end,
-  launching_events.ts_end - lpart.ts AS dur,
-  lpart.id AS id,
-  package_name AS package
-FROM launch_partitions AS lpart
-JOIN launching_events ON
-  (launching_events.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur) AND
-  (launching_events.ts_end BETWEEN lpart.ts AND lpart.ts + lpart.dur)
-WHERE (
-  SELECT COUNT(1)
-  FROM activity_intent_launch_successful AS successful
-  WHERE successful.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur
-) > 0;
+-- Note: on Q, we didn't have Android fingerprints but we *did*
+-- have ActivityMetricsLogger events so we will use this approach
+-- if we see any such events.
+SELECT CASE
+  WHEN (
+    ANDROID_SDK_LEVEL() >= 29
+    OR (
+      SELECT COUNT(1) FROM slice
+      WHERE name GLOB 'MetricsLogger:*'
+    ) > 0
+  )
+  THEN RUN_METRIC('android/startup/launches_minsdk29.sql')
+  ELSE RUN_METRIC('android/startup/launches_maxsdk28.sql')
+END;
 
 -- Maps a launch to the corresponding set of processes that handled the
 -- activity start. The vast majority of cases should be a single process.
 -- However it is possible that the process dies during the activity launch
 -- and is respawned.
 DROP TABLE IF EXISTS launch_processes;
-CREATE TABLE launch_processes(launch_id INT, upid BIG INT);
+CREATE TABLE launch_processes(launch_id INT, upid BIG INT, launch_type STRING);
 
-INSERT INTO launch_processes
-SELECT launches.id, process.upid
-FROM launches
-  LEFT JOIN package_list ON (launches.package = package_list.package_name)
-  JOIN process ON (launches.package = process.name OR process.uid = package_list.uid)
-  JOIN thread ON (process.upid = thread.upid AND process.pid = thread.tid)
-WHERE (process.start_ts IS NULL OR process.start_ts < launches.ts_end)
-AND (thread.end_ts IS NULL OR thread.end_ts > launches.ts_end)
-ORDER BY process.start_ts DESC;
+SELECT CREATE_FUNCTION(
+  'STARTUP_SLICE_COUNT(start_ts LONG, end_ts LONG, utid INT, name STRING)',
+  'INT',
+  '
+    SELECT COUNT(1)
+    FROM thread_track t
+    JOIN slice s ON s.track_id = t.id
+    WHERE
+      t.utid = $utid AND
+      s.ts >= $start_ts AND
+      s.ts < $end_ts AND
+      s.name = $name
+  '
+);
+
+INSERT INTO launch_processes(launch_id, upid, launch_type)
+SELECT
+  l.id AS launch_id,
+  p.upid,
+  CASE
+    WHEN STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'bindApplication') > 0
+      THEN 'cold'
+    WHEN STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'activityStart') > 0
+      THEN 'warm'
+    WHEN STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'activityResume') > 0
+      THEN 'hot'
+    ELSE NULL
+  END AS launch_type
+FROM launches l
+LEFT JOIN package_list ON (l.package = package_list.package_name)
+JOIN process p ON (l.package = p.name OR p.uid = package_list.uid)
+JOIN thread t ON (p.upid = t.upid AND t.is_main_thread)
+WHERE launch_type IS NOT NULL;
diff --git a/src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql b/src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql
new file mode 100644
index 0000000..8753da1
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql
@@ -0,0 +1,36 @@
+--
+-- Copyright 2021 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 at
+--
+--     https://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.
+--
+
+-- All activity launches in the trace, keyed by ID.
+DROP TABLE IF EXISTS launches;
+CREATE TABLE launches(
+  id INTEGER PRIMARY KEY,
+  ts BIG INT,
+  ts_end BIG INT,
+  dur BIG INT,
+  package STRING
+);
+
+-- Cold/warm starts emitted launching slices on API level 28-.
+INSERT INTO launches(ts, ts_end, dur, package)
+SELECT
+  launching_events.ts AS ts,
+  launching_events.ts_end AS ts_end,
+  launching_events.ts_end - launching_events.ts AS dur,
+  package_name AS package
+FROM launching_events;
+
+-- TODO(lalitm): add handling of hot starts using frame timings.
diff --git a/src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql b/src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql
new file mode 100644
index 0000000..1f4c0f7
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql
@@ -0,0 +1,82 @@
+--
+-- Copyright 2021 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 at
+--
+--     https://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.
+--
+
+-- Marks the beginning of the trace and is equivalent to when the statsd launch
+-- logging begins.
+DROP VIEW IF EXISTS activity_intent_received;
+CREATE VIEW activity_intent_received AS
+SELECT ts FROM slice
+WHERE name = 'MetricsLogger:launchObserverNotifyIntentStarted';
+
+-- We partition the trace into spans based on posted activity intents.
+-- We will refine these progressively in the next steps to only encompass
+-- activity starts.
+DROP TABLE IF EXISTS activity_intent_recv_spans;
+CREATE TABLE activity_intent_recv_spans(id INT, ts BIG INT, dur BIG INT);
+
+INSERT INTO activity_intent_recv_spans
+SELECT
+  ROW_NUMBER()
+    OVER(ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS id,
+  ts,
+  LEAD(ts, 1, (SELECT end_ts FROM trace_bounds)) OVER(ORDER BY ts) - ts AS dur
+FROM activity_intent_received
+ORDER BY ts;
+
+-- Filter activity_intent_recv_spans, keeping only the ones that triggered
+-- a launch.
+DROP VIEW IF EXISTS launch_partitions;
+CREATE VIEW launch_partitions AS
+SELECT * FROM activity_intent_recv_spans AS spans
+WHERE 1 = (
+  SELECT COUNT(1)
+  FROM launching_events
+  WHERE launching_events.ts BETWEEN spans.ts AND spans.ts + spans.dur);
+
+-- Successful activity launch. The end of the 'launching' event is not related
+-- to whether it actually succeeded or not.
+DROP VIEW IF EXISTS activity_intent_launch_successful;
+CREATE VIEW activity_intent_launch_successful AS
+SELECT ts FROM slice
+WHERE name = 'MetricsLogger:launchObserverNotifyActivityLaunchFinished';
+
+-- All activity launches in the trace, keyed by ID.
+DROP TABLE IF EXISTS launches;
+CREATE TABLE launches(
+  ts BIG INT,
+  ts_end BIG INT,
+  dur BIG INT,
+  id INT,
+  package STRING);
+
+-- Use the starting event package name. The finish event package name
+-- is not reliable in the case of failed launches.
+INSERT INTO launches
+SELECT
+  lpart.ts AS ts,
+  launching_events.ts_end AS ts_end,
+  launching_events.ts_end - lpart.ts AS dur,
+  lpart.id AS id,
+  package_name AS package
+FROM launch_partitions AS lpart
+JOIN launching_events ON
+  (launching_events.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur) AND
+  (launching_events.ts_end BETWEEN lpart.ts AND lpart.ts + lpart.dur)
+WHERE (
+  SELECT COUNT(1)
+  FROM activity_intent_launch_successful AS successful
+  WHERE successful.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur
+) > 0;
diff --git a/src/trace_processor/timestamped_trace_piece.h b/src/trace_processor/timestamped_trace_piece.h
index ca61546..1b1a0f6 100644
--- a/src/trace_processor/timestamped_trace_piece.h
+++ b/src/trace_processor/timestamped_trace_piece.h
@@ -77,9 +77,19 @@
   std::array<double, kMaxNumExtraCounters> extra_counter_values = {};
 };
 
+// On Windows std::aligned_storage was broken before VS 2017 15.8 and the
+// compiler (even clang-cl) requires -D_ENABLE_EXTENDED_ALIGNED_STORAGE. Given
+// the alignment here is purely a performance enhancment with no other
+// functional requirement, disable it on Win.
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#define PERFETTO_TTS_ALIGNMENT alignas(64)
+#else
+#define PERFETTO_TTS_ALIGNMENT
+#endif
+
 // A TimestampedTracePiece is (usually a reference to) a piece of a trace that
 // is sorted by TraceSorter.
-struct alignas(64) TimestampedTracePiece {
+struct PERFETTO_TTS_ALIGNMENT TimestampedTracePiece {
   enum class Type {
     kInvalid = 0,
     kFtraceEvent,
diff --git a/src/tracing/test/api_test_support.cc b/src/tracing/test/api_test_support.cc
index 0142d28..a03135d 100644
--- a/src/tracing/test/api_test_support.cc
+++ b/src/tracing/test/api_test_support.cc
@@ -27,6 +27,10 @@
 #include "test/test_helper.h"
 #endif
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#endif
+
 namespace perfetto {
 namespace test {
 
diff --git a/test/trace_processor/startup/android_startup.out b/test/trace_processor/startup/android_startup.out
index a7dd383..5d323e4 100644
--- a/test/trace_processor/startup/android_startup.out
+++ b/test/trace_processor/startup/android_startup.out
@@ -18,12 +18,12 @@
       }
     }
     zygote_new_process: false
-    activity_hosting_process_count: 2
+    activity_hosting_process_count: 1
     to_first_frame {
       dur_ns: 108
       main_thread_by_task_state {
-        running_dur_ns: 41
-        runnable_dur_ns: 129
+        running_dur_ns: 10
+        runnable_dur_ns: 80
         uninterruptible_sleep_dur_ns: 0
         interruptible_sleep_dur_ns: 10
       }
@@ -34,6 +34,10 @@
       }
       mcycles_by_core_type {
       }
+      time_activity_start {
+        dur_ns: 2
+        dur_ms: 2e-06
+      }
       dur_ms: 0.000108
     }
     report_fully_drawn {
diff --git a/test/trace_processor/startup/android_startup.py b/test/trace_processor/startup/android_startup.py
index 494849c..c5f0e37 100644
--- a/test/trace_processor/startup/android_startup.py
+++ b/test/trace_processor/startup/android_startup.py
@@ -22,7 +22,7 @@
 trace.add_process(1, 0, 'init')
 trace.add_process(2, 1, 'system_server')
 trace.add_process(3, 1, 'com.google.android.calendar', 10001)
-trace.add_process(4, 1, 'com.google.android.calendar')
+trace.add_process(4, 3, 'com.google.android.calendar', 10001)
 
 trace.add_package_list(
     ts=1, name='com.google.android.calendar', uid=10001, version_code=123)
@@ -42,6 +42,13 @@
     ts=110, tid=2, pid=2, buf='launching: com.google.android.calendar')
 
 trace.add_sched(ts=110, prev_pid=0, next_pid=3)
+
+# As the process already existed before intent started, this is a
+# warm/hot start (we choose warm). Therefore, emit an activityStart
+# slice.
+trace.add_atrace_begin(ts=115, tid=3, pid=3, buf='activityStart')
+trace.add_atrace_end(ts=117, tid=3, pid=3)
+
 # P1: 10ns running
 trace.add_sched(ts=120, prev_pid=3, next_pid=0, prev_state='S')
 # P1: 10ns sleep
diff --git a/test/trace_processor/startup/android_startup_attribution.out b/test/trace_processor/startup/android_startup_attribution.out
index 87b1a30..3c74145 100644
--- a/test/trace_processor/startup/android_startup_attribution.out
+++ b/test/trace_processor/startup/android_startup_attribution.out
@@ -17,6 +17,10 @@
         dur_ns: 2
         dur_ms: 2e-06
       }
+      time_activity_resume {
+        dur_ns: 5
+        dur_ms: 5e-06
+      }
       dur_ms: 999.9999
       time_dex_open {
         dur_ns: 20
diff --git a/test/trace_processor/startup/android_startup_attribution.py b/test/trace_processor/startup/android_startup_attribution.py
index cf3ab8e..2f8a4be 100644
--- a/test/trace_processor/startup/android_startup_attribution.py
+++ b/test/trace_processor/startup/android_startup_attribution.py
@@ -18,7 +18,7 @@
 import synth_common
 
 APP_PID = 3
-APP_TID = 1
+APP_TID = APP_PID
 SECOND_APP_TID = 3
 JIT_TID = 4
 GC_TID = 5
@@ -65,6 +65,10 @@
     tid=SYSTEM_SERVER_TID,
     buf='launching: com.some.app')
 
+# Emulate a hot start (and therefore that we only see activityResume).
+trace.add_atrace_begin(ts=125, tid=APP_TID, pid=APP_PID, buf='activityResume')
+trace.add_atrace_end(ts=130, tid=APP_TID, pid=APP_PID)
+
 # OpenDex slices within the startup.
 trace.add_atrace_begin(
     ts=150, pid=APP_PID, tid=APP_TID, buf='OpenDexFilesFromOat(something)')
diff --git a/test/trace_processor/startup/android_startup_process_track.out b/test/trace_processor/startup/android_startup_process_track.out
index 357603d..7a8ae0f 100644
--- a/test/trace_processor/startup/android_startup_process_track.out
+++ b/test/trace_processor/startup/android_startup_process_track.out
@@ -8,7 +8,7 @@
     }
     zygote_new_process: false
     to_first_frame {
-      dur_ns: 4
+      dur_ns: 5
       main_thread_by_task_state {
         running_dur_ns: 0
         runnable_dur_ns: 0
@@ -22,11 +22,19 @@
       }
       mcycles_by_core_type {
       }
-      dur_ms: 4e-06
+      time_bind_application {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      dur_ms: 5e-06
+      to_bind_application {
+        dur_ns: 3
+        dur_ms: 3e-06
+      }
     }
     event_timestamps {
       intent_received: 100
-      first_frame: 104
+      first_frame: 105
     }
     activity_hosting_process_count: 1
   }
@@ -39,7 +47,7 @@
     }
     zygote_new_process: false
     to_first_frame {
-      dur_ns: 4
+      dur_ns: 5
       main_thread_by_task_state {
         running_dur_ns: 0
         runnable_dur_ns: 0
@@ -53,11 +61,19 @@
       }
       mcycles_by_core_type {
       }
-      dur_ms: 4e-06
+      time_bind_application {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      dur_ms: 5e-06
+      to_bind_application {
+        dur_ns: 3
+        dur_ms: 3e-06
+      }
     }
     event_timestamps {
       intent_received: 200
-      first_frame: 204
+      first_frame: 205
     }
     activity_hosting_process_count: 1
   }
diff --git a/test/trace_processor/startup/android_startup_process_track.py b/test/trace_processor/startup/android_startup_process_track.py
index a9696ad..07fcf92 100644
--- a/test/trace_processor/startup/android_startup_process_track.py
+++ b/test/trace_processor/startup/android_startup_process_track.py
@@ -34,10 +34,12 @@
       new_tid=pid,
       new_comm='com.google.android.calendar',
       flags=0)
+  trace.add_atrace_begin(ts=ts + 3, tid=pid, pid=pid, buf='bindApplication')
+  trace.add_atrace_end(ts=ts + 4, tid=pid, pid=pid)
   trace.add_atrace_async_end(
-      ts=ts + 4, tid=2, pid=2, buf='launching: com.google.android.calendar')
+      ts=ts + 5, tid=2, pid=2, buf='launching: com.google.android.calendar')
   trace.add_atrace_begin(
-      ts=ts + 5,
+      ts=ts + 6,
       tid=2,
       pid=2,
       buf='MetricsLogger:launchObserverNotifyActivityLaunchFinished')
diff --git a/tools/gen_amalgamated_sql_metrics.py b/tools/gen_amalgamated_sql_metrics.py
index eed5d8e..16f6aac 100755
--- a/tools/gen_amalgamated_sql_metrics.py
+++ b/tools/gen_amalgamated_sql_metrics.py
@@ -85,7 +85,7 @@
     with open(file_name, 'r') as f:
       relpath = os.path.relpath(file_name, root_path)
       sql_outputs[relpath] = "".join(
-          x for x in f.readlines() if not x.startswith('--'))
+          x.lstrip() for x in f.readlines() if not x.lstrip().startswith('--'))
 
   with open(args.cpp_out, 'w+') as output:
     output.write(REPLACEMENT_HEADER)
diff --git a/tools/test_data b/tools/test_data
index cf8ae14..7dd8975 100755
--- a/tools/test_data
+++ b/tools/test_data
@@ -141,7 +141,7 @@
     subprocess.check_call(cmd)
     with open(fs.path + SUFFIX + '.swp', 'w') as f:
       f.write(fs.actual_digest)
-    os.rename(fs.path + SUFFIX + '.swp', fs.path + SUFFIX)
+    os.replace(fs.path + SUFFIX + '.swp', fs.path + SUFFIX)
     return fs
 
   map_concurrently(upload_one_file, files_to_upload)
@@ -194,7 +194,7 @@
     if digest != fs.expected_digest:
       raise Exception('Mismatching digest for %s. expected=%s, actual=%s' %
                       (uri, fs.expected_digest, digest))
-    os.rename(tmp_path, fs.path)
+    os.replace(tmp_path, fs.path)
     return fs
 
   map_concurrently(download_one_file, files_to_download)