summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/scoped_fd.h54
-rw-r--r--chrome/app/breakpad_linux.cc169
-rw-r--r--chrome/app/breakpad_linux.h15
-rw-r--r--chrome/app/chrome_dll_main.cc18
-rw-r--r--chrome/browser/browser_main.cc3
-rw-r--r--chrome/common/chrome_switches.cc4
-rw-r--r--chrome/common/chrome_switches.h2
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