diff --git a/src/client/linux/minidump_writer/linux_core_dumper.cc b/src/client/linux/minidump_writer/linux_core_dumper.cc index 3bb2ee7e..b24573b1 100644 --- a/src/client/linux/minidump_writer/linux_core_dumper.cc +++ b/src/client/linux/minidump_writer/linux_core_dumper.cc @@ -241,6 +241,16 @@ bool LinuxCoreDumper::EnumerateThreads() { crash_address_ = reinterpret_cast(info->si_addr); break; } + + // Set crash_exception_info for common signals. + switch (info->si_signo) { + case MD_EXCEPTION_CODE_LIN_SIGKILL: + set_crash_exception_info({info->si_pid, info->si_uid}); + break; + case MD_EXCEPTION_CODE_LIN_SIGSYS: + set_crash_exception_info({info->si_syscall, info->si_arch}); + break; + } break; } #if defined(__i386) || defined(__x86_64) diff --git a/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc b/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc index f5b299f8..927d5aa3 100644 --- a/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc +++ b/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc @@ -109,7 +109,7 @@ TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) { EXPECT_TRUE(dumper.ThreadsSuspend()); EXPECT_TRUE(dumper.ThreadsResume()); - // LinuxCoreDumper cannot determine the crash address and thus it always + // Linux does not set the crash address with SIGABRT, so make sure it always // sets the crash address to 0. EXPECT_EQ(0U, dumper.crash_address()); EXPECT_EQ(kCrashSignal, dumper.crash_signal()); @@ -126,3 +126,48 @@ TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) { EXPECT_EQ(getpid(), info.ppid); } } + +TEST(LinuxCoreDumperTest, VerifyExceptionDetails) { + CrashGenerator crash_generator; + if (!crash_generator.HasDefaultCorePattern()) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " + "is skipped due to non-default core pattern\n"); + return; + } + + const unsigned kNumOfThreads = 2; + const unsigned kCrashThread = 1; + const int kCrashSignal = SIGSYS; + pid_t child_pid; + ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, + kCrashSignal, &child_pid)); + + const string core_file = crash_generator.GetCoreFilePath(); + const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy(); + +#if defined(__ANDROID__) + struct stat st; + if (stat(core_file.c_str(), &st) != 0) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyExceptionDetails test is " + "skipped due to no core file being generated"); + return; + } +#endif + + LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str()); + + EXPECT_TRUE(dumper.Init()); + + EXPECT_TRUE(dumper.IsPostMortem()); + + // Check the exception details. + EXPECT_NE(0U, dumper.crash_address()); + EXPECT_EQ(kCrashSignal, dumper.crash_signal()); + EXPECT_EQ(crash_generator.GetThreadId(kCrashThread), + dumper.crash_thread()); + + // We check the length, but not the actual fields. We sent SIGSYS ourselves + // instead of the kernel, so the extended fields are garbage. + const std::vector info(dumper.crash_exception_info()); + EXPECT_EQ(2U, info.size()); +} diff --git a/src/client/linux/minidump_writer/linux_dumper.h b/src/client/linux/minidump_writer/linux_dumper.h index 76448fad..f4a75d90 100644 --- a/src/client/linux/minidump_writer/linux_dumper.h +++ b/src/client/linux/minidump_writer/linux_dumper.h @@ -38,6 +38,7 @@ #ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ #define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ +#include #include #if defined(__ANDROID__) #include @@ -47,6 +48,8 @@ #include #include +#include + #include "client/linux/dump_writer_common/mapping_info.h" #include "client/linux/dump_writer_common/thread_info.h" #include "common/linux/file_id.h" @@ -184,6 +187,14 @@ class LinuxDumper { void set_crash_signal_code(int code) { crash_signal_code_ = code; } int crash_signal_code() const { return crash_signal_code_; } + void set_crash_exception_info(const std::vector& exception_info) { + assert(exception_info.size() <= MD_EXCEPTION_MAXIMUM_PARAMETERS); + crash_exception_info_ = exception_info; + } + const std::vector& crash_exception_info() const { + return crash_exception_info_; + } + pid_t crash_thread() const { return crash_thread_; } void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; } @@ -236,6 +247,9 @@ class LinuxDumper { // The code associated with |crash_signal_|. int crash_signal_code_; + // The additional fields associated with |crash_signal_|. + std::vector crash_exception_info_; + // ID of the crashed thread. pid_t crash_thread_; diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc index 31e7bd62..e436bf07 100644 --- a/src/client/linux/minidump_writer/minidump_writer.cc +++ b/src/client/linux/minidump_writer/minidump_writer.cc @@ -713,6 +713,12 @@ class MinidumpWriter { stream->exception_record.exception_code = dumper_->crash_signal(); stream->exception_record.exception_flags = dumper_->crash_signal_code(); stream->exception_record.exception_address = dumper_->crash_address(); + const std::vector crash_exception_info = + dumper_->crash_exception_info(); + stream->exception_record.number_parameters = crash_exception_info.size(); + memcpy(stream->exception_record.exception_information, + crash_exception_info.data(), + sizeof(uint64_t) * crash_exception_info.size()); stream->thread_context = crashing_thread_context_; return true;