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());