diff options
-rw-r--r-- | base/scoped_fd.h | 54 | ||||
-rw-r--r-- | chrome/app/breakpad_linux.cc | 169 | ||||
-rw-r--r-- | chrome/app/breakpad_linux.h | 15 | ||||
-rw-r--r-- | chrome/app/chrome_dll_main.cc | 18 | ||||
-rw-r--r-- | chrome/browser/browser_main.cc | 3 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 4 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 2 |
7 files changed, 264 insertions, 1 deletions
diff --git a/base/scoped_fd.h b/base/scoped_fd.h new file mode 100644 index 0000000..2837ff8 --- /dev/null +++ b/base/scoped_fd.h @@ -0,0 +1,54 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if !defined(BASE_SCOPED_FD_H_) && defined(OS_POSIX) +#define BASE_SCOPED_FD_H_ + +#include "base/eintr_wrapper.h" + +// POSIX only +// +// A wrapper class for file descriptors which automatically closes them when +// they go out of scope: +// ScopedFd fd(open("/tmp/file", O_RDONLY)); +// read(fd.get(), ...); +class ScopedFd { + public: + ScopedFd() + : fd_(-1) { } + + explicit ScopedFd(int fd) + : fd_(fd) { } + + ~ScopedFd() { + Close(); + } + + void Close() { + if (fd_ >= 0) { + HANDLE_EINTR(close(fd_)); + fd_ = -1; + } + } + + int get() const { return fd_; } + + int Take() { + const int temp = fd_; + fd_ = -1; + return temp; + } + + void Set(int new_fd) { + Close(); + fd_ = new_fd; + } + + private: + int fd_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedFd); +}; + +#endif // BASE_SCOPED_FD_H_ diff --git a/chrome/app/breakpad_linux.cc b/chrome/app/breakpad_linux.cc index 199371e..c5dd0d3 100644 --- a/chrome/app/breakpad_linux.cc +++ b/chrome/app/breakpad_linux.cc @@ -4,9 +4,13 @@ #include "chrome/app/breakpad_linux.h" +#include <arpa/inet.h> #include <fcntl.h> +#include <netinet/in.h> +#include <sys/sendfile.h> #include <sys/socket.h> #include <sys/uio.h> +#include <sys/wait.h> #include <unistd.h> #include <algorithm> @@ -15,11 +19,15 @@ #include "base/command_line.h" #include "base/eintr_wrapper.h" #include "base/file_version_info_linux.h" +#include "base/format_macros.h" #include "base/global_descriptors_posix.h" +#include "base/json_writer.h" #include "base/linux_util.h" #include "base/path_service.h" #include "base/rand_util.h" +#include "base/scoped_fd.h" #include "base/string_util.h" +#include "base/values.h" #include "breakpad/linux/directory_reader.h" #include "breakpad/linux/exception_handler.h" #include "breakpad/linux/linux_libc_support.h" @@ -618,3 +626,164 @@ void InitCrashReporter() { EnableRendererCrashDumping(); } } + +// ----------------------------------------------------------------------------- + +bool EnableCoreDumping(std::string* core_dump_directory) { + // First we check that the core files will get dumped to the + // current-directory in a file called 'core'. We could try to support other + // setups by simulating the kernel's code, but it's extra complexity and we + // only intend for this code to be run internally. + static const char kCorePatternFd[] = "/proc/sys/kernel/core_pattern"; + + ScopedFd core_pattern_fd(open(kCorePatternFd, O_RDONLY)); + if (core_pattern_fd.get() < 0) { + LOG(WARNING) << "Cannot open " << kCorePatternFd << ": " << strerror(errno); + return false; + } + + char buf[6]; + if (read(core_pattern_fd.get(), buf, sizeof(buf)) != 5 || + memcmp(buf, "core\n", 5)) { + LOG(WARNING) << "Your core pattern is not set to 'core\n', cannot dump"; + return false; + } + core_pattern_fd.Close(); + + // We check that the rlimit on core file size is unlimited. + struct rlimit core_dump_limit; + if (getrlimit(RLIMIT_CORE, &core_dump_limit)) { + LOG(WARNING) << "Failed to get core dump limit: " << strerror(errno); + return false; + } + + if (core_dump_limit.rlim_cur != RLIM_INFINITY) { + if (core_dump_limit.rlim_max != RLIM_INFINITY) { + LOG(WARNING) << "Cannot core dump: hard limit on core dumps found"; + return false; + } + + core_dump_limit.rlim_cur = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &core_dump_limit)) { + LOG(WARNING) << "Failed to set core dump limit: " << strerror(errno); + return false; + } + } + + // Finally, we move the current directory into a temp dir and return the path + // to the temp dir so that we can clean up afterwards. + char temp_dir_template[] = "/tmp/chromium-core-dump-XXXXXX"; + if (mkdtemp(temp_dir_template) == NULL) { + LOG(WARNING) << "Failed to create temp dir for core dumping: " + << strerror(errno); + return false; + } + + if (chdir(temp_dir_template)) { + LOG(WARNING) << "Cannot chdir into temp directory: " << strerror(errno); + return false; + } + + *core_dump_directory = temp_dir_template; + + return true; +} + +static void UploadCoreFile(const pid_t child, std::string* core_filename) { + *core_filename = "core"; + ScopedFd fd(open(core_filename->c_str(), O_RDONLY)); + if (fd.get() < 0) { + *core_filename = StringPrintf("core.%d", child); + fd.Set(open(core_filename->c_str(), O_RDONLY)); + if (fd.get() < 0) { + LOG(WARNING) << "Cannot open resulting core dump from browser: " + << strerror(errno); + return; + } + } + + struct stat st; + if (fstat(fd.get(), &st)) { + LOG(WARNING) << "Failed to stat core file: " << strerror(errno); + return; + } + + const uint32_t core_size = st.st_size; + + static const char kMyBinary[] = "/proc/self/exe"; + ScopedFd self_fd(open(kMyBinary, O_RDONLY)); + if (self_fd.get() < 0) { + LOG(WARNING) << "Cannot open " << kMyBinary << ": " << strerror(errno); + return; + } + + if (fstat(self_fd.get(), &st)) { + LOG(WARNING) << "Failed to stat " << kMyBinary << ": " << strerror(errno); + return; + } + + const uint64_t binary_size = st.st_size; + + DictionaryValue header; + header.SetString(L"core-size", StringPrintf("%" PRIu32, core_size)); + header.SetString(L"chrome-version", FILE_VERSION); + header.SetString(L"binary-size", StringPrintf("%" PRIu64, binary_size)); + header.SetString(L"user", getenv("USER")); +#if defined(GOOGLE_CHROME_BUILD) + header.SetBoolean(L"offical-build", true); +#endif + + std::string json; + JSONWriter::Write(&header, true /* pretty print */, &json); + const uint32_t json_size = json.size(); + + ScopedFd sock(socket(PF_INET, SOCK_STREAM, 0)); + if (sock.get() < 0) { + LOG(WARNING) << "Cannot open socket: " << strerror(errno); + return; + } + + static const char kUploadIP[] = "172.22.68.141"; + static const uint16_t kUploadPort = 9999; + + struct sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(kUploadIP); + sin.sin_port = htons(kUploadPort); + + if (connect(sock.get(), (struct sockaddr*) &sin, sizeof(sin))) { + LOG(WARNING) << "Failed to connect to upload server (" << kUploadIP + << ":" << kUploadPort << "): " << strerror(errno); + return; + } + + if (HANDLE_EINTR(write(sock.get(), &json_size, sizeof(json_size))) != + static_cast<ssize_t>(sizeof(json_size)) || + HANDLE_EINTR(write(sock.get(), json.data(), json_size)) != + static_cast<ssize_t>(json_size) || + HANDLE_EINTR(write(sock.get(), &core_size, sizeof(core_size))) != + static_cast<ssize_t>(sizeof(core_size)) || + HANDLE_EINTR(sendfile(sock.get(), fd.get(), NULL, core_size)) != + static_cast<ssize_t>(core_size)) { + LOG(WARNING) << "Failed to write all data to server"; + return; + } +} + +void MonitorForCoreDumpsAndReport(const std::string& core_dump_directory, + const pid_t child) { + int status; + const pid_t result = HANDLE_EINTR(waitpid(child, &status, 0)); + if (result < 1) { + LOG(ERROR) << "Failed to wait for browser child: " << strerror(errno); + return; + } + + if (WIFSIGNALED(status) && WCOREDUMP(status)) { + std::string core_filename; + UploadCoreFile(child, &core_filename); + unlink(core_filename.c_str()); + } + + rmdir(core_dump_directory.c_str()); +} diff --git a/chrome/app/breakpad_linux.h b/chrome/app/breakpad_linux.h index 518cf42..0c3ccde 100644 --- a/chrome/app/breakpad_linux.h +++ b/chrome/app/breakpad_linux.h @@ -5,6 +5,8 @@ #ifndef CHROME_APP_BREAKPAD_LINUX_H_ #define CHROME_APP_BREAKPAD_LINUX_H_ +#include <string> + extern void InitCrashReporter(); #if defined(GOOGLE_CHROME_BUILD) @@ -25,6 +27,19 @@ struct BreakpadInfo { }; extern int UploadCrashDump(const BreakpadInfo& info); + +// Checks that the kernel's core filename pattern is "core" and moves the +// current working directory to a temp directory. +// Returns true iff core dumping has been successfully enabled for the current +// process. +bool EnableCoreDumping(std::string* core_dump_directory); +// Blocks until the given child has exited. If the kernel indicates that the +// child dumped core, then the core is expected a file called "core" and is +// uploaded to a collection server. The core file is deleted and the given +// directory is removed. +void MonitorForCoreDumpsAndReport(const std::string& core_dump_directory, + const pid_t child); + #endif // defined(GOOGLE_CHROME_BUILD) #endif // CHROME_APP_BREAKPAD_LINUX_H_ diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc index 652217e..b375474 100644 --- a/chrome/app/chrome_dll_main.cc +++ b/chrome/app/chrome_dll_main.cc @@ -51,6 +51,9 @@ #if defined(OS_MACOSX) #include "chrome/app/breakpad_mac.h" #endif +#if defined(OS_LINUX) +#include "chrome/app/breakpad_linux.h" +#endif #include "chrome/app/scoped_ole_initializer.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/common/chrome_constants.h" @@ -346,6 +349,21 @@ int ChromeMain(int argc, const char** argv) { int ret = execlp("man", "man", binary.BaseName().value().c_str(), NULL); LOG(FATAL) << "execlp failed: " << strerror(ret); } + +#if defined(GOOGLE_CHROME_BUILD) + if (parsed_command_line.HasSwitch(switches::kGoogleInternalCrashReporting)) { + // Enable full core dump reporting. Internal only. + std::string core_dump_directory; + if (EnableCoreDumping(&core_dump_directory)) { + LOG(WARNING) << "Full core dump reporting enabled."; + const pid_t child = fork(); + if (child != 0) { + MonitorForCoreDumpsAndReport(core_dump_directory, child); + _exit(0); + } + } + } +#endif #endif #if defined(OS_POSIX) diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index f4e139b..a975ef3 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -249,7 +249,8 @@ int BrowserMain(const MainFunctionParams& parameters) { #if defined(OS_LINUX) // Needs to be called after we have chrome::DIR_USER_DATA. - InitCrashReporter(); + if (!parsed_command_line.HasSwitch(switches::kGoogleInternalCrashReporting)) + InitCrashReporter(); #endif // WARNING: If we get a WM_ENDSESSION objects created on the stack here diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index d6a1ab6..a8f208e 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -557,6 +557,10 @@ const wchar_t kDiagnostics[] = L"diagnostics"; // Enables the custom JumpList on Windows 7. const wchar_t kEnableCustomJumpList[] = L"enable-custom-jumplist"; +// Enables full core dump reporting for Googlers only (Linux only). +const wchar_t kGoogleInternalCrashReporting[] = + L"google-internal-crash-reporting"; + // Enables HTML5 DB support. const wchar_t kEnableDatabases[] = L"enable-databases"; } // namespace switches diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index d77182e..bfe9cdd 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -219,6 +219,8 @@ extern const wchar_t kDiagnostics[]; extern const wchar_t kEnableCustomJumpList[]; +extern const wchar_t kGoogleInternalCrashReporting[]; + extern const wchar_t kEnableDatabases[]; } // namespace switches |