Add WatchDog class to kill process after a timeout.
Change-Id: I014e8a1148b5b0e731b14c39d2410aaedd877d2f
Bug: 72652321
diff --git a/Android.bp b/Android.bp
index 95237e3..98e5208 100644
--- a/Android.bp
+++ b/Android.bp
@@ -28,6 +28,7 @@
"src/base/page_allocator.cc",
"src/base/thread_checker.cc",
"src/base/unix_task_runner.cc",
+ "src/base/watchdog.cc",
"src/ftrace_reader/cpu_reader.cc",
"src/ftrace_reader/event_info.cc",
"src/ftrace_reader/event_info_constants.cc",
@@ -100,6 +101,7 @@
"src/base/page_allocator.cc",
"src/base/thread_checker.cc",
"src/base/unix_task_runner.cc",
+ "src/base/watchdog.cc",
"src/ipc/buffered_frame_deserializer.cc",
"src/ipc/client_impl.cc",
"src/ipc/deferred.cc",
@@ -187,6 +189,7 @@
"src/base/test/vm_test_utils.cc",
"src/base/thread_checker.cc",
"src/base/unix_task_runner.cc",
+ "src/base/watchdog.cc",
"src/ftrace_reader/cpu_reader.cc",
"src/ftrace_reader/end_to_end_integrationtest.cc",
"src/ftrace_reader/event_info.cc",
@@ -912,6 +915,7 @@
"src/base/page_allocator.cc",
"src/base/thread_checker.cc",
"src/base/unix_task_runner.cc",
+ "src/base/watchdog.cc",
"src/ipc/buffered_frame_deserializer.cc",
"src/ipc/client_impl.cc",
"src/ipc/deferred.cc",
@@ -1003,6 +1007,8 @@
"src/base/thread_checker_unittest.cc",
"src/base/unix_task_runner.cc",
"src/base/utils_unittest.cc",
+ "src/base/watchdog.cc",
+ "src/base/watchdog_unittest.cc",
"src/base/weak_ptr_unittest.cc",
"src/ftrace_reader/cpu_reader.cc",
"src/ftrace_reader/cpu_reader_unittest.cc",
diff --git a/include/perfetto/base/BUILD.gn b/include/perfetto/base/BUILD.gn
index d1d188a..27ee980 100644
--- a/include/perfetto/base/BUILD.gn
+++ b/include/perfetto/base/BUILD.gn
@@ -22,6 +22,7 @@
"thread_checker.h",
"unix_task_runner.h",
"utils.h",
+ "watchdog.h",
"weak_ptr.h",
]
if (is_android) {
diff --git a/include/perfetto/base/watchdog.h b/include/perfetto/base/watchdog.h
new file mode 100644
index 0000000..969059b
--- /dev/null
+++ b/include/perfetto/base/watchdog.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef INCLUDE_PERFETTO_BASE_WATCHDOG_H_
+#define INCLUDE_PERFETTO_BASE_WATCHDOG_H_
+
+#include <stdint.h>
+#include <time.h>
+
+namespace perfetto {
+namespace base {
+
+// WatchDog crashes the calling program using SIGABRT if it does
+// not go out of scope within the number of milliseconds passed
+// to the constructor.
+class WatchDog {
+ public:
+ explicit WatchDog(time_t millisecs);
+ ~WatchDog();
+
+ WatchDog(const WatchDog&) = delete;
+ WatchDog& operator=(const WatchDog&) = delete;
+
+ private:
+ timer_t timerid_;
+};
+
+} // namespace base
+} // namespace perfetto
+#endif
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index a1b6f7b..64fa733 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -25,6 +25,7 @@
"page_allocator.cc",
"thread_checker.cc",
"unix_task_runner.cc",
+ "watchdog.cc",
]
if (is_debug) {
deps += [ ":debug_crash_stack_trace" ]
@@ -98,6 +99,7 @@
"task_runner_unittest.cc",
"thread_checker_unittest.cc",
"utils_unittest.cc",
+ "watchdog_unittest.cc",
"weak_ptr_unittest.cc",
]
}
diff --git a/src/base/debug_crash_stack_trace.cc b/src/base/debug_crash_stack_trace.cc
index 8990a14..6e1f6c2 100644
--- a/src/base/debug_crash_stack_trace.cc
+++ b/src/base/debug_crash_stack_trace.cc
@@ -20,6 +20,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
#include <unistd.h>
#include <unwind.h>
@@ -151,6 +153,19 @@
}
Print("------------------ END OF CRASH ------------------\n");
+ // info->si_code <= 0 iff SI_FROMUSER (SI_FROMKERNEL otherwise).
+ if (info->si_code <= 0 || sig_num == SIGABRT) {
+ // This signal was triggered by somebody sending us the signal with kill().
+ // In order to retrigger it, we have to queue a new signal by calling
+ // kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is
+ // due to the kernel sending a SIGABRT from a user request via SysRQ.
+ if (syscall(__NR_tgkill, getpid(), syscall(__NR_gettid), sig_num) < 0) {
+ // If we failed to kill ourselves (e.g. because a sandbox disallows us
+ // to do so), we instead resort to terminating our process. This will
+ // result in an incorrect exit code.
+ _exit(1);
+ }
+ }
}
// __attribute__((constructor)) causes a static initializer that automagically
diff --git a/src/base/watchdog.cc b/src/base/watchdog.cc
new file mode 100644
index 0000000..8ba7981
--- /dev/null
+++ b/src/base/watchdog.cc
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#include "perfetto/base/watchdog.h"
+
+#include "perfetto/base/logging.h"
+
+#include <signal.h>
+#include <stdint.h>
+
+namespace perfetto {
+namespace base {
+
+WatchDog::WatchDog(time_t millisecs) {
+ struct sigevent sev;
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = SIGABRT;
+ sev.sigev_value.sival_ptr = &timerid_;
+ PERFETTO_CHECK(timer_create(CLOCK_MONOTONIC, &sev, &timerid_) != -1);
+ struct itimerspec its;
+ its.it_value.tv_sec = millisecs / 1000;
+ its.it_value.tv_nsec = 1000000L * (millisecs % 1000);
+ its.it_interval.tv_sec = 0;
+ its.it_interval.tv_nsec = 0;
+ PERFETTO_CHECK(timer_settime(timerid_, 0, &its, nullptr) != -1);
+}
+
+WatchDog::~WatchDog() {
+ PERFETTO_CHECK(timer_delete(timerid_) != -1);
+}
+
+} // namespace base
+} // namespace perfetto
diff --git a/src/base/watchdog_unittest.cc b/src/base/watchdog_unittest.cc
new file mode 100644
index 0000000..b61e493
--- /dev/null
+++ b/src/base/watchdog_unittest.cc
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#include "perfetto/base/watchdog.h"
+
+#include "gtest/gtest.h"
+
+#include <time.h>
+
+namespace perfetto {
+namespace base {
+namespace {
+
+TEST(WatchDogTest, Crash) {
+ EXPECT_DEATH(
+ {
+ WatchDog watchdog(1);
+ usleep(20000);
+ },
+ "");
+}
+
+TEST(WatchDogTest, NoCrash) {
+ WatchDog watchdog(100000);
+ usleep(5000);
+}
+
+} // namespace
+} // namespace base
+} // namespace perfetto