Merge "Keep raw name of power_rails"
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/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index 69cc958..757157f 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -480,9 +480,11 @@
 
           // Write the event itself.
           {
+            bool on_current_thread_track =
+                (&track == &TrackEventInternal::kDefaultTrack);
             auto event_ctx = TrackEventInternal::WriteEvent(
                 trace_writer, incr_state, tls_state, static_category,
-                event_name, type, trace_timestamp);
+                event_name, type, trace_timestamp, on_current_thread_track);
             // 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
@@ -499,7 +501,7 @@
                     return true;
                   });
             }
-            if (&track != &TrackEventInternal::kDefaultTrack)
+            if (!on_current_thread_track)
               event_ctx.event()->set_track_uuid(track.uuid);
             WriteTrackEventArgs(std::move(event_ctx),
                                 std::forward<Arguments>(args)...);
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index dc3a617..be2c528 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -101,10 +101,12 @@
       const auto& config = locked_ds->GetConfig();
       disable_incremental_timestamps = config.disable_incremental_timestamps();
       filter_debug_annotations = config.filter_debug_annotations();
+      enable_thread_time_sampling = config.enable_thread_time_sampling();
     }
   }
   bool disable_incremental_timestamps = false;
   bool filter_debug_annotations = false;
+  bool enable_thread_time_sampling = false;
 };
 
 struct TrackEventIncrementalState {
@@ -157,6 +159,7 @@
   // counter track. The key (uint64_t) is the uuid of counter track.
   // The value is used for delta encoding of counter values.
   std::unordered_map<uint64_t, int64_t> last_counter_value_per_track;
+  int64_t last_thread_time_ns = 0;
 };
 
 // The backend portion of the track event trace point implemention. Outlined to
@@ -188,7 +191,8 @@
       const Category* category,
       const char* name,
       perfetto::protos::pbzero::TrackEvent::Type,
-      const TraceTimestamp& timestamp);
+      const TraceTimestamp& timestamp,
+      bool on_current_thread_track);
 
   static void ResetIncrementalStateIfRequired(
       TraceWriterBase* trace_writer,
diff --git a/include/perfetto/tracing/track.h b/include/perfetto/tracing/track.h
index 1ddb85e..cee0be6 100644
--- a/include/perfetto/tracing/track.h
+++ b/include/perfetto/tracing/track.h
@@ -25,6 +25,7 @@
 #include "perfetto/tracing/internal/compile_time_hash.h"
 #include "perfetto/tracing/platform.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/track_event/counter_descriptor.gen.h"
 #include "protos/perfetto/trace/track_event/counter_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/track_descriptor.gen.h"
 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
@@ -199,6 +200,8 @@
 
  public:
   using Unit = perfetto::protos::pbzero::CounterDescriptor::Unit;
+  using CounterType =
+      perfetto::protos::gen::CounterDescriptor::BuiltinCounterType;
 
   // |name| must be a string with static lifetime.
   constexpr explicit CounterTrack(const char* name,
@@ -240,27 +243,32 @@
 
   constexpr CounterTrack set_unit(Unit unit) const {
     return CounterTrack(uuid, parent_uuid, name_, category_, unit, unit_name_,
-                        unit_multiplier_, is_incremental_);
+                        unit_multiplier_, is_incremental_, type_);
+  }
+
+  constexpr CounterTrack set_type(CounterType type) const {
+    return CounterTrack(uuid, parent_uuid, name_, category_, unit_, unit_name_,
+                        unit_multiplier_, is_incremental_, type);
   }
 
   constexpr CounterTrack set_unit_name(const char* unit_name) const {
     return CounterTrack(uuid, parent_uuid, name_, category_, unit_, unit_name,
-                        unit_multiplier_, is_incremental_);
+                        unit_multiplier_, is_incremental_, type_);
   }
 
   constexpr CounterTrack set_unit_multiplier(int64_t unit_multiplier) const {
     return CounterTrack(uuid, parent_uuid, name_, category_, unit_, unit_name_,
-                        unit_multiplier, is_incremental_);
+                        unit_multiplier, is_incremental_, type_);
   }
 
   constexpr CounterTrack set_category(const char* category) const {
     return CounterTrack(uuid, parent_uuid, name_, category, unit_, unit_name_,
-                        unit_multiplier_, is_incremental_);
+                        unit_multiplier_, is_incremental_, type_);
   }
 
   constexpr CounterTrack set_is_incremental(bool is_incremental = true) const {
     return CounterTrack(uuid, parent_uuid, name_, category_, unit_, unit_name_,
-                        unit_multiplier_, is_incremental);
+                        unit_multiplier_, is_incremental, type_);
   }
 
   constexpr bool is_incremental() const { return is_incremental_; }
@@ -276,14 +284,16 @@
                          Unit unit,
                          const char* unit_name,
                          int64_t unit_multiplier,
-                         bool is_incremental)
+                         bool is_incremental,
+                         CounterType type)
       : Track(uuid_, parent_uuid_),
         name_(name),
         category_(category),
         unit_(unit),
         unit_name_(unit_name),
         unit_multiplier_(unit_multiplier),
-        is_incremental_(is_incremental) {}
+        is_incremental_(is_incremental),
+        type_(type) {}
 
   const char* const name_;
   const char* const category_;
@@ -291,6 +301,8 @@
   const char* const unit_name_ = nullptr;
   int64_t unit_multiplier_ = 1;
   const bool is_incremental_ = false;
+  CounterType type_ =
+      perfetto::protos::gen::CounterDescriptor::COUNTER_UNSPECIFIED;
 };
 
 namespace internal {
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 5c354e8..cbcc81e 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -1581,6 +1581,16 @@
   // TRACE_EVENT macros are not written into the trace. Typed arguments will
   // still be emitted even if set to true.
   optional bool filter_debug_annotations = 7;
+
+  // Default : false (i.e. disabled)
+  // When true, the SDK samples and emits the current thread time counter value
+  // for each event on the current thread's track. This value represents the
+  // total CPU time consumed by that thread since its creation. Note that if a
+  // thread is not scheduled by OS for some duration, that time won't be
+  // included in thread_time.
+  // Learn more : "CLOCK_THREAD_CPUTIME_ID" flag at
+  // https://linux.die.net/man/3/clock_gettime
+  optional bool enable_thread_time_sampling = 8;
 }
 
 // End of protos/perfetto/config/track_event/track_event_config.proto
diff --git a/protos/perfetto/config/track_event/track_event_config.proto b/protos/perfetto/config/track_event/track_event_config.proto
index 3725cd1..983e2c9 100644
--- a/protos/perfetto/config/track_event/track_event_config.proto
+++ b/protos/perfetto/config/track_event/track_event_config.proto
@@ -81,4 +81,14 @@
   // TRACE_EVENT macros are not written into the trace. Typed arguments will
   // still be emitted even if set to true.
   optional bool filter_debug_annotations = 7;
+
+  // Default : false (i.e. disabled)
+  // When true, the SDK samples and emits the current thread time counter value
+  // for each event on the current thread's track. This value represents the
+  // total CPU time consumed by that thread since its creation. Note that if a
+  // thread is not scheduled by OS for some duration, that time won't be
+  // included in thread_time.
+  // Learn more : "CLOCK_THREAD_CPUTIME_ID" flag at
+  // https://linux.die.net/man/3/clock_gettime
+  optional bool enable_thread_time_sampling = 8;
 }
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/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 63b058f..27c1e5d 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -1581,6 +1581,16 @@
   // TRACE_EVENT macros are not written into the trace. Typed arguments will
   // still be emitted even if set to true.
   optional bool filter_debug_annotations = 7;
+
+  // Default : false (i.e. disabled)
+  // When true, the SDK samples and emits the current thread time counter value
+  // for each event on the current thread's track. This value represents the
+  // total CPU time consumed by that thread since its creation. Note that if a
+  // thread is not scheduled by OS for some duration, that time won't be
+  // included in thread_time.
+  // Learn more : "CLOCK_THREAD_CPUTIME_ID" flag at
+  // https://linux.die.net/man/3/clock_gettime
+  optional bool enable_thread_time_sampling = 8;
 }
 
 // End of protos/perfetto/config/track_event/track_event_config.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/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/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index 2d4d12a..08b491a 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -339,6 +339,10 @@
 
   incr_state->last_timestamp_ns = sequence_timestamp.value;
   auto default_track = ThreadTrack::Current();
+  auto thread_time_counter_track =
+      CounterTrack("thread_time", default_track)
+          .set_is_incremental(true)
+          .set_type(protos::gen::CounterDescriptor::COUNTER_THREAD_TIME_NS);
   {
     // Mark any incremental state before this point invalid. Also set up
     // defaults so that we don't need to repeat constant data for each packet.
@@ -350,6 +354,10 @@
     // Establish the default track for this event sequence.
     auto track_defaults = defaults->set_track_event_defaults();
     track_defaults->set_track_uuid(default_track.uuid);
+    if (tls_state.enable_thread_time_sampling) {
+      track_defaults->add_extra_counter_track_uuids(
+          thread_time_counter_track.uuid);
+    }
 
     if (PERFETTO_LIKELY(!tls_state.disable_incremental_timestamps)) {
       defaults->set_timestamp_clock_id(kClockIdIncremental);
@@ -382,6 +390,11 @@
 
   WriteTrackDescriptor(ProcessTrack::Current(), trace_writer, incr_state,
                        tls_state, sequence_timestamp);
+
+  if (tls_state.enable_thread_time_sampling) {
+    WriteTrackDescriptor(thread_time_counter_track, trace_writer, incr_state,
+                         tls_state, sequence_timestamp);
+  }
 }
 
 // static
@@ -429,7 +442,8 @@
     const Category* category,
     const char* name,
     perfetto::protos::pbzero::TrackEvent::Type type,
-    const TraceTimestamp& timestamp) {
+    const TraceTimestamp& timestamp,
+    bool on_current_thread_track) {
   PERFETTO_DCHECK(g_main_thread);
   PERFETTO_DCHECK(!incr_state->was_cleared);
   auto packet = NewTracePacket(trace_writer, incr_state, tls_state, timestamp);
@@ -439,6 +453,14 @@
   if (type != protos::pbzero::TrackEvent::TYPE_UNSPECIFIED)
     track_event->set_type(type);
 
+  if (tls_state.enable_thread_time_sampling && on_current_thread_track) {
+    int64_t thread_time_ns = base::GetThreadCPUTimeNs().count();
+    auto thread_time_delta_ns =
+        thread_time_ns - incr_state->last_thread_time_ns;
+    incr_state->last_thread_time_ns = thread_time_ns;
+    track_event->add_extra_counter_values(thread_time_delta_ns);
+  }
+
   // We assume that |category| and |name| point to strings with static lifetime.
   // This means we can use their addresses as interning keys.
   // TODO(skyostil): Intern categories at compile time.
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 0604d09..466c06e 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -144,6 +144,12 @@
   return WriteFile(file_name, data.data(), data.size());
 }
 
+// Returns true if the |key| is present in |container|.
+template <typename ContainerType, class KeyType>
+bool ContainsKey(const ContainerType& container, const KeyType& key) {
+  return container.find(key) != container.end();
+}
+
 // Represents an opaque (from Perfetto's point of view) thread identifier (e.g.,
 // base::PlatformThreadId in Chromium).
 struct MyThreadId {
@@ -2144,6 +2150,86 @@
   std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
 }
 
+TEST_P(PerfettoApiTest, TrackEventThreadTime) {
+  for (auto enable_thread_time : {true, false}) {
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    te_cfg.set_enable_thread_time_sampling(enable_thread_time);
+    auto* tracing_session = NewTraceWithCategories({"foo"}, te_cfg);
+
+    tracing_session->get()->StartBlocking();
+
+    perfetto::Track custom_track(1);
+
+    TRACE_EVENT_BEGIN("foo", "event1");
+    TRACE_EVENT_BEGIN("foo", "event2");
+    TRACE_EVENT_BEGIN("foo", "event3");
+    TRACE_EVENT_BEGIN("foo", "event4", custom_track);
+    TRACE_EVENT_END("foo");
+    TRACE_EVENT_END("foo");
+    TRACE_EVENT_END("foo");
+    TRACE_EVENT_END("foo");
+
+    tracing_session->get()->StopBlocking();
+
+    std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+
+    perfetto::protos::gen::Trace parsed_trace;
+    EXPECT_TRUE(
+        parsed_trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+    bool found_counter_track_descriptor = false;
+    uint64_t thread_time_counter_uuid = 0;
+    uint64_t default_counter_uuid = 0;
+    std::unordered_set<std::string> event_names;
+    for (const auto& packet : parsed_trace.packet()) {
+      if (packet.has_track_descriptor() &&
+          packet.track_descriptor().has_counter()) {
+        EXPECT_FALSE(found_counter_track_descriptor);
+        found_counter_track_descriptor = true;
+        thread_time_counter_uuid = packet.track_descriptor().uuid();
+        EXPECT_EQ("thread_time", packet.track_descriptor().name());
+        auto counter = packet.track_descriptor().counter();
+        EXPECT_EQ(
+            perfetto::protos::gen::
+                CounterDescriptor_BuiltinCounterType_COUNTER_THREAD_TIME_NS,
+            counter.type());
+        EXPECT_TRUE(counter.is_incremental());
+      }
+      if (packet.has_trace_packet_defaults()) {
+        auto& defaults = packet.trace_packet_defaults().track_event_defaults();
+        ASSERT_EQ(enable_thread_time ? 1u : 0u,
+                  defaults.extra_counter_track_uuids().size());
+        if (enable_thread_time) {
+          default_counter_uuid = defaults.extra_counter_track_uuids().at(0);
+        }
+      }
+      if (packet.has_track_event()) {
+        std::string event_name;
+        if (packet.has_interned_data()) {
+          auto& event_names_info = packet.interned_data().event_names();
+          if (event_names_info.size() > 0) {
+            event_name = event_names_info[0].name();
+          }
+        }
+        event_names.insert(event_name);
+        EXPECT_EQ((enable_thread_time && event_name != "event4") ? 1u : 0u,
+                  packet.track_event().extra_counter_values().size());
+      }
+    }
+    EXPECT_TRUE(ContainsKey(event_names, "event1"));
+    EXPECT_TRUE(ContainsKey(event_names, "event2"));
+    EXPECT_TRUE(ContainsKey(event_names, "event3"));
+    EXPECT_TRUE(ContainsKey(event_names, "event4"));
+    EXPECT_EQ(enable_thread_time, found_counter_track_descriptor);
+    EXPECT_EQ(default_counter_uuid, thread_time_counter_uuid);
+    if (enable_thread_time) {
+      EXPECT_GT(thread_time_counter_uuid, 0u);
+    } else {
+      EXPECT_EQ(thread_time_counter_uuid, 0u);
+    }
+  }
+}
+
 TEST_P(PerfettoApiTest, TrackEventArgs_TypedAndUntyped) {
   // Create a new trace session.
   auto* tracing_session = NewTraceWithCategories({"foo"});
diff --git a/src/tracing/track.cc b/src/tracing/track.cc
index 467a56b..f24138b 100644
--- a/src/tracing/track.cc
+++ b/src/tracing/track.cc
@@ -100,8 +100,16 @@
     counter->add_categories(category_);
   if (unit_ != perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED)
     counter->set_unit(static_cast<protos::gen::CounterDescriptor_Unit>(unit_));
-  if (unit_name_)
-    counter->set_unit_name(unit_name_);
+  {
+    // if |type| is set, we don't want to emit |unit_name|. Trace processor
+    // infers the track name from the type in that case.
+    if (type_ !=
+        perfetto::protos::gen::CounterDescriptor::COUNTER_UNSPECIFIED) {
+      counter->set_type(type_);
+    } else if (unit_name_) {
+      counter->set_unit_name(unit_name_);
+    }
+  }
   if (unit_multiplier_ != 1)
     counter->set_unit_multiplier(unit_multiplier_);
   if (is_incremental_)
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();