Snap for 6357634 from 5bae2e6212a1b912472acf1ceca310c1422e98d1 to sdk-release

Change-Id: I362f060f743929fce0d9fb8de1a476273f20d66f
diff --git a/src/profiling/memory/malloc_hooks.cc b/src/profiling/memory/malloc_hooks.cc
index 1c9799d..62910bb 100644
--- a/src/profiling/memory/malloc_hooks.cc
+++ b/src/profiling/memory/malloc_hooks.cc
@@ -220,36 +220,21 @@
   return prop_value;
 }
 
-bool ShouldForkPrivateDaemon() {
-  std::string build_type = ReadSystemProperty("ro.build.type");
-  if (build_type.empty()) {
-    PERFETTO_ELOG(
-        "Cannot determine platform build type, proceeding in fork mode "
-        "profiling.");
-    return true;
-  }
-
-  // On development builds, we support both modes of profiling, depending on a
-  // system property.
-  if (build_type == "userdebug" || build_type == "eng") {
-    // Note: if renaming the property, also update system_property.cc
-    std::string mode = ReadSystemProperty("heapprofd.userdebug.mode");
-    return mode == "fork";
-  }
-
-  // User/other builds - always fork private profiler.
-  return true;
+bool ForceForkPrivateDaemon() {
+  // Note: if renaming the property, also update system_property.cc
+  std::string mode = ReadSystemProperty("heapprofd.userdebug.mode");
+  return mode == "fork";
 }
 
 std::shared_ptr<perfetto::profiling::Client> CreateClientForCentralDaemon(
     UnhookedAllocator<perfetto::profiling::Client> unhooked_allocator) {
-  PERFETTO_DLOG("Constructing client for central daemon.");
+  PERFETTO_LOG("Constructing client for central daemon.");
   using perfetto::profiling::Client;
 
   perfetto::base::Optional<perfetto::base::UnixSocketRaw> sock =
       Client::ConnectToHeapprofd(perfetto::profiling::kHeapprofdSocketFile);
   if (!sock) {
-    PERFETTO_ELOG("Failed to connect to %s.",
+    PERFETTO_ELOG("Failed to connect to %s. This is benign on user builds.",
                   perfetto::profiling::kHeapprofdSocketFile);
     return nullptr;
   }
@@ -259,7 +244,7 @@
 
 std::shared_ptr<perfetto::profiling::Client> CreateClientAndPrivateDaemon(
     UnhookedAllocator<perfetto::profiling::Client> unhooked_allocator) {
-  PERFETTO_DLOG("Setting up fork mode profiling.");
+  PERFETTO_LOG("Setting up fork mode profiling.");
   perfetto::base::UnixSocketRaw parent_sock;
   perfetto::base::UnixSocketRaw child_sock;
   std::tie(parent_sock, child_sock) = perfetto::base::UnixSocketRaw::CreatePair(
@@ -405,10 +390,11 @@
 
   // These factory functions use heap objects, so we need to run them without
   // the spinlock held.
-  std::shared_ptr<Client> client =
-      ShouldForkPrivateDaemon()
-          ? CreateClientAndPrivateDaemon(unhooked_allocator)
-          : CreateClientForCentralDaemon(unhooked_allocator);
+  std::shared_ptr<Client> client;
+  if (!ForceForkPrivateDaemon())
+    client = CreateClientForCentralDaemon(unhooked_allocator);
+  if (!client) 
+    client = CreateClientAndPrivateDaemon(unhooked_allocator);
 
   if (!client) {
     PERFETTO_LOG("%s: heapprofd_client not initialized, not installing hooks.",
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index fa53c36..144b804 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -390,6 +390,7 @@
         events.insert(GroupAndName("kmem", "rss_stat"));
         events.insert(GroupAndName("kmem", "ion_heap_grow"));
         events.insert(GroupAndName("kmem", "ion_heap_shrink"));
+        events.insert(GroupAndName("mm_event", "mm_event_record"));
         continue;
       }
     }
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index de9142a..6114874 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -262,6 +262,9 @@
         &:hover {
             background-color: hsl(214, 22%, 90%);
         }
+        &[clickable] {
+          cursor: pointer;
+        }
     }
 }
 
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index b528f87..27d52c9 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -13,10 +13,11 @@
 // limitations under the License.
 
 import * as m from 'mithril';
+import {Row} from 'src/common/protos';
 
 import {Actions} from '../common/actions';
 import {QueryResponse} from '../common/queries';
-import {TimeSpan} from '../common/time';
+import {fromNs, TimeSpan} from '../common/time';
 
 import {copyToClipboard} from './clipboard';
 import {TRACK_SHELL_WIDTH} from './css_constants';
@@ -28,6 +29,10 @@
 import {PanAndZoomHandler} from './pan_and_zoom_handler';
 import {Panel} from './panel';
 import {AnyAttrsVnode, PanelContainer} from './panel_container';
+import {
+  horizontalScrollAndZoomToRange,
+  verticalScrollToTrack
+} from './scroll_helper';
 import {TickmarkPanel} from './tickmark_panel';
 import {TimeAxisPanel} from './time_axis_panel';
 import {computeZoom} from './time_scale';
@@ -39,12 +44,83 @@
 
 const SIDEBAR_WIDTH = 256;
 
+interface QueryTableRowAttrs {
+  row: Row;
+  columns: string[];
+}
+
+class QueryTableRow implements m.ClassComponent<QueryTableRowAttrs> {
+  static columnsContainsSliceLocation(columns: string[]) {
+    const requiredColumns = ['ts', 'dur', 'track_id'];
+    for (const col of requiredColumns) {
+      if (!columns.includes(col)) return false;
+    }
+    return true;
+  }
+
+  static findUiTrackId(traceTrackId: number) {
+    for (const [uiTrackId, trackState] of Object.entries(
+             globals.state.tracks)) {
+      const config = trackState.config as {trackId: number};
+      if (config.trackId === traceTrackId) return uiTrackId;
+    }
+    return null;
+  }
+
+  static rowOnClickHandler(event: Event, row: Row) {
+    // If the click bubbles up to the pan and zoom handler that will deselect
+    // the slice.
+    event.stopPropagation();
+
+    const sliceStart = fromNs(row.ts as number);
+    // row.dur can be negative. Clamp to 1ns.
+    const sliceDur = fromNs(Math.max(row.dur as number, 1));
+    const sliceEnd = sliceStart + sliceDur;
+    const trackId = row.track_id as number;
+    const uiTrackId = this.findUiTrackId(trackId);
+    if (uiTrackId === null) return;
+    verticalScrollToTrack(uiTrackId, true);
+    horizontalScrollAndZoomToRange(sliceStart, sliceEnd);
+    const sliceId = row.slice_id as number | undefined;
+    if (sliceId !== undefined) {
+      globals.makeSelection(
+          Actions.selectChromeSlice({id: sliceId, trackId: uiTrackId}));
+    }
+  }
+
+  view(vnode: m.Vnode<QueryTableRowAttrs>) {
+    const cells = [];
+    const {row, columns} = vnode.attrs;
+    for (const col of columns) {
+      cells.push(m('td', row[col]));
+    }
+    const containsSliceLocation =
+        QueryTableRow.columnsContainsSliceLocation(columns);
+    const maybeOnClick = containsSliceLocation ?
+        (e: Event) => QueryTableRow.rowOnClickHandler(e, row) :
+        null;
+    return m(
+        'tr',
+        {onclick: maybeOnClick, 'clickable': containsSliceLocation},
+        cells);
+  }
+}
+
 class QueryTable extends Panel {
+  private previousResponse?: QueryResponse;
+
+  onbeforeupdate() {
+    const resp = globals.queryResults.get('command') as QueryResponse;
+    const res = resp !== this.previousResponse;
+    return res;
+  }
+
   view() {
     const resp = globals.queryResults.get('command') as QueryResponse;
     if (resp === undefined) {
       return m('');
     }
+    this.previousResponse = resp;
     const cols = [];
     for (const col of resp.columns) {
       cols.push(m('td', col));
@@ -53,43 +129,43 @@
 
     const rows = [];
     for (let i = 0; i < resp.rows.length; i++) {
-      const cells = [];
-      for (const col of resp.columns) {
-        cells.push(m('td', resp.rows[i][col]));
-      }
-      rows.push(m('tr', cells));
+      rows.push(m(QueryTableRow, {row: resp.rows[i], columns: resp.columns}));
     }
+
     return m(
         'div',
-        m('header.overview',
-          `Query result - ${Math.round(resp.durationMs)} ms`,
-          m('span.code', resp.query),
-          resp.error ? null :
-                       m('button.query-ctrl',
-                         {
-                           onclick: () => {
-                             const lines: string[][] = [];
-                             lines.push(resp.columns);
-                             for (const row of resp.rows) {
-                               const line = [];
-                               for (const col of resp.columns) {
-                                 line.push(row[col].toString());
-                               }
-                               lines.push(line);
-                             }
-                             copyToClipboard(
-                                 lines.map(line => line.join('\t')).join('\n'));
-                           },
-                         },
-                         'Copy as .tsv'),
-          m('button.query-ctrl',
-            {
-              onclick: () => {
-                globals.queryResults.delete('command');
-                globals.rafScheduler.scheduleFullRedraw();
-              }
-            },
-            'Close'), ),
+        m(
+            'header.overview',
+            `Query result - ${Math.round(resp.durationMs)} ms`,
+            m('span.code', resp.query),
+            resp.error ?
+                null :
+                m('button.query-ctrl',
+                  {
+                    onclick: () => {
+                      const lines: string[][] = [];
+                      lines.push(resp.columns);
+                      for (const row of resp.rows) {
+                        const line = [];
+                        for (const col of resp.columns) {
+                          line.push(row[col].toString());
+                        }
+                        lines.push(line);
+                      }
+                      copyToClipboard(
+                          lines.map(line => line.join('\t')).join('\n'));
+                    },
+                  },
+                  'Copy as .tsv'),
+            m('button.query-ctrl',
+              {
+                onclick: () => {
+                  globals.queryResults.delete('command');
+                  globals.rafScheduler.scheduleFullRedraw();
+                }
+              },
+              'Close'),
+            ),
         resp.error ?
             m('.query-error', `SQL error: ${resp.error}`) :
             m('table.query-table', m('thead', header), m('tbody', rows)));