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) {