Merge "processor: Resolve descriptor track names during tokenization as well"
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/include/perfetto/tracing/track.h b/include/perfetto/tracing/track.h
index 8a4df74..f297b08 100644
--- a/include/perfetto/tracing/track.h
+++ b/include/perfetto/tracing/track.h
@@ -91,8 +91,11 @@
   static Track Global(uint64_t id) { return Track(id, Track()); }
 
  protected:
-  static Track MakeThreadTrack(base::PlatformThreadId tid_) {
-    return Track(static_cast<uint64_t>(tid_), MakeProcessTrack());
+  static Track MakeThreadTrack(base::PlatformThreadId tid) {
+    // If tid were 0 here (which is an invalid tid), we would create a thread
+    // track with a uuid that conflicts with the corresponding ProcessTrack.
+    PERFETTO_DCHECK(tid != 0);
+    return Track(static_cast<uint64_t>(tid), MakeProcessTrack());
   }
 
   static Track MakeProcessTrack() { return Track(process_uuid, Track()); }
diff --git a/src/trace_processor/importers/proto/android_probes_module.cc b/src/trace_processor/importers/proto/android_probes_module.cc
index 7fca0dd..28405d1 100644
--- a/src/trace_processor/importers/proto/android_probes_module.cc
+++ b/src/trace_processor/importers/proto/android_probes_module.cc
@@ -37,6 +37,7 @@
   RegisterForField(TracePacket::kPowerRailsFieldNumber, context);
   RegisterForField(TracePacket::kAndroidLogFieldNumber, context);
   RegisterForField(TracePacket::kPackagesListFieldNumber, context);
+  RegisterForField(TracePacket::kInitialDisplayStateFieldNumber, context);
 }
 
 ModuleResult AndroidProbesModule::TokenizePacket(
@@ -127,6 +128,10 @@
     case TracePacket::kPackagesListFieldNumber:
       parser_.ParseAndroidPackagesList(decoder.packages_list());
       return;
+    case TracePacket::kInitialDisplayStateFieldNumber:
+      parser_.ParseInitialDisplayState(ttp.timestamp,
+                                       decoder.initial_display_state());
+      return;
   }
 }
 
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index fba0c10..f03d3e3 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -29,6 +29,7 @@
 #include "protos/perfetto/common/android_log_constants.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
 #include "protos/perfetto/trace/android/android_log.pbzero.h"
+#include "protos/perfetto/trace/android/initial_display_state.pbzero.h"
 #include "protos/perfetto/trace/android/packages_list.pbzero.h"
 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 #include "protos/perfetto/trace/power/battery_counters.pbzero.h"
@@ -49,7 +50,8 @@
       batt_capacity_id_(context->storage->InternString("batt.capacity_pct")),
       batt_current_id_(context->storage->InternString("batt.current_ua")),
       batt_current_avg_id_(
-          context->storage->InternString("batt.current.avg_ua")) {}
+          context->storage->InternString("batt.current.avg_ua")),
+      screen_state_id_(context->storage->InternString("ScreenState")) {}
 
 void AndroidProbesParser::ParseBatteryCounters(int64_t ts, ConstBytes blob) {
   protos::pbzero::BatteryCounters::Decoder evt(blob.data, blob.size);
@@ -239,5 +241,14 @@
   }
 }
 
+void AndroidProbesParser::ParseInitialDisplayState(int64_t ts,
+                                                   ConstBytes blob) {
+  protos::pbzero::InitialDisplayState::Decoder state(blob.data, blob.size);
+
+  TrackId track =
+      context_->track_tracker->InternGlobalCounterTrack(screen_state_id_);
+  context_->event_tracker->PushCounter(ts, state.display_state(), track);
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/android_probes_parser.h b/src/trace_processor/importers/proto/android_probes_parser.h
index 2a85c11..8c4994d 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.h
+++ b/src/trace_processor/importers/proto/android_probes_parser.h
@@ -40,6 +40,7 @@
   void ParseAndroidLogStats(ConstBytes);
   void ParseStatsdMetadata(ConstBytes);
   void ParseAndroidPackagesList(ConstBytes);
+  void ParseInitialDisplayState(int64_t ts, ConstBytes);
 
  private:
   TraceProcessorContext* const context_;
@@ -48,6 +49,7 @@
   const StringId batt_capacity_id_;
   const StringId batt_current_id_;
   const StringId batt_current_avg_id_;
+  const StringId screen_state_id_;
   std::vector<StringId> power_rails_strs_id_;
 };
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/args_table_utils.cc b/src/trace_processor/importers/proto/args_table_utils.cc
index 508f5bf..b1dd2e0 100644
--- a/src/trace_processor/importers/proto/args_table_utils.cc
+++ b/src/trace_processor/importers/proto/args_table_utils.cc
@@ -38,6 +38,44 @@
                                         proto_descriptor_array_size);
 }
 
+util::Status ProtoToArgsTable::InternProtoFieldsIntoArgsTable(
+    const protozero::ConstBytes& cb,
+    const std::string& type,
+    const std::vector<uint16_t>& fields,
+    ArgsTracker::BoundInserter* inserter,
+    PacketSequenceStateGeneration* sequence_state) {
+  auto idx = pool_.FindDescriptorIdx(type);
+  if (!idx) {
+    return util::Status("Failed to find proto descriptor");
+  }
+
+  auto descriptor = pool_.descriptors()[*idx];
+
+  protozero::ProtoDecoder decoder(cb);
+  for (protozero::Field f = decoder.ReadField(); f.valid();
+       f = decoder.ReadField()) {
+    auto it = std::find(fields.begin(), fields.end(), f.id());
+    if (it == fields.end()) {
+      continue;
+    }
+
+    auto field_idx = descriptor.FindFieldIdxByTag(*it);
+    if (!field_idx) {
+      return util::Status("Failed to find proto descriptor");
+    }
+    auto field = descriptor.fields()[*field_idx];
+    auto status =
+        InternProtoIntoArgsTable(f.as_bytes(), field.resolved_type_name(),
+                                 inserter, sequence_state, field.name());
+
+    if (!status.ok()) {
+      return status;
+    }
+  }
+
+  return util::OkStatus();
+}
+
 util::Status ProtoToArgsTable::InternProtoIntoArgsTable(
     const protozero::ConstBytes& cb,
     const std::string& type,
diff --git a/src/trace_processor/importers/proto/args_table_utils.h b/src/trace_processor/importers/proto/args_table_utils.h
index 05f158c..c103a1f 100644
--- a/src/trace_processor/importers/proto/args_table_utils.h
+++ b/src/trace_processor/importers/proto/args_table_utils.h
@@ -136,6 +136,14 @@
       PacketSequenceStateGeneration* sequence_state,
       const std::string& key_prefix);
 
+  // Parse several fields with ids given in |fields| using reflection.
+  util::Status InternProtoFieldsIntoArgsTable(
+      const protozero::ConstBytes& cb,
+      const std::string& type,
+      const std::vector<uint16_t>& fields,
+      ArgsTracker::BoundInserter* inserter,
+      PacketSequenceStateGeneration* sequence_state);
+
   // Installs an override for the field at the specified path. We will invoke
   // |parsing_override| when the field is encountered.
   //
diff --git a/src/trace_processor/importers/proto/system_probes_module.cc b/src/trace_processor/importers/proto/system_probes_module.cc
index 1a9af08..fde63fc 100644
--- a/src/trace_processor/importers/proto/system_probes_module.cc
+++ b/src/trace_processor/importers/proto/system_probes_module.cc
@@ -35,6 +35,20 @@
   RegisterForField(TracePacket::kCpuInfoFieldNumber, context);
 }
 
+ModuleResult SystemProbesModule::TokenizePacket(
+    const protos::pbzero::TracePacket::Decoder& decoder,
+    TraceBlobView*,
+    int64_t,
+    PacketSequenceState*,
+    uint32_t field_id) {
+  switch (field_id) {
+    case TracePacket::kSystemInfoFieldNumber:
+      parser_.ParseSystemInfo(decoder.system_info());
+      return ModuleResult::Handled();
+  }
+  return ModuleResult::Ignored();
+}
+
 void SystemProbesModule::ParsePacket(const TracePacket::Decoder& decoder,
                                      const TimestampedTracePiece& ttp,
                                      uint32_t field_id) {
@@ -48,9 +62,6 @@
     case TracePacket::kSysStatsFieldNumber:
       parser_.ParseSysStats(ttp.timestamp, decoder.sys_stats());
       return;
-    case TracePacket::kSystemInfoFieldNumber:
-      parser_.ParseSystemInfo(decoder.system_info());
-      return;
     case TracePacket::kCpuInfoFieldNumber:
       parser_.ParseCpuInfo(decoder.cpu_info());
       return;
diff --git a/src/trace_processor/importers/proto/system_probes_module.h b/src/trace_processor/importers/proto/system_probes_module.h
index 438eabe..8fcd94b 100644
--- a/src/trace_processor/importers/proto/system_probes_module.h
+++ b/src/trace_processor/importers/proto/system_probes_module.h
@@ -30,6 +30,12 @@
  public:
   explicit SystemProbesModule(TraceProcessorContext* context);
 
+  ModuleResult TokenizePacket(const protos::pbzero::TracePacket::Decoder&,
+                              TraceBlobView* packet,
+                              int64_t packet_timestamp,
+                              PacketSequenceState*,
+                              uint32_t field_id) override;
+
   void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
                    const TimestampedTracePiece& ttp,
                    uint32_t field_id) override;
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index af0275c..e432dbb 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -839,24 +839,10 @@
     if (event_.has_log_message()) {
       log_errors(ParseLogMessage(event_.log_message(), inserter));
     }
-    if (event_.has_cc_scheduler_state()) {
-      ParseCcScheduler(event_.cc_scheduler_state(), inserter);
-    }
-    if (event_.has_chrome_user_event()) {
-      ParseChromeUserEvent(event_.chrome_user_event(), inserter);
-    }
-    if (event_.has_chrome_legacy_ipc()) {
-      ParseChromeLegacyIpc(event_.chrome_legacy_ipc(), inserter);
-    }
-    if (event_.has_chrome_keyed_service()) {
-      ParseChromeKeyedService(event_.chrome_keyed_service(), inserter);
-    }
-    if (event_.has_chrome_histogram_sample()) {
-      ParseChromeHistogramSample(event_.chrome_histogram_sample(), inserter);
-    }
-    if (event_.has_chrome_latency_info()) {
-      ParseChromeLatencyInfo(event_.chrome_latency_info(), inserter);
-    }
+
+    log_errors(parser_->proto_to_args_.InternProtoFieldsIntoArgsTable(
+        blob_, ".perfetto.protos.TrackEvent", parser_->reflect_fields_,
+        inserter, sequence_state_));
 
     if (legacy_passthrough_utid_) {
       inserter->AddArg(parser_->legacy_event_passthrough_utid_id_,
@@ -1080,48 +1066,6 @@
     return util::OkStatus();
   }
 
-  void ParseCcScheduler(ConstBytes cc, BoundInserter* outer_inserter) {
-    parser_->proto_to_args_.InternProtoIntoArgsTable(
-        cc, ".perfetto.protos.ChromeCompositorSchedulerState", outer_inserter,
-        sequence_state_,
-        /* prefix= */ "cc_scheduler_state");
-  }
-
-  void ParseChromeUserEvent(protozero::ConstBytes chrome_user_event,
-                            BoundInserter* inserter) {
-    parser_->proto_to_args_.InternProtoIntoArgsTable(
-        chrome_user_event, ".perfetto.protos.ChromeUserEvent", inserter,
-        sequence_state_, "user_event");
-  }
-
-  void ParseChromeLegacyIpc(protozero::ConstBytes chrome_legacy_ipc,
-                            BoundInserter* inserter) {
-    parser_->proto_to_args_.InternProtoIntoArgsTable(
-        chrome_legacy_ipc, ".perfetto.protos.ChromeLegacyIpc", inserter,
-        sequence_state_, "legacy_ipc");
-  }
-
-  void ParseChromeKeyedService(protozero::ConstBytes chrome_keyed_service,
-                               BoundInserter* inserter) {
-    parser_->proto_to_args_.InternProtoIntoArgsTable(
-        chrome_keyed_service, ".perfetto.protos.ChromeKeyedService", inserter,
-        sequence_state_, "keyed_service");
-  }
-
-  void ParseChromeLatencyInfo(protozero::ConstBytes chrome_latency_info,
-                              BoundInserter* inserter) {
-    parser_->proto_to_args_.InternProtoIntoArgsTable(
-        chrome_latency_info, ".perfetto.protos.ChromeLatencyInfo", inserter,
-        sequence_state_, "latency_info");
-  }
-
-  void ParseChromeHistogramSample(protozero::ConstBytes chrome_histogram_sample,
-                                  BoundInserter* inserter) {
-    parser_->proto_to_args_.InternProtoIntoArgsTable(
-        chrome_histogram_sample, ".perfetto.protos.ChromeHistogramSample",
-        inserter, sequence_state_, "histogram_sample");
-  }
-
   TraceProcessorContext* context_;
   TraceStorage* storage_;
   TrackEventParser* parser_;
@@ -1301,6 +1245,10 @@
             "begin_frame_observer_state.last_begin_frame_args", state, field,
             inserter);
       });
+
+  for (uint16_t index : kReflectFields) {
+    reflect_fields_.push_back(index);
+  }
 }
 
 void TrackEventParser::ParseTrackDescriptor(
diff --git a/src/trace_processor/importers/proto/track_event_parser.h b/src/trace_processor/importers/proto/track_event_parser.h
index ebae388..b3abc4c 100644
--- a/src/trace_processor/importers/proto/track_event_parser.h
+++ b/src/trace_processor/importers/proto/track_event_parser.h
@@ -35,6 +35,12 @@
 
 namespace trace_processor {
 
+// Field numbers to be added to args table automatically via reflection
+//
+// TODO(ddrone): replace with a predicate on field id to import new fields
+// automatically
+static constexpr uint16_t kReflectFields[] = {24, 25, 26, 27, 28, 29};
+
 class PacketSequenceStateGeneration;
 class TraceProcessorContext;
 
@@ -94,6 +100,8 @@
   std::array<StringId, 9> chrome_process_name_ids_;
   std::array<StringId, 14> chrome_thread_name_ids_;
   std::array<StringId, 4> counter_unit_ids_;
+
+  std::vector<uint16_t> reflect_fields_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/systrace/systrace_parser.cc b/src/trace_processor/importers/systrace/systrace_parser.cc
index ddeb4c0..f31dcfd 100644
--- a/src/trace_processor/importers/systrace/systrace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_parser.cc
@@ -26,7 +26,9 @@
 namespace trace_processor {
 
 SystraceParser::SystraceParser(TraceProcessorContext* ctx)
-    : context_(ctx), lmk_id_(ctx->storage->InternString("mem.lmk")) {}
+    : context_(ctx),
+      lmk_id_(ctx->storage->InternString("mem.lmk")),
+      screen_state_id_(ctx->storage->InternString("ScreenState")) {}
 
 SystraceParser::~SystraceParser() = default;
 
@@ -182,6 +184,12 @@
         }
         // TODO(lalitm): we should not add LMK events to the counters table
         // once the UI has support for displaying instants.
+      } else if (point.name == "ScreenState") {
+        // Promote ScreenState to its own top level counter.
+        TrackId track =
+            context_->track_tracker->InternGlobalCounterTrack(screen_state_id_);
+        context_->event_tracker->PushCounter(ts, point.value, track);
+        return;
       }
       // This is per upid on purpose. Some counters are pushed from arbitrary
       // threads but are really per process.
diff --git a/src/trace_processor/importers/systrace/systrace_parser.h b/src/trace_processor/importers/systrace/systrace_parser.h
index eaa6f15..ae5eec6 100644
--- a/src/trace_processor/importers/systrace/systrace_parser.h
+++ b/src/trace_processor/importers/systrace/systrace_parser.h
@@ -253,6 +253,7 @@
 
   TraceProcessorContext* const context_;
   const StringId lmk_id_;
+  const StringId screen_state_id_;
 };
 
 }  // namespace trace_processor
diff --git a/src/traced/probes/ps/process_stats_data_source.cc b/src/traced/probes/ps/process_stats_data_source.cc
index ddbc152..2e4f53d 100644
--- a/src/traced/probes/ps/process_stats_data_source.cc
+++ b/src/traced/probes/ps/process_stats_data_source.cc
@@ -24,6 +24,7 @@
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/hash.h"
 #include "perfetto/ext/base/metatrace.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
@@ -109,10 +110,6 @@
   ProcessStatsConfig::Decoder cfg(ds_config.process_stats_config_raw());
   record_thread_names_ = cfg.record_thread_names();
   dump_all_procs_on_start_ = cfg.scan_all_processes_on_start();
-  record_thread_time_in_state_ = cfg.record_thread_time_in_state();
-  thread_time_in_state_cache_size_ = cfg.thread_time_in_state_cache_size();
-  if (thread_time_in_state_cache_size_ == 0)
-    thread_time_in_state_cache_size_ = kThreadTimeInStateCacheSize;
 
   enable_on_demand_dumps_ = true;
   for (auto quirk = cfg.quirks(); quirk; ++quirk) {
@@ -133,6 +130,12 @@
     process_stats_cache_ttl_ticks_ =
         std::max(proc_stats_ttl_ms / poll_period_ms_, 1u);
   }
+
+  record_thread_time_in_state_ = cfg.record_thread_time_in_state();
+  thread_time_in_state_cache_size_ = cfg.thread_time_in_state_cache_size();
+  if (thread_time_in_state_cache_size_ == 0)
+    thread_time_in_state_cache_size_ = kThreadTimeInStateCacheSize;
+  thread_time_in_state_cache_.resize(thread_time_in_state_cache_size_);
 }
 
 ProcessStatsDataSource::~ProcessStatsDataSource() = default;
@@ -390,6 +393,8 @@
     thiz.cache_ticks_ = 0;
     thiz.process_stats_cache_.clear();
     thiz.thread_time_in_state_cache_.clear();
+    thiz.thread_time_in_state_cache_.resize(
+        thiz.thread_time_in_state_cache_size_);
   }
 }
 
@@ -435,7 +440,7 @@
       }
     }
 
-    if (record_thread_time_in_state_) {
+    if (record_thread_time_in_state_ && ShouldWriteThreadStats(pid)) {
       if (auto task_dir = OpenProcTaskDir(pid)) {
         while (int32_t tid = ReadNextNumericDir(*task_dir))
           WriteThreadStats(pid, tid);
@@ -449,15 +454,6 @@
   // Ensure that we write once long-term process info (e.g., name) for new pids
   // that we haven't seen before.
   WriteProcessTree(pids);
-
-  // Ensure the cache stays within bounds by erasing some entries.
-  while (thread_time_in_state_cache_.size() >
-         thread_time_in_state_cache_size_) {
-    auto random = thread_time_in_state_cache_.begin();
-    std::advance(random, rand() % static_cast<int32_t>(
-                                      thread_time_in_state_cache_.size()));
-    thread_time_in_state_cache_.erase(random);
-  }
 }
 
 // Returns true if the stats for the given |pid| have been written, false it
@@ -571,6 +567,42 @@
   return proc_status_has_mem_counters;
 }
 
+// Fast check to avoid reading information about all threads of a process.
+// If the total process cpu time has not changed, we can skip reading
+// time_in_state for all its threads.
+bool ProcessStatsDataSource::ShouldWriteThreadStats(int32_t pid) {
+  std::string stat = ReadProcPidFile(pid, "stat");
+  // /proc/pid/stat may contain an additional space inside comm. For example:
+  // 1 (comm foo) 2 3 ...
+  // We strip the prefix including comm. So the result is: 2 3 ...
+  size_t comm_end = stat.rfind(") ");
+  if (comm_end == std::string::npos)
+    return false;
+  std::string stat_after_comm = stat.substr(comm_end + 2);
+
+  // Indices of space separated fields in /proc/pid/stat offset by 2 to make
+  // up for fields removed by stripping the prefix including comm.
+  const uint32_t kStatCTimeIndex = 13 - 2;
+  const uint32_t kStatSTimeIndex = 14 - 2;
+
+  auto stat_parts = base::SplitString(stat_after_comm, " ");
+  if (stat_parts.size() <= kStatSTimeIndex)
+    return false;
+  auto maybe_ctime = base::StringToUInt64(stat_parts[kStatCTimeIndex]);
+  if (!maybe_ctime.has_value())
+    return false;
+  auto maybe_stime = base::StringToUInt64(stat_parts[kStatSTimeIndex]);
+  if (!maybe_stime.has_value())
+    return false;
+  uint64_t current = maybe_ctime.value() + maybe_stime.value();
+  uint64_t& cached = process_stats_cache_[pid].cpu_time;
+  if (current != cached) {
+    cached = current;
+    return true;
+  }
+  return false;
+}
+
 void ProcessStatsDataSource::WriteThreadStats(int32_t pid, int32_t tid) {
   // Reads /proc/tid/time_in_state, which looks like:
   // cpu0
@@ -597,7 +629,6 @@
       continue;
     uint32_t freq = ToU32(key_value.cur_token());
     uint32_t freq_index = cpu_freq_info_->GetCpuFreqIndex(last_cpu, freq);
-    TidCpuFreqIndex key = {tid, freq_index};
     if (!key_value.Next())
       continue;
     auto maybe_ticks = base::CStringToUInt64(key_value.cur_token());
@@ -606,15 +637,22 @@
     uint64_t ticks = maybe_ticks.value();
     if (ticks == 0)
       continue;
-    auto& cached_ticks = thread_time_in_state_cache_[key];
-    if (ticks != cached_ticks) {
+    base::Hash key_hash;
+    key_hash.Update(tid);
+    key_hash.Update(freq_index);
+    size_t key = key_hash.digest() % thread_time_in_state_cache_size_;
+    PERFETTO_DCHECK(thread_time_in_state_cache_.size() ==
+                    thread_time_in_state_cache_size_);
+    TimeInStateCacheEntry& cached = thread_time_in_state_cache_[key];
+    TimeInStateCacheEntry current = {tid, freq_index, ticks};
+    if (current != cached) {
+      cached = current;
       if (thread == nullptr) {
         thread = GetOrCreateStatsProcess(pid)->add_threads();
         thread->set_tid(tid);
       }
       thread->add_cpu_freq_indices(freq_index);
       thread->add_cpu_freq_ticks(ticks);
-      cached_ticks = ticks;
     }
   }
 }
@@ -634,6 +672,7 @@
   cache_ticks_ = 0;
   process_stats_cache_.clear();
   thread_time_in_state_cache_.clear();
+  thread_time_in_state_cache_.resize(thread_time_in_state_cache_size_);
 
   // Set the relevant flag in the next packet.
   did_clear_incremental_state_ = true;
diff --git a/src/traced/probes/ps/process_stats_data_source.h b/src/traced/probes/ps/process_stats_data_source.h
index 19a9d4a..9d5640a 100644
--- a/src/traced/probes/ps/process_stats_data_source.h
+++ b/src/traced/probes/ps/process_stats_data_source.h
@@ -18,7 +18,6 @@
 #define SRC_TRACED_PROBES_PS_PROCESS_STATS_DATA_SOURCE_H_
 
 #include <limits>
-#include <map>
 #include <memory>
 #include <set>
 #include <unordered_map>
@@ -87,6 +86,9 @@
     uint32_t vm_locked_kb = std::numeric_limits<uint32_t>::max();
     uint32_t vm_hvm_kb = std::numeric_limits<uint32_t>::max();
     int oom_score_adj = std::numeric_limits<int>::max();
+
+    // ctime + stime from /proc/pid/stat
+    uint64_t cpu_time = std::numeric_limits<uint64_t>::max();
   };
 
   // Common functions.
@@ -109,6 +111,7 @@
   static void Tick(base::WeakPtr<ProcessStatsDataSource>);
   void WriteAllProcessStats();
   bool WriteMemCounters(int32_t pid, const std::string& proc_status);
+  bool ShouldWriteThreadStats(int32_t pid);
   void WriteThreadStats(int32_t pid, int32_t tid);
 
   // Scans /proc/pid/status and writes the ProcessTree packet for input pids.
@@ -158,9 +161,13 @@
   uint32_t process_stats_cache_ttl_ticks_ = 0;
   std::unordered_map<int32_t, CachedProcessStats> process_stats_cache_;
 
-  using TidCpuFreqIndex =
-      std::tuple</* tid */ int32_t, /* cpu_freq_index */ uint32_t>;
-  std::map<TidCpuFreqIndex, uint64_t> thread_time_in_state_cache_;
+  using TimeInStateCacheEntry = std::tuple</* tid */ int32_t,
+                                           /* cpu_freq_index */ uint32_t,
+                                           /* ticks */ uint64_t>;
+
+  // Cache for time in state. Size specificed in the config. Values are stored
+  // at index: hash(tid, cpu_freq_index) % thread_time_in_state_cache_size_.
+  std::vector<TimeInStateCacheEntry> thread_time_in_state_cache_;
   uint32_t thread_time_in_state_cache_size_;
 
   std::unique_ptr<CpuFreqInfo> cpu_freq_info_;
diff --git a/src/traced/probes/ps/process_stats_data_source_unittest.cc b/src/traced/probes/ps/process_stats_data_source_unittest.cc
index f9a2697..a9c62d3 100644
--- a/src/traced/probes/ps/process_stats_data_source_unittest.cc
+++ b/src/traced/probes/ps/process_stats_data_source_unittest.cc
@@ -486,12 +486,18 @@
   auto fake_proc = base::TempDir::Create();
   const int kPid = 1;
   make_proc_path(fake_proc, kPid);
+  const int kIgnoredPid = 5;
+  make_proc_path(fake_proc, kIgnoredPid);
 
   // Populate a fake /proc/1/task directory.
   auto fake_proc_task = base::TempDir::Create();
   const int kTids[] = {1, 2};
   for (int tid : kTids)
     make_proc_path(fake_proc_task, tid);
+  // Populate a fake /proc/5/task directory.
+  auto fake_ignored_proc_task = base::TempDir::Create();
+  const int kIgnoredTid = 5;
+  make_proc_path(fake_ignored_proc_task, kIgnoredTid);
 
   auto checkpoint = task_runner_.CreateCheckpoint("all_done");
 
@@ -500,9 +506,16 @@
   }));
   EXPECT_CALL(*data_source, ReadProcPidFile(kPid, "status"))
       .WillRepeatedly(
-          Return("Name:	pid_10\nVmSize:	 100 kB\nVmRSS:\t100  kB\n"));
+          Return("Name:	pid_1\nVmSize:	 100 kB\nVmRSS:\t100  kB\n"));
   EXPECT_CALL(*data_source, ReadProcPidFile(kPid, "oom_score_adj"))
-      .WillRepeatedly(Return("900"));
+      .WillRepeatedly(Return("901"));
+  EXPECT_CALL(*data_source, ReadProcPidFile(kPid, "stat"))
+      .WillOnce(Return("1 (pid_1) S 1 1 0 0 -1 4210944 2197 2451 0 1 54 117 4"))
+      // ctime++
+      .WillOnce(Return("1 (pid_1) S 1 1 0 0 -1 4210944 2197 2451 0 1 55 117 4"))
+      // stime++
+      .WillOnce(
+          Return("1 (pid_1) S 1 1 0 0 -1 4210944 2197 2451 0 1 55 118 4"));
   EXPECT_CALL(*data_source, OpenProcTaskDir(kPid))
       .WillRepeatedly(Invoke([&fake_proc_task](int32_t) {
         return base::ScopedDir(opendir(fake_proc_task.path().c_str()));
@@ -521,17 +534,39 @@
         return "cpu0\n300000 200\n748800 0\n1324800 30\ncpu1\n300000 "
                "100\n652800 60\n";
       }));
+  EXPECT_CALL(*data_source, ReadProcPidFile(kIgnoredPid, "status"))
+      .WillRepeatedly(
+          Return("Name:	pid_5\nVmSize:	 100 kB\nVmRSS:\t100  kB\n"));
+  EXPECT_CALL(*data_source, ReadProcPidFile(kIgnoredPid, "oom_score_adj"))
+      .WillRepeatedly(Return("905"));
+  EXPECT_CALL(*data_source, OpenProcTaskDir(kIgnoredPid))
+      .WillRepeatedly(Invoke([&fake_ignored_proc_task](int32_t) {
+        return base::ScopedDir(opendir(fake_ignored_proc_task.path().c_str()));
+      }));
+  EXPECT_CALL(*data_source, ReadProcPidFile(kIgnoredPid, "stat"))
+      .WillRepeatedly(
+          Return("5 (pid_5) S 1 5 0 0 -1 4210944 2197 2451 0 1 99 99 4"));
+  EXPECT_CALL(*data_source, ReadProcPidFile(kIgnoredTid, "time_in_state"))
+      .Times(2)
+      .WillRepeatedly(
+          Return("cpu0\n300000 10\n748800 0\ncpu1\n300000 00\n652800 20\n"));
 
   data_source->Start();
   task_runner_.RunUntilCheckpoint("all_done");
   data_source->Flush(1 /* FlushRequestId */, []() {});
 
-  std::vector<protos::gen::ProcessStats::Process> processes;
+  // Collect all process packets order by their timestamp and pid.
+  using TimestampPid = std::pair</* timestamp */ uint64_t, /* pid */ int32_t>;
+  std::map<TimestampPid, protos::gen::ProcessStats::Process> processes_map;
   for (const auto& packet : writer_raw_->GetAllTracePackets())
     for (const auto& process : packet.process_stats().processes())
-      processes.push_back(process);
+      processes_map.insert({{packet.timestamp(), process.pid()}, process});
+  std::vector<protos::gen::ProcessStats::Process> processes;
+  for (auto it : processes_map)
+    processes.push_back(it.second);
 
-  EXPECT_EQ(processes.size(), 3u);
+  // 3 packets for pid=1, 2 packets for pid=5.
+  EXPECT_EQ(processes.size(), 5u);
 
   auto compare_tid = [](protos::gen::ProcessStats_Thread& l,
                         protos::gen::ProcessStats_Thread& r) {
@@ -539,6 +574,7 @@
   };
 
   // First pull has all threads.
+  // Check pid = 1.
   auto threads = processes[0].threads();
   EXPECT_EQ(threads.size(), 2u);
   std::sort(threads.begin(), threads.end(), compare_tid);
@@ -550,9 +586,14 @@
   EXPECT_EQ(thread.tid(), 2);
   EXPECT_THAT(thread.cpu_freq_indices(), ElementsAre(1u, 10u, 11u));
   EXPECT_THAT(thread.cpu_freq_ticks(), ElementsAre(10, 50, 60));
+  // Check pid = 5.
+  threads = processes[1].threads();
+  EXPECT_EQ(threads.size(), 1u);
+  EXPECT_EQ(threads[0].tid(), 5);
+  EXPECT_THAT(threads[0].cpu_freq_ticks(), ElementsAre(10, 20));
 
   // Second pull has only one thread with delta.
-  threads = processes[1].threads();
+  threads = processes[2].threads();
   EXPECT_EQ(threads.size(), 1u);
   thread = threads[0];
   EXPECT_EQ(thread.tid(), 2);
@@ -560,7 +601,8 @@
   EXPECT_THAT(thread.cpu_freq_ticks(), ElementsAre(20, 30, 100));
 
   // Third pull has all thread because cache was cleared.
-  threads = processes[2].threads();
+  // Check pid = 1.
+  threads = processes[3].threads();
   EXPECT_EQ(threads.size(), 2u);
   std::sort(threads.begin(), threads.end(), compare_tid);
   thread = threads[0];
@@ -571,6 +613,11 @@
   EXPECT_EQ(thread.tid(), 2);
   EXPECT_THAT(thread.cpu_freq_indices(), ElementsAre(1u, 6u, 10u, 11u));
   EXPECT_THAT(thread.cpu_freq_ticks(), ElementsAre(200, 30, 100, 60));
+  // Check pid = 5.
+  threads = processes[4].threads();
+  EXPECT_EQ(threads.size(), 1u);
+  EXPECT_EQ(threads[0].tid(), 5);
+  EXPECT_THAT(threads[0].cpu_freq_ticks(), ElementsAre(10, 20));
 
   for (const std::string& path : dirs_to_delete)
     rmdir(path.c_str());
diff --git a/test/trace_processor/index b/test/trace_processor/index
index 255481b..2d7d73e 100644
--- a/test/trace_processor/index
+++ b/test/trace_processor/index
@@ -211,3 +211,6 @@
 
 # Thread time_in_state
 thread_time_in_state.textproto thread_time_in_state.sql thread_time_in_state.out
+
+# Initial display state
+initial_display_state.textproto initial_display_state.sql initial_display_state.out
diff --git a/test/trace_processor/initial_display_state.out b/test/trace_processor/initial_display_state.out
new file mode 100644
index 0000000..a3a19b0
--- /dev/null
+++ b/test/trace_processor/initial_display_state.out
@@ -0,0 +1,3 @@
+"name","ts","value"
+"ScreenState",1,2.000000
+"ScreenState",1000,0.000000
diff --git a/test/trace_processor/initial_display_state.sql b/test/trace_processor/initial_display_state.sql
new file mode 100644
index 0000000..bec5a87
--- /dev/null
+++ b/test/trace_processor/initial_display_state.sql
@@ -0,0 +1,6 @@
+SELECT t.name,
+       c.ts,
+       c.value
+FROM counter_track t
+    JOIN counter c ON t.id = c.track_id
+WHERE t.name = 'ScreenState';
diff --git a/test/trace_processor/initial_display_state.textproto b/test/trace_processor/initial_display_state.textproto
new file mode 100644
index 0000000..31cf253
--- /dev/null
+++ b/test/trace_processor/initial_display_state.textproto
@@ -0,0 +1,19 @@
+packet: {
+  timestamp: 1
+  initial_display_state: {
+    display_state: 2
+    brightness: 0.5
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 1000
+      pid: 1234
+      print {
+        buf: "C|5678|ScreenState|0\n"
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/track_event_typed_args_args.out b/test/trace_processor/track_event_typed_args_args.out
index b61f865..e6d06f7 100644
--- a/test/trace_processor/track_event_typed_args_args.out
+++ b/test/trace_processor/track_event_typed_args_args.out
@@ -1,10 +1,10 @@
 "arg_set_id","flat_key","key","int_value","string_value"
-1,"user_event.action","user_event.action","[NULL]","NewTab"
-2,"legacy_ipc.message_class","legacy_ipc.message_class","[NULL]","CLASS_AUTOMATION"
-2,"legacy_ipc.message_line","legacy_ipc.message_line",10,"[NULL]"
-3,"keyed_service.name","keyed_service.name","[NULL]","MediaRouter"
-4,"latency_info.component_info.component_type","latency_info.component_info[0].component_type","[NULL]","COMPONENT_INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL"
-4,"latency_info.component_info.time_us","latency_info.component_info[0].time_us",1201,"[NULL]"
-4,"latency_info.component_info.time_us","latency_info.component_info[1].time_us",928310,"[NULL]"
-4,"latency_info.is_coalesced","latency_info.is_coalesced",1,"[NULL]"
-4,"latency_info.trace_id","latency_info.trace_id",7,"[NULL]"
+1,"chrome_user_event.action","chrome_user_event.action","[NULL]","NewTab"
+2,"chrome_legacy_ipc.message_class","chrome_legacy_ipc.message_class","[NULL]","CLASS_AUTOMATION"
+2,"chrome_legacy_ipc.message_line","chrome_legacy_ipc.message_line",10,"[NULL]"
+3,"chrome_keyed_service.name","chrome_keyed_service.name","[NULL]","MediaRouter"
+4,"chrome_latency_info.component_info.component_type","chrome_latency_info.component_info[0].component_type","[NULL]","COMPONENT_INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL"
+4,"chrome_latency_info.component_info.time_us","chrome_latency_info.component_info[0].time_us",1201,"[NULL]"
+4,"chrome_latency_info.component_info.time_us","chrome_latency_info.component_info[1].time_us",928310,"[NULL]"
+4,"chrome_latency_info.is_coalesced","chrome_latency_info.is_coalesced",1,"[NULL]"
+4,"chrome_latency_info.trace_id","chrome_latency_info.trace_id",7,"[NULL]"
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index a480b09..a18e05a 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -603,8 +603,8 @@
           tid,
           upid,
           pid,
-          ifnull(thread_track.name, thread.name) as threadName,
-          ifnull(process_track.name, process.name) as processName,
+          thread.name as threadName,
+          process.name as processName,
           total_dur as totalDur,
           ifnull(has_sched, false) as hasSched
         from
@@ -612,9 +612,7 @@
           left join (select utid, count(1), true as has_sched
               from sched group by utid
           ) using(utid)
-          left join process_track using(upid)
           left join process using(upid)
-          left join thread_track using(utid)
           left join (select upid, sum(dur) as total_dur
               from sched join thread using(utid)
               group by upid