Merge "trace_processor: support drm-related events"
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index 2995192..5ef78a2 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -772,6 +772,8 @@
     "benchmark/src/json_reporter.cc",
     "benchmark/src/log.h",
     "benchmark/src/mutex.h",
+    "benchmark/src/perf_counters.cc",
+    "benchmark/src/perf_counters.h",
     "benchmark/src/re.h",
     "benchmark/src/reporter.cc",
     "benchmark/src/sleep.cc",
diff --git a/protos/perfetto/metrics/android/network_metric.proto b/protos/perfetto/metrics/android/network_metric.proto
index e09c571..5f10d12 100644
--- a/protos/perfetto/metrics/android/network_metric.proto
+++ b/protos/perfetto/metrics/android/network_metric.proto
@@ -92,11 +92,45 @@
     optional int64 avg_freq_khz = 5;
   }
 
+  message NetTxActionStatistic {
+    // SoftIrq NET_TX action count.
+    optional int64 count = 1;
+
+    // SoftIrq NET_TX action was running in millisecond.
+    optional double runtime_ms = 2;
+
+    // SoftIrq NET_TX action average running time.
+    optional double avg_runtime_ms = 3;
+
+    // CPU megacycles (i.e. cycles divided by 1e6).
+    optional int64 mcycles = 4;
+
+    // Average weighted CPU frequency by the time the NET_TX Action
+    // running at each frequency.
+    optional int64 avg_freq_khz = 5;
+  }
+
+  message IpiActionStatistic {
+    // SoftIrq IPI action count.
+    optional int64 count = 1;
+
+    // SoftIrq IPI action was running in millisecond.
+    optional double runtime_ms = 2;
+
+    // SoftIrq IPI action average running time.
+    optional double avg_runtime_ms = 3;
+  }
+
   message CoreNetRxActionStatistic {
     optional uint32 id = 1;
     optional NetRxActionStatistic net_rx_action_statistic = 2;
   }
 
+  message CoreNetTxActionStatistic {
+    optional uint32 id = 1;
+    optional NetTxActionStatistic net_tx_action_statistic = 2;
+  }
+
   message NetRxAction {
     // Total NET_RX action statistics.
     optional NetRxActionStatistic total = 1;
@@ -108,6 +142,19 @@
     optional double avg_interstack_latency_ms = 3;
   }
 
+  message NetTxAction {
+    // Total NET_TX action statistics.
+    optional NetTxActionStatistic total = 1;
+
+    // Per core NET_TX action statistics.
+    repeated CoreNetTxActionStatistic core = 2;
+  }
+
+  message IpiAction {
+    // Total IPI action statistics.
+    optional IpiActionStatistic total = 1;
+  }
+
   // Network device metrics.
   repeated NetDevice net_devices = 1;
 
@@ -120,4 +167,10 @@
   // Kfree Skb rate (i.e. kfree_skb count divided by the packet count from all
   // net devices).
   optional double kfree_skb_rate = 4;
+
+  // SoftIrq NET_TX action metrics.
+  optional NetTxAction net_tx_action = 5;
+
+  // SoftIrq IPI action metrics.
+  optional IpiAction ipi_action = 6;
 }
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index 97ee857..e7c1262 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -953,11 +953,45 @@
     optional int64 avg_freq_khz = 5;
   }
 
+  message NetTxActionStatistic {
+    // SoftIrq NET_TX action count.
+    optional int64 count = 1;
+
+    // SoftIrq NET_TX action was running in millisecond.
+    optional double runtime_ms = 2;
+
+    // SoftIrq NET_TX action average running time.
+    optional double avg_runtime_ms = 3;
+
+    // CPU megacycles (i.e. cycles divided by 1e6).
+    optional int64 mcycles = 4;
+
+    // Average weighted CPU frequency by the time the NET_TX Action
+    // running at each frequency.
+    optional int64 avg_freq_khz = 5;
+  }
+
+  message IpiActionStatistic {
+    // SoftIrq IPI action count.
+    optional int64 count = 1;
+
+    // SoftIrq IPI action was running in millisecond.
+    optional double runtime_ms = 2;
+
+    // SoftIrq IPI action average running time.
+    optional double avg_runtime_ms = 3;
+  }
+
   message CoreNetRxActionStatistic {
     optional uint32 id = 1;
     optional NetRxActionStatistic net_rx_action_statistic = 2;
   }
 
+  message CoreNetTxActionStatistic {
+    optional uint32 id = 1;
+    optional NetTxActionStatistic net_tx_action_statistic = 2;
+  }
+
   message NetRxAction {
     // Total NET_RX action statistics.
     optional NetRxActionStatistic total = 1;
@@ -969,6 +1003,19 @@
     optional double avg_interstack_latency_ms = 3;
   }
 
+  message NetTxAction {
+    // Total NET_TX action statistics.
+    optional NetTxActionStatistic total = 1;
+
+    // Per core NET_TX action statistics.
+    repeated CoreNetTxActionStatistic core = 2;
+  }
+
+  message IpiAction {
+    // Total IPI action statistics.
+    optional IpiActionStatistic total = 1;
+  }
+
   // Network device metrics.
   repeated NetDevice net_devices = 1;
 
@@ -981,6 +1028,12 @@
   // Kfree Skb rate (i.e. kfree_skb count divided by the packet count from all
   // net devices).
   optional double kfree_skb_rate = 4;
+
+  // SoftIrq NET_TX action metrics.
+  optional NetTxAction net_tx_action = 5;
+
+  // SoftIrq IPI action metrics.
+  optional IpiAction ipi_action = 6;
 }
 
 // End of protos/perfetto/metrics/android/network_metric.proto
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index a91d7a9..9c2407b 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/src/trace_processor/containers/nullable_vector.h b/src/trace_processor/containers/nullable_vector.h
index 2fed430..537e822 100644
--- a/src/trace_processor/containers/nullable_vector.h
+++ b/src/trace_processor/containers/nullable_vector.h
@@ -152,9 +152,8 @@
       } else {
         valid_.Insert(idx);
 
-        opt_row = valid_.RowOf(idx);
-        PERFETTO_DCHECK(opt_row);
-        data_.insert(data_.begin() + static_cast<ptrdiff_t>(*opt_row), val);
+        uint32_t inserted_row = *valid_.RowOf(idx);
+        data_.insert(data_.begin() + static_cast<ptrdiff_t>(inserted_row), val);
       }
     }
   }
diff --git a/src/trace_processor/importers/proto/android_probes_module.cc b/src/trace_processor/importers/proto/android_probes_module.cc
index 559c78e..141d176 100644
--- a/src/trace_processor/importers/proto/android_probes_module.cc
+++ b/src/trace_processor/importers/proto/android_probes_module.cc
@@ -126,8 +126,10 @@
       writer.AppendStringView(desc.rail_name());
       writer.AppendStringView("_uws");
     }
-    AndroidProbesTracker::GetOrCreate(context_)->SetPowerRailName(
-        desc.index(), context_->storage->InternString(writer.GetStringView()));
+    AndroidProbesTracker::GetOrCreate(context_)->SetPowerRailNames(
+        desc.index(),
+        {context_->storage->InternString(desc.rail_name()),
+         context_->storage->InternString(writer.GetStringView())});
   }
 
   // For each energy data message, turn it into its own trace packet
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index 882191d..7eadba6 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -104,10 +104,15 @@
     PERFETTO_DCHECK(desc.has_timestamp_ms());
     PERFETTO_DCHECK(ts / 1000000 == static_cast<int64_t>(desc.timestamp_ms()));
 
-    TrackId track =
-        context_->track_tracker->InternGlobalCounterTrack(*opt_rail_name);
-    context_->event_tracker->PushCounter(ts, static_cast<double>(desc.energy()),
-                                         track);
+    TrackId track = context_->track_tracker->InternGlobalCounterTrack(
+        opt_rail_name->friendly);
+    context_->event_tracker->PushCounter(
+        ts, static_cast<double>(desc.energy()), track,
+        [this, opt_rail_name](ArgsTracker::BoundInserter* args_table) {
+          args_table->AddArg(
+              context_->storage->InternString("power_rails.names.raw"),
+              Variadic::String(opt_rail_name->raw));
+        });
   } else {
     context_->storage->IncrementStats(stats::power_rail_unknown_index);
   }
diff --git a/src/trace_processor/importers/proto/android_probes_tracker.h b/src/trace_processor/importers/proto/android_probes_tracker.h
index fa0ffe2..c9a6704 100644
--- a/src/trace_processor/importers/proto/android_probes_tracker.h
+++ b/src/trace_processor/importers/proto/android_probes_tracker.h
@@ -51,21 +51,27 @@
     seen_packages_.emplace(std::move(package_name));
   }
 
-  base::Optional<StringId> GetPowerRailName(uint32_t index) {
-    if (index >= power_rails_strs_id_.size())
+  struct PowerRailsNames {
+    StringId raw;
+    StringId friendly;
+  };
+
+  base::Optional<PowerRailsNames> GetPowerRailName(uint32_t index) {
+    if (index >= power_rails_names_str_id_.size())
       return base::nullopt;
-    return power_rails_strs_id_[index];
+    return power_rails_names_str_id_[index];
   }
 
-  void SetPowerRailName(uint32_t index, StringId name) {
-    if (power_rails_strs_id_.size() <= index)
-      power_rails_strs_id_.resize(index + 1);
-    power_rails_strs_id_[index] = name;
+  void SetPowerRailNames(uint32_t index, PowerRailsNames names) {
+    if (power_rails_names_str_id_.size() <= index)
+      power_rails_names_str_id_.resize(index + 1);
+    power_rails_names_str_id_[index] = names;
   }
 
  private:
   std::set<std::string> seen_packages_;
-  std::vector<StringId> power_rails_strs_id_;
+  std::vector<PowerRailsNames> power_rails_names_str_id_;
+  std::vector<StringId> power_rails_raw_names_str_id_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/metrics/sql/android/android_netperf.sql b/src/trace_processor/metrics/sql/android/android_netperf.sql
index eb773ab..2f67d67 100644
--- a/src/trace_processor/metrics/sql/android/android_netperf.sql
+++ b/src/trace_processor/metrics/sql/android/android_netperf.sql
@@ -209,6 +209,28 @@
     ON s.track_id = t.id
   WHERE s.name = "NET_RX";
 
+DROP VIEW IF EXISTS net_tx_actions;
+CREATE VIEW net_tx_actions AS
+  SELECT
+    s.ts,
+    s.dur,
+    CAST(SUBSTR(t.name, 13, 1) AS int) AS cpu
+  FROM slice s
+  LEFT JOIN track t
+    ON s.track_id = t.id
+  WHERE s.name = "NET_TX";
+
+DROP VIEW IF EXISTS ipi_actions;
+CREATE VIEW ipi_actions AS
+  SELECT
+    s.ts,
+    s.dur,
+    CAST(SUBSTR(t.name, 13, 1) AS int) AS cpu
+  FROM slice s
+  LEFT JOIN track t
+    ON s.track_id = t.id
+  WHERE s.name = "IRQ (IPI)";
+
 DROP VIEW IF EXISTS cpu_freq_view;
 CREATE VIEW cpu_freq_view AS
 SELECT
@@ -225,6 +247,10 @@
 CREATE VIRTUAL TABLE cpu_freq_net_rx_action_per_core
 USING SPAN_LEFT_JOIN(net_rx_actions PARTITIONED cpu, cpu_freq_view PARTITIONED cpu);
 
+DROP TABLE IF EXISTS cpu_freq_net_tx_action_per_core;
+CREATE VIRTUAL TABLE cpu_freq_net_tx_action_per_core
+USING SPAN_LEFT_JOIN(net_tx_actions PARTITIONED cpu, cpu_freq_view PARTITIONED cpu);
+
 DROP VIEW IF EXISTS total_net_rx_action_statistic;
 CREATE VIEW total_net_rx_action_statistic AS
   SELECT
@@ -234,12 +260,34 @@
     (SELECT COUNT(1) FROM rx_packets) AS total_packet
   FROM net_rx_actions;
 
-DROP VIEW IF EXISTS activated_cores;
-CREATE VIEW activated_cores AS
+DROP VIEW IF EXISTS total_net_tx_action_statistic;
+CREATE VIEW total_net_tx_action_statistic AS
+  SELECT
+    COUNT(1) AS times,
+    SUM(dur) AS runtime,
+    AVG(dur) AS avg_runtime
+  FROM net_tx_actions;
+
+DROP VIEW IF EXISTS total_ipi_action_statistic;
+CREATE VIEW total_ipi_action_statistic AS
+  SELECT
+    COUNT(1) AS times,
+    SUM(dur) AS runtime,
+    AVG(dur) AS avg_runtime
+  FROM ipi_actions;
+
+DROP VIEW IF EXISTS activated_cores_net_rx;
+CREATE VIEW activated_cores_net_rx AS
  SELECT
    DISTINCT cpu
  FROM net_rx_actions;
 
+DROP VIEW IF EXISTS activated_cores_net_tx;
+CREATE VIEW activated_cores_net_tx AS
+ SELECT
+   DISTINCT cpu
+ FROM net_tx_actions;
+
 DROP VIEW IF EXISTS per_core_net_rx_action_statistic;
 CREATE VIEW per_core_net_rx_action_statistic AS
   SELECT
@@ -253,7 +301,22 @@
         'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_rx_action_per_core AS cc WHERE cc.cpu = ac.cpu)
       )
     ) AS proto
-  FROM activated_cores AS ac;
+  FROM activated_cores_net_rx AS ac;
+
+DROP VIEW IF EXISTS per_core_net_tx_action_statistic;
+CREATE VIEW per_core_net_tx_action_statistic AS
+  SELECT
+    AndroidNetworkMetric_CoreNetTxActionStatistic(
+      'id', cpu,
+      'net_tx_action_statistic', AndroidNetworkMetric_NetTxActionStatistic(
+        'count', (SELECT COUNT(1) FROM net_tx_actions AS na WHERE na.cpu = ac.cpu),
+        'runtime_ms',  (SELECT SUM(dur)/1e6 FROM net_tx_actions AS na WHERE na.cpu = ac.cpu),
+        'avg_runtime_ms', (SELECT AVG(dur)/1e6 FROM net_tx_actions AS na WHERE na.cpu = ac.cpu),
+        'avg_freq_khz', (SELECT SUM(dur * freq_khz) / SUM(dur) FROM cpu_freq_net_tx_action_per_core AS cc WHERE cc.cpu = ac.cpu),
+        'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_tx_action_per_core AS cc WHERE cc.cpu = ac.cpu)
+      )
+    ) AS proto
+  FROM activated_cores_net_tx AS ac;
 
 DROP VIEW IF EXISTS android_netperf_output;
 CREATE VIEW android_netperf_output AS
@@ -291,6 +354,27 @@
       SELECT
         cnt * 100.0 / ((SELECT count(1) FROM rx_packets) + (SELECT count(1) FROM tx_packets))
       FROM kfree_skb_count
+    ),
+    'net_tx_action', AndroidNetworkMetric_NetTxAction(
+       'total', AndroidNetworkMetric_NetTxActionStatistic(
+         'count', (SELECT times FROM total_net_tx_action_statistic),
+         'runtime_ms', (SELECT runtime/1e6 FROM total_net_tx_action_statistic),
+         'avg_runtime_ms', (SELECT avg_runtime/1e6 FROM total_net_tx_action_statistic),
+         'avg_freq_khz', (SELECT SUM(dur * freq_khz) / SUM(dur) FROM cpu_freq_net_tx_action_per_core),
+         'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_tx_action_per_core)
+       ),
+       'core', (
+         SELECT
+           RepeatedField(proto)
+         FROM per_core_net_tx_action_statistic
+       )
+    ),
+    'ipi_action', AndroidNetworkMetric_IpiAction(
+       'total', AndroidNetworkMetric_IpiActionStatistic(
+         'count', (SELECT times FROM total_ipi_action_statistic),
+         'runtime_ms', (SELECT runtime/1e6 FROM total_ipi_action_statistic),
+         'avg_runtime_ms', (SELECT avg_runtime/1e6 FROM total_ipi_action_statistic)
+       )
     )
   );
 
diff --git a/src/trace_processor/sqlite/create_view_function.cc b/src/trace_processor/sqlite/create_view_function.cc
index 211251a..12b4915 100644
--- a/src/trace_processor/sqlite/create_view_function.cc
+++ b/src/trace_processor/sqlite/create_view_function.cc
@@ -186,12 +186,14 @@
       return SQLITE_ERROR;
     }
 
-    SqlValue::Type type =
-        sqlite_utils::SqliteTypeToSqlValueType(sqlite3_value_type(argv[i]));
-    if (type != table_->prototype_.arguments[col_to_arg_idx(cs.column)].type) {
+    const auto& arg = table_->prototype_.arguments[col_to_arg_idx(cs.column)];
+    SqlValue::Type expected_type = arg.type;
+    base::Status status = TypeCheckSqliteValue(argv[i], expected_type);
+    if (!status.ok()) {
       table_->SetErrorMessage(
-          sqlite3_mprintf("%s: type of input argument does not match",
-                          table_->prototype_.function_name.c_str()));
+          sqlite3_mprintf("%s: argument %s (index %u) %s",
+                          table_->prototype_.function_name.c_str(),
+                          arg.name.c_str(), i, status.c_message()));
       return SQLITE_ERROR;
     }
 
@@ -218,7 +220,9 @@
   stmt.reset(stmt_);
   if (ret != SQLITE_OK) {
     table_->SetErrorMessage(sqlite3_mprintf(
-        "%s: SQLite error when preparing statement %s",
+        "%s: Failed to prepare SQL statement for function. "
+        "Check the SQL defintion this function for syntax errors. "
+        "(SQLite error: %s).",
         table_->prototype_.function_name.c_str(), sqlite3_errmsg(table_->db_)));
     return SQLITE_ERROR;
   }
@@ -324,7 +328,7 @@
   RETURN_IF_ERROR(ParseFunctionName(prototype_str, function_name));
 
   base::StackString<1024> sql(
-      "CREATE OR REPLACE VIRTUAL TABLE %s USING "
+      "CREATE VIRTUAL TABLE IF NOT EXISTS %s USING "
       "INTERNAL_VIEW_FUNCTION_IMPL('%s', '%s', '%s');",
       function_name.ToStdString().c_str(), prototype_str, return_prototype_str,
       sql_defn_str);
diff --git a/src/traced/probes/ftrace/proto_translation_table_unittest.cc b/src/traced/probes/ftrace/proto_translation_table_unittest.cc
index 2748ac9..a157fad 100644
--- a/src/traced/probes/ftrace/proto_translation_table_unittest.cc
+++ b/src/traced/probes/ftrace/proto_translation_table_unittest.cc
@@ -379,7 +379,7 @@
   std::vector<Event> events;
 
   {
-    Event event;
+    Event event{};
     event.name = "foo";
     event.group = "group_one";
     event.ftrace_event_id = 1;
@@ -387,7 +387,7 @@
   }
 
   {
-    Event event;
+    Event event{};
     event.name = "bar";
     event.group = "group_one";
     event.ftrace_event_id = 2;
@@ -395,7 +395,7 @@
   }
 
   {
-    Event event;
+    Event event{};
     event.name = "baz";
     event.group = "group_two";
     event.ftrace_event_id = 100;
diff --git a/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256 b/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
index 1c1134b..035f335 100644
--- a/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
@@ -1 +1 @@
-582c5550a14de74aabcd2b0342f8295b040febc4cf6bcb4e9fb6cb155e9c8d4d
\ No newline at end of file
+e8ffe0046ea98d27b4216c8d1ff64c665392c90ab3d90e2db99069ae54d12707
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256 b/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256
index ce4aadf..d7c7cb9 100644
--- a/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256
@@ -1 +1 @@
-72e9695470c9067005bca54fcdee310feaeed23d58d6bf4c2e575748a992027e
\ No newline at end of file
+14e436dda252119dbe75bbe7233e8b62ade69e27bbad311181927c097adb86d0
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256
index 3c99db9..b5f3a52 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256
@@ -1 +1 @@
-9425ea9f775ab88ebd9d0f47946c0835191a7d4579da40d04f20c9db9d288e95
\ No newline at end of file
+77ead2d35348d988d3938b88fc66f0731f494fb77f2165d3fbddca7f37df97f6
\ No newline at end of file
diff --git a/test/trace_processor/network/netperf_metric.out b/test/trace_processor/network/netperf_metric.out
index cbb642e..14c089c 100644
--- a/test/trace_processor/network/netperf_metric.out
+++ b/test/trace_processor/network/netperf_metric.out
@@ -144,4 +144,40 @@
   }
   retransmission_rate: 20.0
   kfree_skb_rate: 10.0
+  net_tx_action {
+    total {
+      count: 2
+      runtime_ms: 0.04
+      avg_runtime_ms: 0.02
+      mcycles: 0
+      avg_freq_khz: 2000000
+    }
+    core {
+      id: 0
+      net_tx_action_statistic {
+        count: 1
+        runtime_ms: 0.02
+        avg_runtime_ms: 0.02
+        mcycles: 0
+        avg_freq_khz: 2000000
+      }
+    }
+    core {
+      id: 1
+      net_tx_action_statistic {
+        count: 1
+        runtime_ms: 0.02
+        avg_runtime_ms: 0.02
+        mcycles: 0
+        avg_freq_khz: 2000000
+      }
+    }
+  }
+  ipi_action {
+    total {
+      count: 2
+      runtime_ms: 0.02
+      avg_runtime_ms: 0.01
+    }
+  }
 }
diff --git a/test/trace_processor/network/netperf_metric.textproto b/test/trace_processor/network/netperf_metric.textproto
index 60d196e..818962d 100644
--- a/test/trace_processor/network/netperf_metric.textproto
+++ b/test/trace_processor/network/netperf_metric.textproto
@@ -88,6 +88,36 @@
         vec: 3
       }
     }
+    event {
+      timestamp: 100505000
+      pid: 200
+      irq_handler_entry {
+        irq: 1
+        name: "IPI"
+      }
+    }
+    event {
+      timestamp: 100515000
+      pid: 200
+      irq_handler_exit {
+        irq: 1
+        ret: 1
+      }
+    }
+    event {
+      timestamp: 100520000
+      pid: 200
+      softirq_entry {
+        vec: 2
+      }
+    }
+    event {
+      timestamp: 100540000
+      pid: 200
+      softirq_exit {
+        vec: 2
+      }
+    }
   }
 }
 
@@ -163,11 +193,41 @@
       }
     }
     event {
-     timestamp: 120500000
-     pid: 300
-     softirq_exit {
-       vec: 3
-     }
+      timestamp: 120500000
+      pid: 300
+      softirq_exit {
+        vec: 3
+      }
+    }
+    event {
+      timestamp: 120505000
+      pid: 200
+      irq_handler_entry {
+        irq: 1
+        name: "IPI"
+      }
+    }
+    event {
+      timestamp: 120515000
+      pid: 200
+      irq_handler_exit {
+        irq: 1
+        ret: 1
+      }
+    }
+    event {
+      timestamp: 120520000
+      pid: 200
+      softirq_entry {
+        vec: 2
+      }
+    }
+    event {
+      timestamp: 120540000
+      pid: 200
+      softirq_exit {
+        vec: 2
+      }
     }
   }
 }
@@ -237,11 +297,11 @@
       }
     }
     event {
-     timestamp: 140500000
-     pid: 400
-     softirq_exit {
-       vec: 3
-     }
+      timestamp: 140500000
+      pid: 400
+      softirq_exit {
+        vec: 3
+      }
     }
   }
 }
@@ -265,19 +325,19 @@
       }
     }
     event {
-     timestamp: 140000000
-     pid: 200
-     cpu_frequency {
-       cpu_id: 1
-       state: 1000000
-     }
+      timestamp: 140000000
+      pid: 200
+      cpu_frequency {
+        cpu_id: 1
+        state: 1000000
+      }
     }
     event {
-     timestamp: 140000000
-     pid: 500
-     softirq_entry {
-       vec: 3
-     }
+      timestamp: 140000000
+      pid: 500
+      softirq_entry {
+        vec: 3
+      }
     }
     event {
       timestamp: 140000000
@@ -296,11 +356,11 @@
       }
     }
     event {
-     timestamp: 140500000
-     pid: 500
-     softirq_exit {
-       vec: 3
-     }
+      timestamp: 140500000
+      pid: 500
+      softirq_exit {
+        vec: 3
+      }
     }
   }
 }
diff --git a/tools/install-build-deps b/tools/install-build-deps
index de21e94..eeb0130 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -176,7 +176,7 @@
     Dependency(
         'buildtools/benchmark',
         'https://chromium.googlesource.com/external/github.com/google/benchmark.git',
-        '090faecb454fbd6e6e17a75ef8146acb037118d4', 'all', 'all'),
+        'e991355c02b93fe17713efe04cbc2e278e00fdbd', 'all', 'all'),
 
     # Libbacktrace, for stacktraces in Linux/Android debug builds.
     # From https://github.com/ianlancetaylor/libbacktrace/archive/177940370e4a6b2509e92a0aaa9749184e64af43.zip
diff --git a/tools/proto_utils.py b/tools/proto_utils.py
index 45fad89..87a3150 100644
--- a/tools/proto_utils.py
+++ b/tools/proto_utils.py
@@ -18,7 +18,7 @@
 import subprocess
 import tempfile
 
-from google.protobuf import descriptor, descriptor_pb2, message_factory, descriptor_pool
+from google.protobuf import descriptor, descriptor_pb2, message_factory
 from google.protobuf import reflection, text_format
 
 
@@ -26,14 +26,15 @@
 
 
 def create_message_factory(descriptor_file_paths, proto_type):
-  pool = descriptor_pool.DescriptorPool()
+  files = []
   for file_path in descriptor_file_paths:
-    descriptor = read_descriptor(file_path)
-    for file in descriptor.file:
-      pool.Add(file)
+    files.extend(read_descriptor(file_path).file)
 
-  return message_factory.MessageFactory().GetPrototype(
-      pool.FindMessageTypeByName(proto_type))
+  # We use this method rather than working directly with DescriptorPool
+  # because, when the pure-Python protobuf runtime is used, extensions
+  # need to be explicitly registered with the message type. See
+  # https://github.com/protocolbuffers/protobuf/blob/9e09343a49e9e75be576b31ed7402bf8502b080c/python/google/protobuf/message_factory.py#L145
+  return message_factory.GetMessages(files)[proto_type]
 
 
 def read_descriptor(file_name):
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 1f8ca17..e668c86 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -756,10 +756,6 @@
     state.extensionInstalled = args.available;
   },
 
-  updateBufferUsage(state: StateDraft, args: {percentage: number}): void {
-    state.bufferUsage = args.percentage;
-  },
-
   setRecordingTarget(state: StateDraft, args: {target: RecordingTarget}): void {
     state.recordingTarget = args.target;
   },
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 6447767..cd8fb60 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -394,8 +394,6 @@
     LoadedConfigNone|LoadedConfigAutomatic|LoadedConfigNamed;
 
 export interface State {
-  // tslint:disable-next-line:no-any
-  [key: string]: any;
   version: number;
   currentEngineId?: string;
   nextId: string;
diff --git a/ui/src/controller/permalink_controller.ts b/ui/src/controller/permalink_controller.ts
index 31a7010..b922e8b 100644
--- a/ui/src/controller/permalink_controller.ts
+++ b/ui/src/controller/permalink_controller.ts
@@ -110,8 +110,9 @@
 
   private static isRecordConfig(stateOrConfig: State|
                                 RecordConfig): stateOrConfig is RecordConfig {
-    return ['STOP_WHEN_FULL', 'RING_BUFFER', 'LONG_TRACE'].includes(
-        stateOrConfig.mode);
+    const mode = (stateOrConfig as {mode?: string}).mode;
+    return mode !== undefined &&
+        ['STOP_WHEN_FULL', 'RING_BUFFER', 'LONG_TRACE'].includes(mode);
   }
 
   private static async createPermalink(isRecordingConfig: boolean):
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 62e9e59..ab76002 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -104,7 +104,6 @@
   'android_ion',
   'android_lmk',
   'android_dma_heap',
-  'android_thread_time_in_state',
   'android_surfaceflinger',
   'android_batt',
   'android_sysui_cuj',
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index 88a44ce..d553aa9 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -643,7 +643,7 @@
     from thread_counter_track
     join thread using(utid)
     left join process using(upid)
-    where thread_counter_track.name not in ('time_in_state', 'thread_time')
+    where thread_counter_track.name != 'thread_time'
   `);
 
     const it = result.iter({
@@ -1102,10 +1102,6 @@
       from sched join thread using(utid)
       group by upid
     ) using(upid)
-    left join (select upid, max(value) as total_cycles
-      from android_thread_time_in_state_event
-      group by upid
-    ) using(upid)
     left join (
       select
         distinct(upid) as upid,
@@ -1123,7 +1119,6 @@
       chromeProcessRank desc,
       hasHeapProfiles desc,
       total_dur desc,
-      total_cycles desc,
       the_tracks.upid,
       the_tracks.utid;
   `);
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index c0977ce..af96463 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -81,7 +81,8 @@
     globals.frontendLocalState.mergeState(this.state.frontendLocalState);
 
     // Only redraw if something other than the frontendLocalState changed.
-    for (const key in this.state) {
+    let key: keyof State;
+    for (key in this.state) {
       if (key !== 'frontendLocalState' && key !== 'visibleTracks' &&
           oldState[key] !== this.state[key]) {
         globals.rafScheduler.scheduleFullRedraw();