Win port: Introduce //base/ctrl_c_handler.h

A simple wrapper to abstract signal handling
and provide a Windows alternative.

Bug: 174454879
Change-Id: I0af165dd262ba8830ad9852a2840eac810167e85
diff --git a/Android.bp b/Android.bp
index 3376ba3..046da6e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6402,6 +6402,7 @@
 filegroup {
   name: "perfetto_src_base_base",
   srcs: [
+    "src/base/ctrl_c_handler.cc",
     "src/base/event_fd.cc",
     "src/base/file_utils.cc",
     "src/base/logging.cc",
diff --git a/BUILD b/BUILD
index 8d3c5ea..d91e53f 100644
--- a/BUILD
+++ b/BUILD
@@ -291,6 +291,7 @@
     srcs = [
         "include/perfetto/ext/base/circular_queue.h",
         "include/perfetto/ext/base/container_annotations.h",
+        "include/perfetto/ext/base/ctrl_c_handler.h",
         "include/perfetto/ext/base/endian.h",
         "include/perfetto/ext/base/event_fd.h",
         "include/perfetto/ext/base/file_utils.h",
@@ -574,6 +575,7 @@
 perfetto_cc_library(
     name = "src_base_base",
     srcs = [
+        "src/base/ctrl_c_handler.cc",
         "src/base/event_fd.cc",
         "src/base/file_utils.cc",
         "src/base/logging.cc",
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index 694d23d..15e1799 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -18,6 +18,7 @@
   sources = [
     "circular_queue.h",
     "container_annotations.h",
+    "ctrl_c_handler.h",
     "endian.h",
     "event_fd.h",
     "file_utils.h",
diff --git a/include/perfetto/ext/base/ctrl_c_handler.h b/include/perfetto/ext/base/ctrl_c_handler.h
new file mode 100644
index 0000000..6c7b34c
--- /dev/null
+++ b/include/perfetto/ext/base/ctrl_c_handler.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_
+
+namespace perfetto {
+namespace base {
+
+// On Linux/Android/Mac: installs SIGINT + SIGTERM signal handlers.
+// On Windows: installs a SetConsoleCtrlHandler() handler.
+// The passed handler must be async safe.
+using CtrlCHandlerFunction = void (*)();
+void InstallCtrCHandler(CtrlCHandlerFunction);
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 057629b..76921cf 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -30,6 +30,7 @@
     "../../include/perfetto/ext/base",
   ]
   sources = [
+    "ctrl_c_handler.cc",
     "event_fd.cc",
     "file_utils.cc",
     "logging.cc",
diff --git a/src/base/ctrl_c_handler.cc b/src/base/ctrl_c_handler.cc
new file mode 100644
index 0000000..72b60a8
--- /dev/null
+++ b/src/base/ctrl_c_handler.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include "perfetto/ext/base/ctrl_c_handler.h"
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/base/compiler.h"
+#include "perfetto/base/logging.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#include <consoleapi.h>
+#include <io.h>
+#else
+#include <signal.h>
+#include <unistd.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+namespace {
+CtrlCHandlerFunction g_handler = nullptr;
+}
+
+void InstallCtrCHandler(CtrlCHandlerFunction handler) {
+  PERFETTO_CHECK(g_handler == nullptr);
+  g_handler = handler;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  auto trampoline = [](DWORD type) -> int {
+    if (type == CTRL_C_EVENT) {
+      g_handler();
+      return true;
+    }
+    return false;
+  };
+  ::SetConsoleCtrlHandler(trampoline, true);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  // Setup signal handler.
+  struct sigaction sa {};
+
+// Glibc headers for sa_sigaction trigger this.
+#pragma GCC diagnostic push
+#if defined(__clang__)
+#pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
+#endif
+  sa.sa_handler = [](int) { g_handler(); };
+  sa.sa_flags = static_cast<decltype(sa.sa_flags)>(SA_RESETHAND | SA_RESTART);
+#pragma GCC diagnostic pop
+  sigaction(SIGINT, &sa, nullptr);
+  sigaction(SIGTERM, &sa, nullptr);
+#else
+  // Do nothing on NaCL and Fuchsia.
+  ignore_result(handler);
+#endif
+}
+
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index b8e332c..2610154 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -20,7 +20,6 @@
 
 #include <fcntl.h>
 #include <getopt.h>
-#include <signal.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -45,6 +44,7 @@
 #include "perfetto/base/compiler.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/base/time.h"
+#include "perfetto/ext/base/ctrl_c_handler.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/thread_utils.h"
@@ -920,20 +920,7 @@
 }
 
 void PerfettoCmd::SetupCtrlCSignalHandler() {
-  // Setup signal handler.
-  struct sigaction sa {};
-
-// Glibc headers for sa_sigaction trigger this.
-#pragma GCC diagnostic push
-#if defined(__clang__)
-#pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
-#endif
-  sa.sa_handler = [](int) { g_consumer_cmd->SignalCtrlC(); };
-  sa.sa_flags = static_cast<decltype(sa.sa_flags)>(SA_RESETHAND | SA_RESTART);
-#pragma GCC diagnostic pop
-  sigaction(SIGINT, &sa, nullptr);
-  sigaction(SIGTERM, &sa, nullptr);
-
+  base::InstallCtrCHandler([] { g_consumer_cmd->SignalCtrlC(); });
   task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [this] {
     PERFETTO_LOG("SIGINT/SIGTERM received: disabling tracing.");
     ctrl_c_evt_.Clear();
diff --git a/test/stress_test/stress_test.cc b/test/stress_test/stress_test.cc
index ac53bd3..4fa0d2d 100644
--- a/test/stress_test/stress_test.cc
+++ b/test/stress_test/stress_test.cc
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-#include <signal.h>
 #include <stdarg.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
 
 #include <chrono>
 #include <list>
@@ -28,7 +25,9 @@
 #include <thread>
 #include <vector>
 
+#include "perfetto/base/build_config.h"
 #include "perfetto/base/compiler.h"
+#include "perfetto/ext/base/ctrl_c_handler.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/subprocess.h"
@@ -44,6 +43,12 @@
 #include "protos/perfetto/trace/test_event.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#else
+#include <signal.h>
+#endif
+
 // Generated by gen_configs_blob.py. It defines the kStressTestConfigs array,
 // which contains a proto-encoded StressTestConfig message for each .cfg file
 // listed in /test/stress_test/configs/BUILD.gn.
@@ -52,6 +57,19 @@
 namespace perfetto {
 namespace {
 
+// TODO(primiano): We need a base::File to get around the awkwardness of
+// files on Windows being a mix of int and HANDLE (and open() vs CreateFile())
+// in our codebase.
+base::ScopedPlatformHandle OpenLogFile(const std::string& path) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  return base::ScopedPlatformHandle(::CreateFileA(
+      path.c_str(), GENERIC_READ | GENERIC_WRITE,
+      FILE_SHARE_DELETE | FILE_SHARE_READ, nullptr, CREATE_ALWAYS, 0, nullptr));
+#else
+  return base::OpenFile(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
+#endif
+}
+
 using StressTestConfig = protos::gen::StressTestConfig;
 
 struct SigHandlerCtx {
@@ -105,7 +123,11 @@
 
 TestHarness::TestHarness() {
   results_dir_ = base::GetSysTempDir() + "/perfetto-stress-test";
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  system(("rmdir \"" + results_dir_ + "\" /s /q").c_str());
+#else
   system(("rm -r -- \"" + results_dir_ + "\"").c_str());
+#endif
   PERFETTO_CHECK(base::Mkdir(results_dir_));
   PERFETTO_LOG("Saving test results in %s", results_dir_.c_str());
 }
@@ -125,7 +147,8 @@
     log_msg[res++] = '\n';
     log_msg[res++] = '\0';
   }
-  base::ignore_result(write(*error_log_, log_msg, static_cast<size_t>(res)));
+
+  base::WriteAll(*error_log_, log_msg, static_cast<size_t>(res));
 }
 
 void TestHarness::RunConfig(const char* cfg_name,
@@ -154,8 +177,7 @@
   base::Subprocess traced({bin_dir + "/traced"});
   traced.args.env = env_;
   if (!verbose) {
-    traced.args.out_fd = base::OpenFile(result_dir + "/traced.log",
-                                        O_RDWR | O_CREAT | O_TRUNC, 0644);
+    traced.args.out_fd = OpenLogFile(result_dir + "/traced.log");
     traced.args.stderr_mode = traced.args.stdout_mode = base::Subprocess::kFd;
   }
   traced.Start();
@@ -171,8 +193,7 @@
     producer.args.input = cfg.SerializeAsString();
     if (!verbose) {
       producer.args.out_fd =
-          base::OpenFile(result_dir + "/producer." + std::to_string(i) + ".log",
-                         O_RDWR | O_CREAT | O_TRUNC, 0644);
+          OpenLogFile(result_dir + "/producer." + std::to_string(i) + ".log");
       producer.args.stderr_mode = producer.args.stdout_mode =
           base::Subprocess::kFd;
     }
@@ -190,12 +211,11 @@
   consumer.args.env = env_;
   consumer.args.input = cfg.trace_config().SerializeAsString();
   if (!verbose) {
-    consumer.args.out_fd = base::OpenFile(result_dir + "/perfetto.log",
-                                          O_RDWR | O_CREAT | O_TRUNC, 0644);
+    consumer.args.out_fd = OpenLogFile(result_dir + "/perfetto.log");
     consumer.args.stderr_mode = consumer.args.stdout_mode =
         base::Subprocess::kFd;
   }
-  unlink(trace_file_path.c_str());
+  remove(trace_file_path.c_str());
   consumer.Start();
   int64_t t_start = base::GetBootTimeNs().count();
   g_sig->pids_to_kill.emplace_back(consumer.pid());
@@ -209,8 +229,6 @@
     consumer.KillAndWaitForTermination();
   }
 
-  // Stop
-  consumer.KillAndWaitForTermination(SIGTERM);
   int64_t t_end = base::GetBootTimeNs().count();
 
   for (auto& producer : producers) {
@@ -260,12 +278,14 @@
   if (!fd)
     return AddFailure("Trace file does not exist");
   const off_t file_size = lseek(*fd, 0, SEEK_END);
+  lseek(*fd, 0, SEEK_SET);
   if (file_size <= 0)
     return AddFailure("Trace file is empty");
+
   test_result.trace_size_kb = static_cast<uint32_t>(file_size / 1000);
-  const uint8_t* const start = static_cast<const uint8_t*>(mmap(
-      nullptr, static_cast<size_t>(file_size), PROT_READ, MAP_PRIVATE, *fd, 0));
-  PERFETTO_CHECK(start != MAP_FAILED);
+  std::string trace_data;
+  PERFETTO_CHECK(base::ReadFileDescriptor(*fd, &trace_data));
+  const auto* const start = reinterpret_cast<const uint8_t*>(trace_data.data());
   const uint8_t* const end = start + file_size;
 
   constexpr uint8_t kTracePacketTag = MakeTagLengthDelimited(1);
@@ -401,11 +421,17 @@
   }
 }
 
-void CtrlCHandler(int) {
+void CtrlCHandler() {
   g_sig->aborted.store(true);
   for (auto it = g_sig->pids_to_kill.rbegin(); it != g_sig->pids_to_kill.rend();
        it++) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    base::ScopedPlatformHandle proc_handle(
+        ::OpenProcess(PROCESS_TERMINATE, false, *it));
+    ::TerminateProcess(*proc_handle, STATUS_CONTROL_C_EXIT);
+#else
     kill(*it, SIGKILL);
+#endif
   }
 }
 
@@ -425,7 +451,7 @@
   }
 
   g_sig = new SigHandlerCtx();
-  signal(SIGINT, CtrlCHandler);
+  base::InstallCtrCHandler(&CtrlCHandler);
 
   for (size_t i = 0; i < base::ArraySize(kStressTestConfigs) && !g_sig->aborted;
        ++i) {