Merge "perfetto-ui: Fix canvas limit px"
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index f8d73c9..eb63eee 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -973,10 +973,13 @@
   ]
   sources = [
     "android-core/base/file.cpp",
+    "android-core/base/liblog_symbols.cpp",
     "android-core/base/logging.cpp",
     "android-core/base/stringprintf.cpp",
     "android-core/base/strings.cpp",
     "android-core/base/threads.cpp",
+    "android-core/liblog/fake_log_device.cpp",
+    "android-core/liblog/logger_write.cpp",
     "android-core/libunwindstack/ArmExidx.cpp",
     "android-core/libunwindstack/DwarfCfa.cpp",
     "android-core/libunwindstack/DwarfEhFrameWithHdr.cpp",
@@ -1002,7 +1005,6 @@
     "android-core/libunwindstack/RegsX86_64.cpp",
     "android-core/libunwindstack/Symbols.cpp",
     "android-core/libunwindstack/Unwinder.cpp",
-    "android-core/libunwindstack/tests/LogFake.cpp",
   ]
   if (current_cpu == "x86") {
     sources += [ "android-core/libunwindstack/AsmGetRegsX86.S" ]
@@ -1014,6 +1016,7 @@
     "//gn/standalone:c++11",
     "//gn/standalone:visibility_hidden",
   ]
+  cflags = [ "-DFAKE_LOG_DEVICE=1" ]
   configs += [ "//gn/standalone:c++17" ]
   public_configs = [ ":libunwindstack_config" ]
 }
diff --git a/src/profiling/memory/unwinding.cc b/src/profiling/memory/unwinding.cc
index ad18031..339077b 100644
--- a/src/profiling/memory/unwinding.cc
+++ b/src/profiling/memory/unwinding.cc
@@ -157,6 +157,9 @@
   std::string content;
   if (!base::ReadFileDescriptor(*fd_, &content))
     return false;
+
+  unwindstack::MapInfo* prev_map = nullptr;
+  unwindstack::MapInfo* prev_real_map = nullptr;
   return android::procinfo::ReadMapFileContent(
       &content[0], [&](uint64_t start, uint64_t end, uint16_t flags,
                        uint64_t pgoff, ino_t, const char* name) {
@@ -165,10 +168,12 @@
             strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        unwindstack::MapInfo* prev_map =
-            maps_.empty() ? nullptr : maps_.back().get();
         maps_.emplace_back(
-            new unwindstack::MapInfo(prev_map, start, end, pgoff, flags, name));
+            new unwindstack::MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
+        prev_map = maps_.back().get();
+        if (!prev_map->IsBlank()) {
+          prev_real_map = prev_map;
+        }
       });
 }
 
diff --git a/src/trace_processor/importers/proto/graphics_event_parser.cc b/src/trace_processor/importers/proto/graphics_event_parser.cc
index eb58594..5337e81 100644
--- a/src/trace_processor/importers/proto/graphics_event_parser.cc
+++ b/src/trace_processor/importers/proto/graphics_event_parser.cc
@@ -245,25 +245,6 @@
     snprintf(buffer, sizeof(buffer), "render stage(%zu)", stage_id);
     stage_name = context_->storage->InternString(buffer);
   }
-  // If the slice has a render target handle, we append the hex value of the
-  // handle to the name.  If a debug marker is available, we append the name
-  // of the render target.
-  if (event.has_render_target_handle()) {
-    char buffer[256];
-    base::StringWriter str_writer(buffer, sizeof(buffer));
-    str_writer.AppendString(context_->storage->GetString(stage_name));
-    auto debug_marker_name =
-        FindDebugName(VK_OBJECT_TYPE_FRAMEBUFFER, event.render_target_handle());
-    str_writer.AppendChar('[');
-    if (debug_marker_name.has_value()) {
-      str_writer.AppendString(base::StringView(debug_marker_name.value()));
-    } else {
-      str_writer.AppendLiteral("0x");
-      str_writer.AppendHexInt(event.render_target_handle());
-    }
-    str_writer.AppendChar(']');
-    stage_name = context_->storage->InternString(str_writer.GetStringView());
-  }
   return stage_name;
 }
 
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 2ca1a28..fc46e7b 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -265,7 +265,7 @@
   protos::pbzero::ProcessDescriptor::Decoder decoder(process_descriptor);
   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
       static_cast<uint32_t>(decoder.pid()));
-  if (decoder.has_process_name()) {
+  if (decoder.has_process_name() && decoder.process_name().size) {
     // Don't override system-provided names.
     context_->process_tracker->SetProcessNameIfUnset(
         upid, context_->storage->InternString(decoder.process_name()));
@@ -306,7 +306,7 @@
       static_cast<uint32_t>(decoder.tid()),
       static_cast<uint32_t>(decoder.pid()));
   StringId name_id = kNullStringId;
-  if (decoder.has_thread_name()) {
+  if (decoder.has_thread_name() && decoder.thread_name().size) {
     name_id = context_->storage->InternString(decoder.thread_name());
   } else if (decoder.has_chrome_thread_type()) {
     // TODO(skyostil): Remove parsing for legacy chrome_thread_type field.
@@ -456,6 +456,12 @@
   base::Optional<UniqueTid> utid;
   base::Optional<UniqueTid> upid;
 
+  // All events in legacy JSON require a thread ID, but for some types of events
+  // (e.g. async events or process/global-scoped instants), we don't store it in
+  // the slice/track model. To pass the utid through to the json export, we
+  // store it in an arg.
+  base::Optional<UniqueTid> legacy_passthrough_utid;
+
   // Determine track from track_uuid specified in either TrackEvent or
   // TrackEventDefaults. If a non-default track is not set, we either:
   //   a) fall back to the track specified by the sequence's (or event's) pid +
@@ -481,8 +487,16 @@
     } else {
       auto process_track_row =
           context_->storage->process_track_table().id().IndexOf(track_id);
-      if (process_track_row)
+      if (process_track_row) {
         upid = storage->process_track_table().upid()[*process_track_row];
+        if (sequence_state->state()->pid_and_tid_valid()) {
+          uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
+          uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
+          UniqueTid utid_candidate = procs->UpdateThread(tid, pid);
+          if (storage->thread_table().upid()[utid_candidate] == upid)
+            legacy_passthrough_utid = utid_candidate;
+        }
+      }
     }
   } else if ((!event.has_track_uuid() || !event.has_type()) &&
              (sequence_state->state()->pid_and_tid_valid() ||
@@ -502,12 +516,6 @@
     track_id = track_tracker->GetOrCreateDefaultDescriptorTrack();
   }
 
-  // All events in legacy JSON require a thread ID, but for some types of events
-  // (e.g. async events or process/global-scoped instants), we don't store it in
-  // the slice/track model. To pass the utid through to the json export, we
-  // store it in an arg.
-  base::Optional<UniqueTid> legacy_passthrough_utid;
-
   // TODO(eseckler): Replace phase with type and remove handling of
   // legacy_event.phase() once it is no longer used by producers.
   int32_t phase = 0;
diff --git a/src/trace_processor/sqlite_raw_table.cc b/src/trace_processor/sqlite_raw_table.cc
index 3177a96..af66914 100644
--- a/src/trace_processor/sqlite_raw_table.cc
+++ b/src/trace_processor/sqlite_raw_table.cc
@@ -101,6 +101,8 @@
   // the proto field order is also the order of insertion (which happens to
   // be true but proabably shouldn't be relied on).
   RowMap rm = storage_->arg_table().FilterToRowMap({set_ids.eq(arg_set_id)});
+  if (rm.empty())
+    return;
 
   uint32_t start_row = rm.Get(0);
   using ValueWriter = std::function<void(const Variadic&)>;
diff --git a/src/trace_processor/tables/macros_benchmark.cc b/src/trace_processor/tables/macros_benchmark.cc
index fec67ed..c4ed6e3 100644
--- a/src/trace_processor/tables/macros_benchmark.cc
+++ b/src/trace_processor/tables/macros_benchmark.cc
@@ -359,6 +359,31 @@
 }
 BENCHMARK(BM_TableFilterParentSortedEq)->Apply(TableFilterArgs);
 
+static void BM_TableFilterParentSortedAndOther(benchmark::State& state) {
+  StringPool pool;
+  RootTestTable root(&pool, nullptr);
+
+  uint32_t size = static_cast<uint32_t>(state.range(0));
+
+  for (uint32_t i = 0; i < size; ++i) {
+    // Group the rows into rows of 10. This emulates the behaviour of e.g.
+    // args.
+    RootTestTable::Row row;
+    row.root_sorted = (i / 10) * 10;
+    row.root_non_null = i;
+    root.Insert(row);
+  }
+
+  // We choose to search for the last group as if there is O(n^2), it will
+  // be more easily visible.
+  uint32_t last_group = ((size - 1) / 10) * 10;
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(root.Filter({root.root_sorted().eq(last_group),
+                                          root.root_non_null().eq(size - 1)}));
+  }
+}
+BENCHMARK(BM_TableFilterParentSortedAndOther)->Apply(TableFilterArgs);
+
 static void BM_TableFilterChildSortedEq(benchmark::State& state) {
   StringPool pool;
   RootTestTable root(&pool, nullptr);
diff --git a/test/trace_processor/gpu_render_stages.out b/test/trace_processor/gpu_render_stages.out
index fc94c06..dfeb78d 100644
--- a/test/trace_processor/gpu_render_stages.out
+++ b/test/trace_processor/gpu_render_stages.out
@@ -10,17 +10,17 @@
 "queue 0","queue desc 0",60,5,"stage 0",0,"key1","value1",42,0,0,0,0,0
 "queue 0","queue desc 0",70,5,"stage 0",0,"key1","[NULL]",42,0,0,0,0,0
 "queue 0","queue desc 0",80,5,"stage 2",0,"[NULL]","[NULL]",42,0,0,0,0,0
-"queue 0","queue desc 0",90,5,"stage 0[0x10]",0,"VkCommandBuffer","0x0000000000000030",42,16,32,48,0,0
-"queue 0","queue desc 0",90,5,"stage 0[0x10]",0,"VkFramebuffer","0x0000000000000010",42,16,32,48,0,0
-"queue 0","queue desc 0",90,5,"stage 0[0x10]",0,"VkRenderPass","0x0000000000000020",42,16,32,48,0,0
-"queue 0","queue desc 0",100,5,"stage 0[0x10]",0,"VkCommandBuffer","0x0000000000000010 (command_buffer)",42,16,16,16,0,0
-"queue 0","queue desc 0",100,5,"stage 0[0x10]",0,"VkFramebuffer","0x0000000000000010",42,16,16,16,0,0
-"queue 0","queue desc 0",100,5,"stage 0[0x10]",0,"VkRenderPass","0x0000000000000010",42,16,16,16,0,0
-"queue 0","queue desc 0",110,5,"stage 0[0x10]",0,"VkCommandBuffer","0x0000000000000010 (command_buffer)",42,16,16,16,0,0
-"queue 0","queue desc 0",110,5,"stage 0[0x10]",0,"VkFramebuffer","0x0000000000000010",42,16,16,16,0,0
-"queue 0","queue desc 0",110,5,"stage 0[0x10]",0,"VkRenderPass","0x0000000000000010 (render_pass)",42,16,16,16,0,0
-"queue 0","queue desc 0",120,5,"stage 0[framebuffer]",0,"VkCommandBuffer","0x0000000000000010 (command_buffer)",42,16,16,16,0,0
-"queue 0","queue desc 0",120,5,"stage 0[framebuffer]",0,"VkFramebuffer","0x0000000000000010 (framebuffer)",42,16,16,16,0,0
-"queue 0","queue desc 0",120,5,"stage 0[framebuffer]",0,"VkRenderPass","0x0000000000000010 (render_pass)",42,16,16,16,0,0
-"queue 0","queue desc 0",130,5,"stage 0[renamed_buffer]",0,"VkFramebuffer","0x0000000000000010 (renamed_buffer)",42,16,0,0,0,0
+"queue 0","queue desc 0",90,5,"stage 0",0,"VkCommandBuffer","0x0000000000000030",42,16,32,48,0,0
+"queue 0","queue desc 0",90,5,"stage 0",0,"VkFramebuffer","0x0000000000000010",42,16,32,48,0,0
+"queue 0","queue desc 0",90,5,"stage 0",0,"VkRenderPass","0x0000000000000020",42,16,32,48,0,0
+"queue 0","queue desc 0",100,5,"stage 0",0,"VkCommandBuffer","0x0000000000000010 (command_buffer)",42,16,16,16,0,0
+"queue 0","queue desc 0",100,5,"stage 0",0,"VkFramebuffer","0x0000000000000010",42,16,16,16,0,0
+"queue 0","queue desc 0",100,5,"stage 0",0,"VkRenderPass","0x0000000000000010",42,16,16,16,0,0
+"queue 0","queue desc 0",110,5,"stage 0",0,"VkCommandBuffer","0x0000000000000010 (command_buffer)",42,16,16,16,0,0
+"queue 0","queue desc 0",110,5,"stage 0",0,"VkFramebuffer","0x0000000000000010",42,16,16,16,0,0
+"queue 0","queue desc 0",110,5,"stage 0",0,"VkRenderPass","0x0000000000000010 (render_pass)",42,16,16,16,0,0
+"queue 0","queue desc 0",120,5,"stage 0",0,"VkCommandBuffer","0x0000000000000010 (command_buffer)",42,16,16,16,0,0
+"queue 0","queue desc 0",120,5,"stage 0",0,"VkFramebuffer","0x0000000000000010 (framebuffer)",42,16,16,16,0,0
+"queue 0","queue desc 0",120,5,"stage 0",0,"VkRenderPass","0x0000000000000010 (render_pass)",42,16,16,16,0,0
+"queue 0","queue desc 0",130,5,"stage 0",0,"VkFramebuffer","0x0000000000000010 (renamed_buffer)",42,16,0,0,0,0
 "Unknown GPU Queue ","[NULL]",140,5,"render stage(18446744073709551615)",0,"[NULL]","[NULL]",42,0,0,0,0,1024
diff --git a/test/trace_processor/track_event_tracks_slices.out b/test/trace_processor/track_event_tracks_slices.out
index a031897..d7119df 100644
--- a/test/trace_processor/track_event_tracks_slices.out
+++ b/test/trace_processor/track_event_tracks_slices.out
@@ -2,12 +2,12 @@
 "[NULL]","[NULL]","t1","p1",1000,0,"cat","event1_on_t1",0
 "[NULL]","[NULL]","t2","p1",2000,0,"cat","event1_on_t2",0
 "[NULL]","[NULL]","t2","p1",3000,0,"cat","event2_on_t2",0
-"[NULL]","p1","[NULL]","[NULL]",4000,0,"cat","event1_on_p1",0
-"async","p1","[NULL]","[NULL]",5000,0,"cat","event1_on_async",0
-"async2","p1","[NULL]","[NULL]",5100,100,"cat","event1_on_async2",0
+"[NULL]","p1","[NULL]","[NULL]",4000,0,"cat","event1_on_p1",8
+"async","p1","[NULL]","[NULL]",5000,0,"cat","event1_on_async",8
+"async2","p1","[NULL]","[NULL]",5100,100,"cat","event1_on_async2",3
 "[NULL]","[NULL]","t1","p1",6000,0,"cat","event3_on_t1",0
 "[NULL]","[NULL]","t3","p1",11000,0,"cat","event1_on_t3",0
-"[NULL]","p2","[NULL]","[NULL]",21000,0,"cat","event1_on_p2",0
+"[NULL]","p2","[NULL]","[NULL]",21000,0,"cat","event1_on_p2",7
 "[NULL]","[NULL]","t4","p2",22000,0,"cat","event1_on_t4",0
 "Default Track","[NULL]","[NULL]","[NULL]",30000,0,"cat","event1_on_t1",0
-"[NULL]","p2","[NULL]","[NULL]",31000,0,"cat","event1_on_t1",5
+"[NULL]","p2","[NULL]","[NULL]",31000,0,"cat","event1_on_t1",6
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 1ed1fab..788a0fe 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -132,7 +132,7 @@
     # These dependencies are for libunwindstack, which is used by src/profiling.
     ('buildtools/android-core',
      'https://android.googlesource.com/platform/system/core.git',
-     '860e8682e27eea30cc604b60c9cab83d0b047012', 'all'),
+     '8bf4e29e44098e3232ff646331675fb113064162', 'all'),
     ('buildtools/lzma',
      'https://android.googlesource.com/platform/external/lzma.git',
      '7851dce6f4ca17f5caa1c93a4e0a45686b1d56c3', 'all'),
diff --git a/ui/src/assets/record.scss b/ui/src/assets/record.scss
index b2170bb..d5d0e6a 100644
--- a/ui/src/assets/record.scss
+++ b/ui/src/assets/record.scss
@@ -274,6 +274,7 @@
   transition: opacity 0.25s ease;
   opacity: 0;
   visibility: hidden;
+  overflow: auto;
 
   &:not(.active) {
     max-height: 0;
diff --git a/ui/src/frontend/legacy_trace_viewer.ts b/ui/src/frontend/legacy_trace_viewer.ts
index c55451d..253d6af 100644
--- a/ui/src/frontend/legacy_trace_viewer.ts
+++ b/ui/src/frontend/legacy_trace_viewer.ts
@@ -18,12 +18,48 @@
 
 import {globals} from './globals';
 
-export function isLegacyTrace(fileName: string): boolean {
-  fileName = fileName.toLowerCase();
-  return (
-      fileName.endsWith('.json') || fileName.endsWith('.json.gz') ||
+function readText(blob: Blob): Promise<string> {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader();
+    reader.onload = () => {
+      if (typeof reader.result === 'string') {
+        return resolve(reader.result);
+      }
+    };
+    reader.onerror = err => {
+      reject(err);
+    };
+    reader.readAsText(blob);
+  });
+}
+
+export async function isLegacyTrace(file: File): Promise<boolean> {
+  const fileName = file.name.toLowerCase();
+  if (fileName.endsWith('.json') || fileName.endsWith('.json.gz') ||
       fileName.endsWith('.zip') || fileName.endsWith('.ctrace') ||
-      fileName.endsWith('.html'));
+      fileName.endsWith('.html')) {
+    return true;
+  }
+
+  // Sometimes systrace formatted traces end with '.trace'. This is a
+  // little generic to assume all such traces are systrace format though
+  // so we read the beginning of the file and check to see if is has the
+  // systrace header (several comment lines):
+  if (fileName.endsWith('.trace')) {
+    const header = await readText(file.slice(0, 512));
+    const lines = header.split('\n');
+    let commentCount = 0;
+    for (const line of lines) {
+      if (line.startsWith('#')) {
+        commentCount++;
+      }
+    }
+    if (commentCount > 5) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 export function openFileWithLegacyTraceViewer(file: File) {
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index cfeeec5..a295a75 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -311,12 +311,7 @@
   globals.frontendLocalState.localOnlyMode = false;
 
   if (e.target.dataset['useCatapultLegacyUi'] === '1') {
-    // Switch back to the old catapult UI.
-    if (isLegacyTrace(file.name)) {
-      openFileWithLegacyTraceViewer(file);
-      return;
-    }
-    openInOldUIWithSizeCheck(file);
+    openWithLegacyUi(file);
     return;
   }
 
@@ -343,7 +338,15 @@
   }
 
   globals.dispatch(Actions.openTraceFromFile({file}));
+}
 
+async function openWithLegacyUi(file: File) {
+  // Switch back to the old catapult UI.
+  if (await isLegacyTrace(file)) {
+    openFileWithLegacyTraceViewer(file);
+    return;
+  }
+  openInOldUIWithSizeCheck(file);
 }
 
 function openInOldUIWithSizeCheck(trace: Blob) {