Merge "trace_processor: Add execution time to raw_query.proto"
diff --git a/include/perfetto/base/file_utils.h b/include/perfetto/base/file_utils.h
index 3126b7c..1efb450 100644
--- a/include/perfetto/base/file_utils.h
+++ b/include/perfetto/base/file_utils.h
@@ -17,9 +17,9 @@
 #ifndef INCLUDE_PERFETTO_BASE_FILE_UTILS_H_
 #define INCLUDE_PERFETTO_BASE_FILE_UTILS_H_
 
-#include <string>
+#include <stddef.h>
 
-#include "perfetto/base/build_config.h"
+#include <string>
 
 namespace perfetto {
 namespace base {
@@ -27,8 +27,6 @@
 bool ReadFileDescriptor(int fd, std::string* out);
 bool ReadFile(const std::string& path, std::string* out);
 
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-
 // Call write until all data is written or an error is detected.
 //
 // man 2 write:
@@ -38,8 +36,6 @@
 //   succeeds, and returns the number of bytes written.
 ssize_t WriteAll(int fd, const void* buf, size_t count);
 
-#endif
-
 }  // namespace base
 }  // namespace perfetto
 
diff --git a/protos/perfetto/trace/chrome/chrome_trace_event.proto b/protos/perfetto/trace/chrome/chrome_trace_event.proto
index a004d92..7911f42 100644
--- a/protos/perfetto/trace/chrome/chrome_trace_event.proto
+++ b/protos/perfetto/trace/chrome/chrome_trace_event.proto
@@ -41,7 +41,7 @@
 
     // Takes precedence over |name| if set,
     // and is an index into |string_table|.
-    optional int32 name_index = 9;
+    optional uint32 name_index = 9;
   }
 
   optional string name = 1;
@@ -63,8 +63,8 @@
   // Takes precedence over respectively |name| and
   // |category_group_name_index| if set,
   // and are indices into |string_table|.
-  optional int32 name_index = 15;
-  optional int32 category_group_name_index = 16;
+  optional uint32 name_index = 15;
+  optional uint32 category_group_name_index = 16;
 }
 
 message ChromeMetadata {
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index 89440b1..44b27f7 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -16,12 +16,15 @@
 
 #include <sys/stat.h>
 
+#include "perfetto/base/build_config.h"
 #include "perfetto/base/file_utils.h"
-
 #include "perfetto/base/logging.h"
 #include "perfetto/base/scoped_file.h"
+
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <unistd.h>
+#else
+#include <corecrt_io.h>
 #endif
 
 namespace perfetto {
@@ -63,8 +66,6 @@
   return ReadFileDescriptor(*fd, out);
 }
 
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-
 ssize_t WriteAll(int fd, const void* buf, size_t count) {
   size_t written = 0;
   while (written < count) {
@@ -79,7 +80,5 @@
   return static_cast<ssize_t>(written);
 }
 
-#endif
-
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/base/unix_socket.cc b/src/base/unix_socket.cc
index fb4f5ff..bb56248 100644
--- a/src/base/unix_socket.cc
+++ b/src/base/unix_socket.cc
@@ -65,7 +65,8 @@
 #endif
 
 void ShiftMsgHdr(size_t n, struct msghdr* msg) {
-  for (size_t i = 0; i < msg->msg_iovlen; ++i) {
+  using LenType = decltype(msg->msg_iovlen);  // Mac and Linux don't agree.
+  for (LenType i = 0; i < msg->msg_iovlen; ++i) {
     struct iovec* vec = &msg->msg_iov[i];
     if (n < vec->iov_len) {
       // We sent a part of this iovec.
diff --git a/src/base/unix_socket_unittest.cc b/src/base/unix_socket_unittest.cc
index 06dd63a..531f1e4 100644
--- a/src/base/unix_socket_unittest.cc
+++ b/src/base/unix_socket_unittest.cc
@@ -16,6 +16,7 @@
 
 #include "perfetto/base/unix_socket.h"
 
+#include <signal.h>
 #include <sys/mman.h>
 
 #include <list>
diff --git a/src/profiling/memory/unwinding.cc b/src/profiling/memory/unwinding.cc
index 8ed2780..f43743b 100644
--- a/src/profiling/memory/unwinding.cc
+++ b/src/profiling/memory/unwinding.cc
@@ -145,7 +145,7 @@
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
         maps_.push_back(
-            new unwindstack::MapInfo(start, end, pgoff, flags, name));
+            new unwindstack::MapInfo(nullptr, start, end, pgoff, flags, name));
       });
 }
 
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 7770631..1efaf33 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -163,7 +163,7 @@
   # These dependencies are for libunwindstack, which is used by src/profiling.
   ('buildtools/android-core',
    'https://android.googlesource.com/platform/system/core.git',
-   'ec004eb1b376d2d9008829a25f47ac3fcfd728ab',
+   'd3a7ddcf8dd97e162d7d4a9b3f87b4f1ef797d5f',
    'all'
   ),
 
@@ -175,7 +175,7 @@
 
   ('buildtools/bionic',
    'https://android.googlesource.com/platform/bionic.git',
-   '3fd45bba4857fdbf320b6e89d2ae0569d9463bf5',
+   '4b7c5cca7fbd0330cdfef41c97f1401824e78fba',
    'all'
   ),
 
diff --git a/ui/src/assets/perfetto.scss b/ui/src/assets/perfetto.scss
index cac3d75..8ef1161 100644
--- a/ui/src/assets/perfetto.scss
+++ b/ui/src/assets/perfetto.scss
@@ -312,6 +312,19 @@
   height: 25px;
 }
 
+header.overview {
+  display: flex;
+  justify-content: space-between;
+}
+
+.query-error {
+  user-select: text;
+}
+
+span.code {
+  user-select: text;
+}
+
 .text-column {
   font-size: 115%;
   // 2-3 alphabets per line is comfortable for reading.
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 98cc5e4..e68d26c 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -199,9 +199,13 @@
       }));
     }
 
-    const threadQuery = await engine.query(
-        'select upid, utid, tid, thread.name, max(slices.depth) ' +
-        'from thread inner join slices using(utid) group by utid');
+    const threadQuery = await engine.query(`
+      select upid, utid, tid, thread.name, depth
+      from thread inner join (
+        select utid, max(slices.depth) as depth
+        from slices
+        group by utid
+      ) using(utid)`);
     for (let i = 0; i < threadQuery.numRecords; i++) {
       const upid = threadQuery.columns[0].longValues![i];
       const utid = threadQuery.columns[1].longValues![i];
diff --git a/ui/src/frontend/clipboard.ts b/ui/src/frontend/clipboard.ts
new file mode 100644
index 0000000..28d70be
--- /dev/null
+++ b/ui/src/frontend/clipboard.ts
@@ -0,0 +1,23 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+export async function copyToClipboard(text: string): Promise<void> {
+  try {
+    // TODO(hjd): Fix typescript type for navigator.
+    // tslint:disable-next-line no-any
+    await(navigator as any).clipboard.writeText(text);
+  } catch (err) {
+    console.error(`Failed to copy "${text}" to clipboard: ${err}`);
+  }
+}
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index 79d8417..21e295e 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -14,21 +14,12 @@
 
 import * as m from 'mithril';
 
+import {copyToClipboard} from './clipboard';
 import {createPage} from './pages';
 
 const RECORD_COMMAND_LINE =
     'echo CgYIgKAGIAESIwohCgxsaW51eC5mdHJhY2UQAKIGDhIFc2NoZWQSBWlucHV0GJBOMh0KFnBlcmZldHRvLnRyYWNlZF9wcm9iZXMQgCAYBEAASAA= | base64 --decode | adb shell "perfetto -c - -o /data/misc/perfetto-traces/trace" && adb pull /data/misc/perfetto-traces/trace /tmp/trace';
 
-async function copyToClipboard(text: string): Promise<void> {
-  try {
-    // TODO(hjd): Fix typescript type for navigator.
-    // tslint:disable-next-line no-any
-    await(navigator as any).clipboard.writeText(text);
-  } catch (err) {
-    console.error(`Failed to copy "${text}" to clipboard: ${err}`);
-  }
-}
-
 interface CodeSampleAttrs {
   text: string;
 }
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index adca962..d89bd5d 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -17,6 +17,7 @@
 import {QueryResponse} from '../common/queries';
 import {TimeSpan} from '../common/time';
 
+import {copyToClipboard} from './clipboard';
 import {FlameGraphPanel} from './flame_graph_panel';
 import {globals} from './globals';
 import {HeaderPanel} from './header_panel';
@@ -54,8 +55,27 @@
     return m(
         'div',
         m('header.overview',
-          `Query result - ${Math.round(resp.durationMs)} ms`,
-          m('span.code', resp.query)),
+          m('span',
+            `Query result - ${Math.round(resp.durationMs)} ms`,
+            m('span.code', resp.query)),
+          resp.error ? null :
+                       m('button',
+                         {
+                           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')),
         resp.error ?
             m('.query-error', `SQL error: ${resp.error}`) :
             m('table.query-table', m('thead', header), m('tbody', rows)));