Merge "UI: Add trace title to sidebar and tab title"
diff --git a/Android.bp b/Android.bp
index 2112df5..5230e56 100644
--- a/Android.bp
+++ b/Android.bp
@@ -613,6 +613,11 @@
     "-DGOOGLE_PROTOBUF_NO_RTTI",
     "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
   ],
+  apex_available: [
+    "//apex_available:platform",
+    "com.android.art.debug",
+    "com.android.art.release",
+  ],
 }
 
 // GN: //src/perfetto_cmd:perfetto
@@ -7502,6 +7507,11 @@
     "-DGOOGLE_PROTOBUF_NO_RTTI",
     "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
   ],
+  apex_available: [
+    "//apex_available:platform",
+    "com.android.art.debug",
+    "com.android.art.release",
+  ],
 }
 
 // GN: //:perfetto_unittests
diff --git a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
index 7d76854..317209b 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -795,10 +795,9 @@
   constexpr TrackId thread_time_track{1u};
 
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
+  // Only the begin thread time can be imported into the counter table.
   EXPECT_CALL(*event_, PushCounter(1005000, testing::DoubleEq(2003000),
                                    thread_time_track));
-  EXPECT_CALL(*event_, PushCounter(1028000, testing::DoubleEq(2015000),
-                                   thread_time_track));
   EXPECT_CALL(*slice_,
               Scoped(1005000, track, kNullStringId, kNullStringId, 23000, _))
       .WillOnce(DoAll(InvokeArgument<5>(&inserter), Return(0u)));
@@ -1038,14 +1037,11 @@
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
 
   MockBoundInserter inserter;
+  // Only the begin timestamp counters can be imported into the counter table.
   EXPECT_CALL(*event_, PushCounter(1005000, testing::DoubleEq(2003000),
                                    thread_time_track));
-  EXPECT_CALL(*event_, PushCounter(1028000, testing::DoubleEq(2015000),
-                                   thread_time_track));
   EXPECT_CALL(*event_, PushCounter(1005000, testing::DoubleEq(3010),
                                    thread_instruction_count_track));
-  EXPECT_CALL(*event_, PushCounter(1028000, testing::DoubleEq(3060),
-                                   thread_instruction_count_track));
   EXPECT_CALL(*slice_, Scoped(1005000, thread_1_track, cat_2_3, ev_2, 23000, _))
       .WillOnce(DoAll(InvokeArgument<5>(&inserter), Return(0u)));
   EXPECT_CALL(inserter, AddArg(_, _, Variadic::UnsignedInteger(9999u), _));
@@ -2256,10 +2252,9 @@
   Tokenize();
 
   EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1));
+  // Only the begin thread time can be imported into the counter table.
   EXPECT_CALL(*event_,
               PushCounter(1010000, testing::DoubleEq(2005000), TrackId{1}));
-  EXPECT_CALL(*event_,
-              PushCounter(1033000, testing::DoubleEq(2020000), TrackId{1}));
 
   tables::ThreadTable::Row row(16);
   row.upid = 1u;
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index 29050b3..8a1c4f3 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -16,6 +16,8 @@
 
 #include "src/trace_processor/importers/proto/system_probes_parser.h"
 
+#include <set>
+
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/traced/sys_stats_counters.h"
 #include "perfetto/protozero/proto_decoder.h"
@@ -302,10 +304,10 @@
   protos::pbzero::ProcessStats::Thread::Decoder stats(blob.data, blob.size);
   UniqueTid utid = context_->process_tracker->UpdateThread(
       static_cast<uint32_t>(stats.tid()), pid);
-  auto index_it = stats.cpu_freq_indices();
-  auto tick_it = stats.cpu_freq_ticks();
-  std::map<StringId, uint64_t> total_ticks_cpu;
-  for (; index_it && tick_it; index_it++, tick_it++) {
+  TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+
+  std::set<StringId> cpu_str_ids;
+  for (auto index_it = stats.cpu_freq_indices(); index_it; index_it++) {
     auto freq_index = *index_it;
     if (PERFETTO_UNLIKELY(freq_index == 0 ||
                           freq_index >=
@@ -314,14 +316,30 @@
           stats::thread_time_in_state_unknown_cpu_freq);
       continue;
     }
-    total_ticks_cpu[thread_time_in_state_cpu_str_ids_[freq_index]] += *tick_it;
+    cpu_str_ids.insert(thread_time_in_state_cpu_str_ids_[freq_index]);
   }
 
-  for (auto it : total_ticks_cpu) {
-    TrackId track =
-        context_->track_tracker->InternThreadCounterTrack(it.first, utid);
-    auto ticks = it.second;
-    context_->event_tracker->PushCounter(ts, ticks * ms_per_tick_, track);
+  for (StringId cpu_str_id : cpu_str_ids) {
+    context_->slice_tracker->Scoped(
+        ts, track_id, kNullStringId, cpu_str_id,
+        /* duration */ 0,
+        [&stats, &cpu_str_id, this](ArgsTracker::BoundInserter* args_table) {
+          auto index_it = stats.cpu_freq_indices();
+          auto tick_it = stats.cpu_freq_ticks();
+          for (; index_it && tick_it; index_it++, tick_it++) {
+            auto freq_index = *index_it;
+            if (PERFETTO_UNLIKELY(
+                    freq_index == 0 ||
+                    freq_index >= thread_time_in_state_cpu_str_ids_.size())) {
+              continue;
+            }
+            if (thread_time_in_state_cpu_str_ids_[freq_index] != cpu_str_id)
+              continue;
+            args_table->AddArg(
+                thread_time_in_state_cpu_freq_ids_[*index_it],
+                Variadic::UnsignedInteger(*tick_it * ms_per_tick_));
+          }
+        });
   }
 }
 
@@ -375,18 +393,24 @@
 }
 
 void SystemProbesParser::ParseCpuInfo(ConstBytes blob) {
+  // Frequency index 0 is invalid.
+  StringId invalid_str_id = context_->storage->InternString("invalid");
+  thread_time_in_state_cpu_str_ids_.push_back(invalid_str_id);
+  thread_time_in_state_cpu_freq_ids_.push_back(invalid_str_id);
+
   protos::pbzero::CpuInfo::Decoder packet(blob.data, blob.size);
   uint32_t cpu_index = 0;
-  thread_time_in_state_cpu_str_ids_.push_back(
-      context_->storage->InternString("invalid"));
   for (auto it = packet.cpus(); it; it++) {
     protos::pbzero::CpuInfo::Cpu::Decoder cpu(*it);
-    std::string cpu_index_string =
-        "cpu" + std::to_string(cpu_index) + ".time_in_state";
-    base::StringView cpu_string(cpu_index_string);
+    std::string cpu_string = "time_in_state.cpu" + std::to_string(cpu_index);
+    base::StringView cpu_string_view(cpu_string);
     for (auto freq_it = cpu.frequencies(); freq_it; freq_it++) {
       thread_time_in_state_cpu_str_ids_.push_back(
-          context_->storage->InternString(cpu_string));
+          context_->storage->InternString(cpu_string_view));
+      std::string freq_string = std::to_string(*freq_it);
+      base::StringView freq_string_view(freq_string);
+      thread_time_in_state_cpu_freq_ids_.push_back(
+          context_->storage->InternString(freq_string_view));
     }
     cpu_index++;
   }
diff --git a/src/trace_processor/importers/proto/system_probes_parser.h b/src/trace_processor/importers/proto/system_probes_parser.h
index c9361db..bcaf44a 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.h
+++ b/src/trace_processor/importers/proto/system_probes_parser.h
@@ -69,8 +69,11 @@
 
   uint64_t ms_per_tick_ = 0;
 
-  // Maps CPU frequency indices to CPU strings.
+  // Maps CPU frequency indices to CPU strings: time_in_state.cpuN.
   std::vector<StringId> thread_time_in_state_cpu_str_ids_;
+
+  // Maps CPU frequency indices to frequency strings.
+  std::vector<StringId> thread_time_in_state_cpu_freq_ids_;
 };
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index e432dbb..66880b9 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -479,34 +479,21 @@
       return;
     // When these fields are set, we don't expect TrackDescriptor-based counters
     // for thread time or instruction count for this thread in the trace, so we
-    // intern separate counter tracks based on name + utid.
+    // intern separate counter tracks based on name + utid. Note that we cannot
+    // import the counter values from the end of a complete event, because the
+    // EventTracker expects counters to be pushed in order of their timestamps.
+    // One more reason to switch to split begin/end events.
     if (event_data_->thread_timestamp) {
       TrackId track_id = context_->track_tracker->InternThreadCounterTrack(
           parser_->counter_name_thread_time_id_, *utid_);
       context_->event_tracker->PushCounter(ts_, event_data_->thread_timestamp,
                                            track_id);
-      if (legacy_event_.duration_us() &&
-          legacy_event_.has_thread_duration_us()) {
-        context_->event_tracker->PushCounter(
-            ts_ + (legacy_event_.duration_us() * 1000),
-            event_data_->thread_timestamp +
-                (legacy_event_.thread_duration_us() * 1000),
-            track_id);
-      }
     }
     if (event_data_->thread_instruction_count) {
       TrackId track_id = context_->track_tracker->InternThreadCounterTrack(
           parser_->counter_name_thread_instruction_count_id_, *utid_);
       context_->event_tracker->PushCounter(
           ts_, event_data_->thread_instruction_count, track_id);
-      if (legacy_event_.duration_us() &&
-          legacy_event_.has_thread_instruction_delta()) {
-        context_->event_tracker->PushCounter(
-            ts_ + (legacy_event_.duration_us() * 1000),
-            event_data_->thread_instruction_count +
-                legacy_event_.thread_instruction_delta(),
-            track_id);
-      }
     }
   }
 
@@ -826,7 +813,7 @@
         return;
       // Log error but continue parsing the other args.
       storage_->IncrementStats(stats::track_event_parser_errors);
-      PERFETTO_DLOG("%s", status.c_message());
+      PERFETTO_DLOG("ParseTrackEventArgs error: %s", status.c_message());
     };
 
     for (auto it = event_.debug_annotations(); it; ++it) {
@@ -1401,7 +1388,7 @@
       EventImporter(this, ts, event_data, std::move(blob)).Import();
   if (!status.ok()) {
     context_->storage->IncrementStats(stats::track_event_parser_errors);
-    PERFETTO_DLOG("%s", status.c_message());
+    PERFETTO_DLOG("ParseTrackEvent error: %s", status.c_message());
   }
 }
 
diff --git a/src/trace_processor/metrics/android/android_thread_time_in_state.sql b/src/trace_processor/metrics/android/android_thread_time_in_state.sql
index 1cb65c8..69041a7 100644
--- a/src/trace_processor/metrics/android/android_thread_time_in_state.sql
+++ b/src/trace_processor/metrics/android/android_thread_time_in_state.sql
@@ -17,13 +17,17 @@
 SELECT RUN_METRIC('android/cpu_info.sql');
 SELECT RUN_METRIC('android/process_metadata.sql');
 
-CREATE VIEW android_thread_time_in_state_tracks AS
+CREATE VIEW android_thread_time_in_state_raw AS
 SELECT
-  id AS track_id,
   utid,
-  cast(REPLACE(REPLACE(name, '.time_in_state', ''), 'cpu', '') AS int) AS cpu
-FROM thread_counter_track
-WHERE name LIKE "cpu%.time_in_state";
+  CAST(SUBSTR(slices.name, 18) AS int) AS cpu,
+  key AS freq,
+  MAX(int_value) - MIN(int_value) runtime_ms
+FROM slices
+JOIN thread_track ON (slices.track_id = thread_track.id)
+JOIN args USING (arg_set_id)
+WHERE slices.name LIKE "time_in_state.%"
+GROUP by 1, 2, 3;
 
 CREATE TABLE android_thread_time_in_state_counters AS
 SELECT
@@ -40,9 +44,8 @@
       END
     FROM core_layout_type
   ) AS core_type,
-  CAST((MAX(counter.value) - MIN(counter.value)) AS int) runtime_ms
-FROM counter
-JOIN android_thread_time_in_state_tracks USING (track_id)
+  SUM(runtime_ms) runtime_ms
+FROM android_thread_time_in_state_raw
 GROUP BY 1, 2
 HAVING runtime_ms > 0;
 
diff --git a/test/metrics/android_thread_time_in_state.out b/test/metrics/android_thread_time_in_state.out
index f4d7af5..61f7493 100644
--- a/test/metrics/android_thread_time_in_state.out
+++ b/test/metrics/android_thread_time_in_state.out
@@ -2,12 +2,12 @@
   processes {
     metrics_by_core_type {
       core_type: "unknown"
-      runtime_ms: 10
+      runtime_ms: 20
     }
     threads {
       metrics_by_core_type {
         core_type: "unknown"
-        runtime_ms: 10
+        runtime_ms: 20
       }
     }
   }
diff --git a/test/trace_processor/thread_time_in_state.out b/test/trace_processor/thread_time_in_state.out
index 7d88686..51aeb7a 100644
--- a/test/trace_processor/thread_time_in_state.out
+++ b/test/trace_processor/thread_time_in_state.out
@@ -1,8 +1,11 @@
-"name","tid","ts","value"
-"cpu0.time_in_state",5,2,20.000000
-"cpu0.time_in_state",5,3,30.000000
-"cpu2.time_in_state",7,3,10.000000
-"cpu2.time_in_state",11,3,10.000000
-"cpu2.time_in_state",12,3,10.000000
-"cpu2.time_in_state",11,4,20.000000
-"cpu2.time_in_state",12,4,20.000000
+"ts","tid","cpu","freq","time_ms"
+2,5,0,100,10
+2,5,0,200,10
+3,5,0,100,20
+3,5,0,200,10
+3,7,2,1000,10
+3,11,2,2000,10
+3,12,2,2000,10
+4,5,0,200,20
+4,11,2,2000,20
+4,12,2,2000,20
diff --git a/test/trace_processor/thread_time_in_state.sql b/test/trace_processor/thread_time_in_state.sql
index ac87c0f..5f2574d 100644
--- a/test/trace_processor/thread_time_in_state.sql
+++ b/test/trace_processor/thread_time_in_state.sql
@@ -1,9 +1,12 @@
 SELECT
-t.name,
-tid,
-c.ts,
-c.value
-FROM counter c
-JOIN thread_counter_track t ON c.track_id = t.id
+  ts,
+  tid,
+  CAST(SUBSTR(slices.name, 18) AS int) AS cpu,
+  CAST(key AS int) AS freq,
+  int_value AS time_ms
+FROM slices
+JOIN thread_track ON (slices.track_id = thread_track.id)
 JOIN thread USING (utid)
-ORDER BY c.ts;
+JOIN args USING (arg_set_id)
+WHERE slices.name LIKE "time_in_state.%"
+ORDER BY ts, tid, cpu, key;
diff --git a/test/trace_processor/thread_time_in_state.textproto b/test/trace_processor/thread_time_in_state.textproto
index 1d6c0f2..c76fe62 100644
--- a/test/trace_processor/thread_time_in_state.textproto
+++ b/test/trace_processor/thread_time_in_state.textproto
@@ -55,6 +55,15 @@
   timestamp: 4
   process_stats {
     processes {
+      pid: 5
+      threads {
+        tid: 5
+        # cpu_freq_indices: 1 was skipped because it did not change.
+        cpu_freq_indices: 2
+        cpu_freq_ticks: 2
+      }
+    }
+    processes {
       pid: 11
       threads {
         tid: 11
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 5567f04..099c384 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -178,6 +178,18 @@
     'trace_processor_shell': [
       ('stl', 'libc++_static'),
     ],
+    'libperfetto_client_experimental': [
+      ('apex_available', {
+        '//apex_available:platform',
+        'com.android.art.debug',
+        'com.android.art.release'}),
+    ],
+    'perfetto_trace_protos': [
+      ('apex_available', {
+        '//apex_available:platform',
+        'com.android.art.debug',
+        'com.android.art.release'}),
+    ],
 }
 
 
@@ -367,6 +379,7 @@
     self.dist = dict()
     self.strip = dict()
     self.data = set()
+    self.apex_available = set()
     # The genrule_XXX below are properties that must to be propagated back
     # on the module(s) that depend on the genrule.
     self.genrule_headers = set()
@@ -400,6 +413,7 @@
     self._output_field(output, 'tool_files')
     self._output_field(output, 'data')
     self._output_field(output, 'stl')
+    self._output_field(output, 'apex_available')
 
     target_out = []
     self._output_field(target_out, 'android')