| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you 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 "kudu/util/minidump.h" |
| |
| #include <unistd.h> |
| |
| #include <csignal> |
| #include <cstdlib> |
| #include <cstring> |
| #include <functional> |
| #include <initializer_list> |
| #include <ostream> |
| #include <string> |
| #include <vector> |
| |
| #include <gflags/gflags_declare.h> |
| #include <glog/logging.h> |
| #include <gtest/gtest.h> |
| |
| #include "kudu/util/env.h" |
| #include "kudu/util/path_util.h" |
| #include "kudu/util/status.h" |
| #include "kudu/util/test_macros.h" |
| #include "kudu/util/test_util.h" |
| |
| using std::string; |
| using std::vector; |
| |
| DECLARE_bool(enable_minidumps); |
| DECLARE_int32(max_minidumps); |
| DECLARE_string(minidump_path); |
| |
| namespace kudu { |
| |
| class MinidumpDeathTest : public KuduTest { |
| protected: |
| void WaitForMinidumps(int expected, const string& dir); |
| }; |
| |
| void MinidumpDeathTest::WaitForMinidumps(int expected, const string& dir) { |
| ASSERT_EVENTUALLY([&] { |
| vector<string> matches; |
| ASSERT_OK(env_->Glob(JoinPathSegments(dir, "*.dmp"), &matches)); |
| ASSERT_EQ(expected, matches.size()); |
| }); |
| } |
| |
| // Test that registering the minidump exception handler results in creation of |
| // minidump files on crash. Also test that deleting excess minidump files works |
| // as expected. |
| TEST_F(MinidumpDeathTest, TestRegisterAndDelete) { |
| FLAGS_enable_minidumps = true; |
| FLAGS_minidump_path = JoinPathSegments(test_dir_, "minidumps"); |
| MinidumpExceptionHandler minidump_handler; |
| ASSERT_DEATH({ |
| abort(); |
| }, |
| // Ensure that a stack trace is produced. |
| "kudu::MinidumpDeathTest_TestRegisterAndDelete_Test::TestBody()"); |
| |
| // Ensure that a minidump is produced. |
| string minidump_dir = minidump_handler.minidump_dir(); |
| NO_FATALS(WaitForMinidumps(1, minidump_dir)); |
| |
| // Now create more minidumps so we can clean them up. |
| for (int num_dumps : {2, 3}) { |
| kill(getpid(), SIGUSR1); |
| NO_FATALS(WaitForMinidumps(num_dumps, minidump_dir)); |
| } |
| |
| FLAGS_max_minidumps = 2; |
| ASSERT_OK(minidump_handler.DeleteExcessMinidumpFiles(env_)); |
| NO_FATALS(WaitForMinidumps(2, minidump_dir)); |
| } |
| |
| // Test that a CHECK() failure produces a stack trace and a minidump. |
| TEST_F(MinidumpDeathTest, TestCheckStackTraceAndMinidump) { |
| FLAGS_enable_minidumps = true; |
| FLAGS_minidump_path = JoinPathSegments(test_dir_, "minidumps"); |
| MinidumpExceptionHandler minidump_handler; |
| ASSERT_DEATH({ |
| CHECK_EQ(1, 0); |
| }, |
| // Ensure that a stack trace is produced. |
| "kudu::MinidumpDeathTest_TestCheckStackTraceAndMinidump_Test::TestBody()"); |
| |
| // Ensure that a minidump is produced. |
| string minidump_dir = minidump_handler.minidump_dir(); |
| NO_FATALS(WaitForMinidumps(1, minidump_dir)); |
| } |
| |
| class MinidumpSignalDeathTest : public MinidumpDeathTest, |
| public ::testing::WithParamInterface<int> { |
| }; |
| |
| // Test that we get both a minidump and a stack trace for each supported signal. |
| TEST_P(MinidumpSignalDeathTest, TestHaveMinidumpAndStackTrace) { |
| FLAGS_enable_minidumps = true; |
| int signal = GetParam(); |
| |
| #if defined(ADDRESS_SANITIZER) |
| // ASAN appears to catch SIGBUS, SIGSEGV, and SIGFPE and the process is not killed. |
| if (signal == SIGBUS || signal == SIGSEGV || signal == SIGFPE) { |
| return; |
| } |
| #endif |
| |
| #if defined(THREAD_SANITIZER) |
| // TSAN appears to catch SIGTERM and the process is not killed. |
| if (signal == SIGTERM) { |
| return; |
| } |
| #endif |
| |
| LOG(INFO) << "Testing signal: " << strsignal(signal); |
| |
| FLAGS_minidump_path = JoinPathSegments(test_dir_, "minidumps"); |
| MinidumpExceptionHandler minidump_handler; |
| ASSERT_DEATH({ |
| kill(getpid(), signal); |
| }, |
| // Ensure that a stack trace is produced. |
| "kudu::MinidumpSignalDeathTest_TestHaveMinidumpAndStackTrace_Test::TestBody()"); |
| |
| // Ensure that a mindump is produced, unless it's SIGTERM, which does not |
| // create a minidump. |
| int num_expected_minidumps = 1; |
| if (signal == SIGTERM) { |
| num_expected_minidumps = 0; |
| } |
| NO_FATALS(WaitForMinidumps(num_expected_minidumps, minidump_handler.minidump_dir())); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(DeadlySignals, MinidumpSignalDeathTest, |
| ::testing::Values(SIGABRT, SIGBUS, SIGSEGV, SIGILL, SIGFPE, SIGTERM)); |
| |
| } // namespace kudu |