Correctly handle partial writes.

Change-Id: Ib2884c37013fd8e811e3cb1f72c74804fac3da8f
diff --git a/include/perfetto/base/file_utils.h b/include/perfetto/base/file_utils.h
index e23add1..b2cfbe2 100644
--- a/include/perfetto/base/file_utils.h
+++ b/include/perfetto/base/file_utils.h
@@ -25,6 +25,15 @@
 bool ReadFileDescriptor(int fd, std::string* out);
 bool ReadFile(const std::string& path, std::string* out);
 
+// Call write until all data is written or an error is detected.
+//
+// man 2 write:
+//   If a write() is interrupted by a signal handler before any bytes are
+//   written, then the call fails with the error EINTR; if it is
+//   interrupted after at least one byte has been written, the call
+//   succeeds, and returns the number of bytes written.
+ssize_t WriteAll(int fd, const void* buf, size_t count);
+
 }  // namespace base
 }  // namespace perfetto
 
diff --git a/src/base/debug_crash_stack_trace.cc b/src/base/debug_crash_stack_trace.cc
index c9cc762..3220d9d 100644
--- a/src/base/debug_crash_stack_trace.cc
+++ b/src/base/debug_crash_stack_trace.cc
@@ -28,6 +28,7 @@
 #include <unwind.h>
 
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
 
 // Some glibc headers hit this when using signals.
 #pragma GCC diagnostic push
@@ -66,7 +67,7 @@
 
 template <typename T>
 void Print(const T& str) {
-  write(STDERR_FILENO, str, sizeof(str));
+  perfetto::base::WriteAll(STDERR_FILENO, str, sizeof(str));
 }
 
 template <typename T>
@@ -74,7 +75,7 @@
   for (unsigned i = 0; i < sizeof(n) * 8; i += 4) {
     char nibble = static_cast<char>(n >> (sizeof(n) * 8 - i - 4)) & 0x0F;
     char c = (nibble < 10) ? '0' + nibble : 'A' + nibble - 10;
-    write(STDERR_FILENO, &c, 1);
+    perfetto::base::WriteAll(STDERR_FILENO, &c, 1);
   }
 }
 
@@ -198,14 +199,16 @@
         // might be moved.
         g_demangled_name = demangled;
       }
-      write(STDERR_FILENO, sym.sym_name, strlen(sym.sym_name));
+      perfetto::base::WriteAll(STDERR_FILENO, sym.sym_name,
+                               strlen(sym.sym_name));
     } else {
       Print("0x");
       PrintHex(frames[i]);
     }
     if (sym.file_name[0]) {
       Print("\n     ");
-      write(STDERR_FILENO, sym.file_name, strlen(sym.file_name));
+      perfetto::base::WriteAll(STDERR_FILENO, sym.file_name,
+                               strlen(sym.file_name));
     }
     Print("\n");
   }
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index 6487dbd..89440b1 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -20,6 +20,9 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/scoped_file.h"
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <unistd.h>
+#endif
 
 namespace perfetto {
 namespace base {
@@ -60,5 +63,23 @@
   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) {
+    ssize_t wr = PERFETTO_EINTR(
+        write(fd, static_cast<const char*>(buf) + written, count - written));
+    if (wr == 0)
+      break;
+    if (wr < 0)
+      return wr;
+    written += static_cast<size_t>(wr);
+  }
+  return static_cast<ssize_t>(written);
+}
+
+#endif
+
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/base/metatrace.cc b/src/base/metatrace.cc
index 045d6fb..1585e43 100644
--- a/src/base/metatrace.cc
+++ b/src/base/metatrace.cc
@@ -20,6 +20,7 @@
 #include <stdlib.h>
 
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
 #include "perfetto/base/time.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
@@ -49,7 +50,7 @@
                     "{\"ts\": %f, \"cat\": \"PERF\", \"ph\": \"%c\", \"name\": "
                     "\"%s\", \"pid\": %zu},\n",
                     GetWallTimeNs().count() / 1000.0, type, evt_name, cpu);
-  ignore_result(write(fd, json, static_cast<size_t>(len)));
+  ignore_result(WriteAll(fd, json, static_cast<size_t>(len)));
 }
 
 }  // namespace base
diff --git a/src/base/task_runner_unittest.cc b/src/base/task_runner_unittest.cc
index 19a3dd5..0bde467 100644
--- a/src/base/task_runner_unittest.cc
+++ b/src/base/task_runner_unittest.cc
@@ -27,6 +27,8 @@
 
 #include <thread>
 
+#include "perfetto/base/file_utils.h"
+
 namespace perfetto {
 namespace base {
 namespace {
@@ -62,7 +64,7 @@
 
   void Write() {
     const char b = '?';
-    PERFETTO_DCHECK(write(write_fd.get(), &b, 1) == 1);
+    PERFETTO_DCHECK(WriteAll(write_fd.get(), &b, 1) == 1);
   }
 
   ScopedFile read_fd;
diff --git a/src/base/unix_socket_unittest.cc b/src/base/unix_socket_unittest.cc
index 887b9ec..06dd63a 100644
--- a/src/base/unix_socket_unittest.cc
+++ b/src/base/unix_socket_unittest.cc
@@ -24,6 +24,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/base/temp_file.h"
 #include "perfetto/base/utils.h"
@@ -351,7 +352,7 @@
     auto srv = UnixSocket::Listen(kSocketName, &event_listener_, &task_runner_);
     ASSERT_TRUE(srv->is_listening());
     // Signal the other process that it can connect.
-    ASSERT_EQ(1, PERFETTO_EINTR(write(pipes[1], ".", 1)));
+    ASSERT_EQ(1, base::WriteAll(pipes[1], ".", 1));
     auto checkpoint = task_runner_.CreateCheckpoint("change_seen_by_server");
     EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
         .WillOnce(Invoke(
diff --git a/src/base/utils_unittest.cc b/src/base/utils_unittest.cc
index 459b420..1f556d6 100644
--- a/src/base/utils_unittest.cc
+++ b/src/base/utils_unittest.cc
@@ -23,6 +23,8 @@
 
 #include "gtest/gtest.h"
 
+#include "perfetto/base/file_utils.h"
+
 namespace perfetto {
 namespace base {
 namespace {
@@ -79,7 +81,7 @@
   if (pid == 0 /* child */) {
     usleep(5000);
     kill(parent_pid, SIGUSR2);
-    ignore_result(write(pipe_fd[1], "foo\0", 4));
+    ignore_result(WriteAll(pipe_fd[1], "foo\0", 4));
     _exit(0);
   }
 
diff --git a/src/ipc/client_impl_unittest.cc b/src/ipc/client_impl_unittest.cc
index 050fcca..61c7768 100644
--- a/src/ipc/client_impl_unittest.cc
+++ b/src/ipc/client_impl_unittest.cc
@@ -23,6 +23,7 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "perfetto/base/file_utils.h"
 #include "perfetto/base/temp_file.h"
 #include "perfetto/base/unix_socket.h"
 #include "perfetto/base/utils.h"
@@ -349,7 +350,8 @@
 
   base::TempFile tx_file = base::TempFile::CreateUnlinked();
   static constexpr char kFileContent[] = "shared file";
-  base::ignore_result(write(tx_file.fd(), kFileContent, sizeof(kFileContent)));
+  ASSERT_EQ(base::WriteAll(tx_file.fd(), kFileContent, sizeof(kFileContent)),
+            sizeof(kFileContent));
   host_->next_reply_fd = tx_file.fd();
 
   EXPECT_CALL(*host_method, OnInvoke(_, _))
@@ -393,7 +395,8 @@
 
   base::TempFile tx_file = base::TempFile::CreateUnlinked();
   static constexpr char kFileContent[] = "shared file";
-  base::ignore_result(write(tx_file.fd(), kFileContent, sizeof(kFileContent)));
+  ASSERT_EQ(base::WriteAll(tx_file.fd(), kFileContent, sizeof(kFileContent)),
+            sizeof(kFileContent));
   EXPECT_CALL(*host_method, OnInvoke(_, _))
       .WillOnce(Invoke(
           [](const Frame::InvokeMethod&, Frame::InvokeMethodReply* reply) {
diff --git a/src/ipc/host_impl_unittest.cc b/src/ipc/host_impl_unittest.cc
index 26b56c8..8247935 100644
--- a/src/ipc/host_impl_unittest.cc
+++ b/src/ipc/host_impl_unittest.cc
@@ -20,6 +20,7 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "perfetto/base/file_utils.h"
 #include "perfetto/base/scoped_file.h"
 #include "perfetto/base/temp_file.h"
 #include "perfetto/base/unix_socket.h"
@@ -330,7 +331,8 @@
   cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
   auto on_reply_sent = task_runner_->CreateCheckpoint("on_reply_sent");
   base::TempFile tx_file = base::TempFile::CreateUnlinked();
-  base::ignore_result(write(tx_file.fd(), kFileContent, sizeof(kFileContent)));
+  ASSERT_EQ(base::WriteAll(tx_file.fd(), kFileContent, sizeof(kFileContent)),
+            sizeof(kFileContent));
   EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
       .WillOnce(Invoke([on_reply_sent, &tx_file](const RequestProto&,
                                                  DeferredBase* reply) {
@@ -370,7 +372,8 @@
   static constexpr char kFileContent[] = "shared file";
   RequestProto req_args;
   base::TempFile tx_file = base::TempFile::CreateUnlinked();
-  base::ignore_result(write(tx_file.fd(), kFileContent, sizeof(kFileContent)));
+  ASSERT_EQ(base::WriteAll(tx_file.fd(), kFileContent, sizeof(kFileContent)),
+            sizeof(kFileContent));
   cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args, false,
                      tx_file.fd());
   EXPECT_CALL(*cli_, OnInvokeMethodReply(_));
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index d2fb134..8c8f20e 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -396,8 +396,8 @@
   sa.sa_handler = [](int) {
     PERFETTO_LOG("SIGINT received: disabling tracing");
     char one = '1';
-    PERFETTO_CHECK(PERFETTO_EINTR(write(g_consumer_cmd->ctrl_c_pipe_wr(), &one,
-                                        sizeof(one))) == 1);
+    PERFETTO_CHECK(base::WriteAll(g_consumer_cmd->ctrl_c_pipe_wr(), &one,
+                                  sizeof(one)) == 1);
   };
   sa.sa_flags = static_cast<decltype(sa.sa_flags)>(SA_RESETHAND | SA_RESTART);
 #pragma GCC diagnostic pop
diff --git a/src/perfetto_cmd/rate_limiter.cc b/src/perfetto_cmd/rate_limiter.cc
index af7ef7f..2b789af 100644
--- a/src/perfetto_cmd/rate_limiter.cc
+++ b/src/perfetto_cmd/rate_limiter.cc
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "perfetto/base/file_utils.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/base/scoped_file.h"
 #include "perfetto/base/utils.h"
@@ -184,7 +185,7 @@
   PERFETTO_CHECK(size < sizeof(buf));
   if (!state.SerializeToArray(&buf, static_cast<int>(size)))
     return false;
-  ssize_t written = PERFETTO_EINTR(write(out_fd.get(), &buf, size));
+  ssize_t written = base::WriteAll(out_fd.get(), &buf, size);
   return written >= 0 && static_cast<size_t>(written) == size;
 }
 
diff --git a/src/perfetto_cmd/rate_limiter_unittest.cc b/src/perfetto_cmd/rate_limiter_unittest.cc
index 8a180a9..988c7b9 100644
--- a/src/perfetto_cmd/rate_limiter_unittest.cc
+++ b/src/perfetto_cmd/rate_limiter_unittest.cc
@@ -18,6 +18,7 @@
 
 #include <stdio.h>
 
+#include "perfetto/base/file_utils.h"
 #include "perfetto/base/scoped_file.h"
 #include "perfetto/base/temp_file.h"
 #include "perfetto/base/utils.h"
@@ -71,7 +72,7 @@
 void WriteGarbageToFile(const std::string& path) {
   base::ScopedFile fd(base::OpenFile(path, O_WRONLY | O_CREAT, 0600));
   constexpr char data[] = "Some random bytes.";
-  if (write(fd.get(), data, sizeof(data)) != sizeof(data))
+  if (base::WriteAll(fd.get(), data, sizeof(data)) != sizeof(data))
     ADD_FAILURE() << "Could not write garbage";
 }
 
diff --git a/src/traced/probes/filesystem/fs_mount_unittest.cc b/src/traced/probes/filesystem/fs_mount_unittest.cc
index da544e8..6c6b489 100644
--- a/src/traced/probes/filesystem/fs_mount_unittest.cc
+++ b/src/traced/probes/filesystem/fs_mount_unittest.cc
@@ -24,6 +24,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
 #include "perfetto/base/scoped_file.h"
 #include "perfetto/base/temp_file.h"
 #include "perfetto/base/utils.h"
@@ -52,7 +53,8 @@
 )";
 
   base::TempFile tmp_file = base::TempFile::Create();
-  base::ignore_result(write(tmp_file.fd(), kMounts, sizeof(kMounts)));
+  ASSERT_EQ(base::WriteAll(tmp_file.fd(), kMounts, sizeof(kMounts)),
+            sizeof(kMounts));
   std::multimap<BlockDeviceID, std::string> mounts =
       ParseMounts(tmp_file.path().c_str());
   struct stat dev_stat = {}, root_stat = {};
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index 08a1deb..abc5d09 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -29,6 +29,7 @@
 #include <utility>
 
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/base/time.h"
 #include "perfetto/tracing/core/trace_writer.h"
@@ -76,7 +77,7 @@
   auto fd = base::OpenFile(path, O_WRONLY);
   if (!fd)
     return;
-  perfetto::base::ignore_result(write(*fd, str, strlen(str)));
+  base::ignore_result(base::WriteAll(*fd, str, strlen(str)));
 }
 
 void ClearFile(const char* path) {
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 87095cf..cb84dd7 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -46,7 +46,7 @@
 void KernelLogWrite(const char* s) {
   PERFETTO_DCHECK(*s && s[strlen(s) - 1] == '\n');
   if (FtraceProcfs::g_kmesg_fd != -1)
-    base::ignore_result(write(FtraceProcfs::g_kmesg_fd, s, strlen(s)));
+    base::ignore_result(base::WriteAll(FtraceProcfs::g_kmesg_fd, s, strlen(s)));
 }
 
 }  // namespace
@@ -224,7 +224,7 @@
   base::ScopedFile fd = base::OpenFile(path, O_WRONLY);
   if (!fd)
     return false;
-  ssize_t written = PERFETTO_EINTR(write(fd.get(), str.c_str(), str.length()));
+  ssize_t written = base::WriteAll(fd.get(), str.c_str(), str.length());
   ssize_t length = static_cast<ssize_t>(str.length());
   // This should either fail or write fully.
   PERFETTO_CHECK(written == length || written == -1);
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 9cab744..80b924c 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -31,6 +31,7 @@
 #include <algorithm>
 
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/utils.h"
 #include "perfetto/tracing/core/consumer.h"
@@ -80,7 +81,7 @@
 ssize_t writev(int fd, const struct iovec* iov, int iovcnt) {
   ssize_t total_size = 0;
   for (int i = 0; i < iovcnt; ++i) {
-    ssize_t current_size = write(fd, iov[i].iov_base, iov[i].iov_len);
+    ssize_t current_size = base::WriteAll(fd, iov[i].iov_base, iov[i].iov_len);
     if (current_size != static_cast<ssize_t>(iov[i].iov_len))
       return -1;
     total_size += current_size;
diff --git a/src/tracing/ipc/posix_shared_memory_unittest.cc b/src/tracing/ipc/posix_shared_memory_unittest.cc
index b07cb00..e6739bf 100644
--- a/src/tracing/ipc/posix_shared_memory_unittest.cc
+++ b/src/tracing/ipc/posix_shared_memory_unittest.cc
@@ -25,6 +25,7 @@
 
 #include "gtest/gtest.h"
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
 #include "perfetto/base/scoped_file.h"
 #include "perfetto/base/temp_file.h"
 #include "perfetto/base/utils.h"
@@ -69,7 +70,7 @@
   base::TempFile tmp_file = base::TempFile::CreateUnlinked();
   const int fd_num = tmp_file.fd();
   ASSERT_EQ(0, ftruncate(fd_num, base::kPageSize));
-  ASSERT_EQ(7, PERFETTO_EINTR(write(fd_num, "foobar", 7)));
+  ASSERT_EQ(7, base::WriteAll(fd_num, "foobar", 7));
 
   std::unique_ptr<PosixSharedMemory> shm =
       PosixSharedMemory::AttachToFd(tmp_file.ReleaseFD());