Add breakpad support to mojo shell apk.

This enables breakpad and save minidump locally when the application crashes,
but at this point it doesn't upload those yet.

R=viettrungluu@chromium.org, tonyg@chromium.org

Review URL: https://codereview.chromium.org/1227333002 .
diff --git a/.gitignore b/.gitignore
index 230d359..c038307 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,7 @@
 /third_party/android_tools/
 /third_party/angle/
 /third_party/appurify-python/
+/third_party/breakpad/src/
 /third_party/boringssl/src/
 /third_party/brotli/src/
 /third_party/colorama/src/
@@ -61,6 +62,7 @@
 /third_party/libjpeg_turbo/
 /third_party/llvm/
 /third_party/llvm-build/
+/third_party/lss/
 /third_party/junit/src/
 /third_party/mesa/src/
 /third_party/mockito/src/
diff --git a/DEPS b/DEPS
index 66fcaf8..95b0b00 100644
--- a/DEPS
+++ b/DEPS
@@ -27,7 +27,7 @@
   'dart_observatory_packages_revision': 'cdc4b3d4c15b9c0c8e7702dff127b440afbb7485',
   'pdfium_revision': 'b0115665b0f33971f1b7077740d51e155583cec0',
   'boringssl_revision': '642f1498d056dbba3e50ed5a232ab2f482626dec',
-  'lss_revision': 'e079768b7e3a94dcbe7d338496c0c3bde7151b6e',
+  'lss_revision': '6f97298fe3794e92c8c896a6bc06e0b36e4c3de3',
   'nss_revision': 'bb4e75a43d007518ae7d618665ea2f25b0c60b63',
   'nacl_revision': '87d5dd90911a0657c27574f78e86b7dfc4ad8b29',
   'archive_dart_revision': '07ffd98c5403b7f9ae067b57dc9487611be420f5',
@@ -154,6 +154,12 @@
 
   'src/third_party/pyelftools':
     Var('chromium_git') + '/chromiumos/third_party/pyelftools.git' + '@' + '19b3e610c86fcadb837d252c794cb5e8008826ae',
+
+  'src/third_party/breakpad/src':
+    Var('chromium_git') + '/external/google-breakpad/src.git' + '@' + '242fb9a38db6ba534b1f7daa341dd4d79171658b', # from svn revision 1471
+
+  'src/third_party/lss':
+    Var('chromium_git') + '/external/linux-syscall-support/lss.git' + '@' + Var('lss_revision'),
 }
 
 deps_os = {
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index 143ef6d..9cf9537 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -47,6 +47,7 @@
 if (!mojo_use_prebuilt_mojo_shell) {
   shell_common_deps = [
     ":parent_lib",
+    "crash",
     "//base",
     "//base/allocator",
     "//build/config/sanitizers:deps",
diff --git a/shell/android/main.cc b/shell/android/main.cc
index 6b713c0..173eb08 100644
--- a/shell/android/main.cc
+++ b/shell/android/main.cc
@@ -31,6 +31,7 @@
 #include "shell/background_application_loader.h"
 #include "shell/command_line_util.h"
 #include "shell/context.h"
+#include "shell/crash/breakpad.h"
 #include "shell/init.h"
 #include "shell/switches.h"
 #include "shell/tracer.h"
@@ -237,6 +238,10 @@
   base::CommandLine::Init(0, nullptr);
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   command_line->InitFromArgv(parameters);
+
+  base::FilePath dumps_path = base::FilePath(tmp_dir).Append("breakpad_dumps");
+  breakpad::InitCrashReporter(dumps_path);
+
   Tracer* tracer = new Tracer;
   g_internal_data.Get().tracer.reset(tracer);
   bool trace_startup = command_line->HasSwitch(switches::kTraceStartup);
diff --git a/shell/crash/BUILD.gn b/shell/crash/BUILD.gn
new file mode 100644
index 0000000..d2f0d8b
--- /dev/null
+++ b/shell/crash/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2015 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 (is_android) {
+  import("//build/config/android/config.gni")
+}
+
+source_set("crash") {
+  sources = [
+    "breakpad.cc",
+    "breakpad.h",
+  ]
+
+  if (is_android) {
+    libs = [ "log" ]
+  }
+
+  deps = [
+    "//base",
+    "//third_party/breakpad:client",
+  ]
+}
diff --git a/shell/crash/breakpad.cc b/shell/crash/breakpad.cc
new file mode 100644
index 0000000..28b74e3
--- /dev/null
+++ b/shell/crash/breakpad.cc
@@ -0,0 +1,781 @@
+// Copyright 2013 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.
+
+// This file was forked from components/crash/app/breakpad_linux.cc and
+// components/crash/app/breakpad_linux_impl.h in chromium.
+
+// For linux_syscall_support.h. This makes it safe to call embedded system
+// calls when in seccomp mode.
+
+#include "shell/crash/breakpad.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/linux_util.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/memory.h"
+#include "base/strings/string_util.h"
+#include "build/build_config.h"
+#include "third_party/breakpad/src/client/linux/crash_generation/crash_generation_client.h"
+#include "third_party/breakpad/src/client/linux/handler/exception_handler.h"
+#include "third_party/breakpad/src/client/linux/minidump_writer/directory_reader.h"
+#include "third_party/breakpad/src/common/linux/linux_libc_support.h"
+#include "third_party/breakpad/src/common/memory.h"
+#include "third_party/breakpad/src/common/simple_string_dictionary.h"
+
+#if defined(OS_ANDROID)
+#include <android/log.h>
+#include <sys/stat.h>
+
+#include "base/android/build_info.h"
+#include "base/android/path_utils.h"
+#endif
+#include "third_party/lss/linux_syscall_support.h"
+
+#if defined(OS_ANDROID)
+#define STAT_STRUCT struct stat
+#define FSTAT_FUNC fstat
+#else
+#define STAT_STRUCT struct kernel_stat
+#define FSTAT_FUNC sys_fstat
+#endif
+
+// Some versions of gcc are prone to warn about unused return values. In cases
+// where we either a) know the call cannot fail, or b) there is nothing we
+// can do when a call fails, we mark the return code as ignored. This avoids
+// spurious compiler warnings.
+#define IGNORE_RET(x) \
+  do {                \
+    if (x)            \
+      ;               \
+  } while (0)
+
+using google_breakpad::ExceptionHandler;
+using google_breakpad::MinidumpDescriptor;
+
+namespace breakpad {
+
+namespace {
+
+using CrashKeyStorage = google_breakpad::NonAllocatingMap<256, 256, 64>;
+
+// BreakpadInfo describes a crash report.
+// The minidump information can either be contained in a file descriptor (fd) or
+// in a file (whose path is in filename).
+struct BreakpadInfo {
+  int fd;                        // File descriptor to the Breakpad dump data.
+  const char* filename;          // Path to the Breakpad dump data.
+  const char* process_type;      // Process type, e.g. "renderer".
+  unsigned process_type_length;  // Length of |process_type|.
+  const char* distro;            // Linux distro string.
+  unsigned distro_length;        // Length of |distro|.
+  uint64_t process_start_time;   // Uptime of the crashing process.
+  size_t oom_size;               // Amount of memory requested if OOM.
+  uint64_t pid;                  // PID where applicable.
+  CrashKeyStorage* crash_keys;
+};
+
+// Define a preferred limit on minidump sizes, because Crash Server currently
+// throws away any larger than 1.2MB (1.2 * 1024 * 1024).  A value of -1 means
+// no limit.
+static const off_t kMaxMinidumpFileSize = 1258291;
+bool g_is_crash_reporter_enabled = false;
+uint64_t g_process_start_time = 0;
+pid_t g_pid = 0;
+ExceptionHandler* g_breakpad = nullptr;
+
+CrashKeyStorage* g_crash_keys = nullptr;
+
+#if defined(OS_ANDROID)
+const char kProductName[] = "Mojo_Android";
+#else
+const char kProductName[] = "Mojo";
+#endif
+const char kVersion[] = "1.0.0";
+
+// Writes the value |v| as 16 hex characters to the memory pointed at by
+// |output|.
+void write_uint64_hex(char* output, uint64_t v) {
+  static const char hextable[] = "0123456789abcdef";
+
+  for (int i = 15; i >= 0; --i) {
+    output[i] = hextable[v & 15];
+    v >>= 4;
+  }
+}
+
+// The following helper functions are for calculating uptime.
+
+// Converts a struct timeval to milliseconds.
+uint64_t timeval_to_ms(struct timeval* tv) {
+  uint64_t ret = tv->tv_sec;  // Avoid overflow by explicitly using a uint64_t.
+  ret *= 1000;
+  ret += tv->tv_usec / 1000;
+  return ret;
+}
+
+// Converts a struct timeval to milliseconds.
+uint64_t kernel_timeval_to_ms(struct kernel_timeval* tv) {
+  uint64_t ret = tv->tv_sec;  // Avoid overflow by explicitly using a uint64_t.
+  ret *= 1000;
+  ret += tv->tv_usec / 1000;
+  return ret;
+}
+
+// String buffer size to use to convert a uint64_t to string.
+const size_t kUint64StringSize = 21;
+
+void SetProcessStartTime() {
+  // Set the base process start time value.
+  struct timeval tv;
+  if (!gettimeofday(&tv, nullptr))
+    g_process_start_time = timeval_to_ms(&tv);
+  else
+    g_process_start_time = 0;
+}
+
+// uint64_t version of my_int_len() from
+// breakpad/src/common/linux/linux_libc_support.h. Return the length of the
+// given, non-negative integer when expressed in base 10.
+unsigned my_uint64_len(uint64_t i) {
+  if (!i)
+    return 1;
+
+  unsigned len = 0;
+  while (i) {
+    len++;
+    i /= 10;
+  }
+
+  return len;
+}
+
+// uint64_t version of my_uitos() from
+// breakpad/src/common/linux/linux_libc_support.h. Convert a non-negative
+// integer to a string (not null-terminated).
+void my_uint64tos(char* output, uint64_t i, unsigned i_len) {
+  for (unsigned index = i_len; index; --index, i /= 10)
+    output[index - 1] = '0' + (i % 10);
+}
+
+size_t LengthWithoutTrailingSpaces(const char* str, size_t len) {
+  while (len > 0 && str[len - 1] == ' ') {
+    len--;
+  }
+  return len;
+}
+
+// MIME substrings.
+const char g_rn[] = "\r\n";
+const char g_form_data_msg[] = "Content-Disposition: form-data; name=\"";
+const char g_quote_msg[] = "\"";
+const char g_dashdash_msg[] = "--";
+const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\"";
+const char g_content_type_msg[] = "Content-Type: application/octet-stream";
+
+// MimeWriter manages an iovec for writing MIMEs to a file.
+class MimeWriter {
+ public:
+  static const int kIovCapacity = 30;
+  static const size_t kMaxCrashChunkSize = 64;
+
+  MimeWriter(int fd, const char* const mime_boundary);
+  ~MimeWriter();
+
+  // Append boundary.
+  virtual void AddBoundary();
+
+  // Append end of file boundary.
+  virtual void AddEnd();
+
+  // Append key/value pair with specified sizes.
+  virtual void AddPairData(const char* msg_type,
+                           size_t msg_type_size,
+                           const char* msg_data,
+                           size_t msg_data_size);
+
+  // Append key/value pair.
+  void AddPairString(const char* msg_type, const char* msg_data) {
+    AddPairData(msg_type, my_strlen(msg_type), msg_data, my_strlen(msg_data));
+  }
+
+  // Append key/value pair, splitting value into chunks no larger than
+  // |chunk_size|. |chunk_size| cannot be greater than |kMaxCrashChunkSize|.
+  // The msg_type string will have a counter suffix to distinguish each chunk.
+  virtual void AddPairDataInChunks(const char* msg_type,
+                                   size_t msg_type_size,
+                                   const char* msg_data,
+                                   size_t msg_data_size,
+                                   size_t chunk_size,
+                                   bool strip_trailing_spaces);
+
+  // Add binary file contents to be uploaded with the specified filename.
+  virtual void AddFileContents(const char* filename_msg,
+                               uint8_t* file_data,
+                               size_t file_size);
+
+  // Flush any pending iovecs to the output file.
+  void Flush() {
+    IGNORE_RET(sys_writev(fd_, iov_, iov_index_));
+    iov_index_ = 0;
+  }
+
+ protected:
+  void AddItem(const void* base, size_t size);
+  // Minor performance trade-off for easier-to-maintain code.
+  void AddString(const char* str) { AddItem(str, my_strlen(str)); }
+  void AddItemWithoutTrailingSpaces(const void* base, size_t size);
+
+  struct kernel_iovec iov_[kIovCapacity];
+  int iov_index_;
+
+  // Output file descriptor.
+  int fd_;
+
+  const char* const mime_boundary_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MimeWriter);
+};
+
+MimeWriter::MimeWriter(int fd, const char* const mime_boundary)
+    : iov_index_(0), fd_(fd), mime_boundary_(mime_boundary) {
+}
+
+MimeWriter::~MimeWriter() {
+}
+
+void MimeWriter::AddBoundary() {
+  AddString(mime_boundary_);
+  AddString(g_rn);
+}
+
+void MimeWriter::AddEnd() {
+  AddString(mime_boundary_);
+  AddString(g_dashdash_msg);
+  AddString(g_rn);
+}
+
+void MimeWriter::AddPairData(const char* msg_type,
+                             size_t msg_type_size,
+                             const char* msg_data,
+                             size_t msg_data_size) {
+  AddString(g_form_data_msg);
+  AddItem(msg_type, msg_type_size);
+  AddString(g_quote_msg);
+  AddString(g_rn);
+  AddString(g_rn);
+  AddItem(msg_data, msg_data_size);
+  AddString(g_rn);
+}
+
+void MimeWriter::AddPairDataInChunks(const char* msg_type,
+                                     size_t msg_type_size,
+                                     const char* msg_data,
+                                     size_t msg_data_size,
+                                     size_t chunk_size,
+                                     bool strip_trailing_spaces) {
+  if (chunk_size > kMaxCrashChunkSize)
+    return;
+
+  unsigned i = 0;
+  size_t done = 0, msg_length = msg_data_size;
+
+  while (msg_length) {
+    char num[kUint64StringSize];
+    const unsigned num_len = my_uint_len(++i);
+    my_uitos(num, i, num_len);
+
+    size_t chunk_len = std::min(chunk_size, msg_length);
+
+    AddString(g_form_data_msg);
+    AddItem(msg_type, msg_type_size);
+    AddItem(num, num_len);
+    AddString(g_quote_msg);
+    AddString(g_rn);
+    AddString(g_rn);
+    if (strip_trailing_spaces) {
+      AddItemWithoutTrailingSpaces(msg_data + done, chunk_len);
+    } else {
+      AddItem(msg_data + done, chunk_len);
+    }
+    AddString(g_rn);
+    AddBoundary();
+    Flush();
+
+    done += chunk_len;
+    msg_length -= chunk_len;
+  }
+}
+
+void MimeWriter::AddFileContents(const char* filename_msg,
+                                 uint8_t* file_data,
+                                 size_t file_size) {
+  AddString(g_form_data_msg);
+  AddString(filename_msg);
+  AddString(g_rn);
+  AddString(g_content_type_msg);
+  AddString(g_rn);
+  AddString(g_rn);
+  AddItem(file_data, file_size);
+  AddString(g_rn);
+}
+
+void MimeWriter::AddItem(const void* base, size_t size) {
+  // Check if the iovec is full and needs to be flushed to output file.
+  if (iov_index_ == kIovCapacity) {
+    Flush();
+  }
+  iov_[iov_index_].iov_base = const_cast<void*>(base);
+  iov_[iov_index_].iov_len = size;
+  ++iov_index_;
+}
+
+void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) {
+  AddItem(base,
+          LengthWithoutTrailingSpaces(static_cast<const char*>(base), size));
+}
+
+void DumpProcess() {
+  if (g_breakpad)
+    g_breakpad->WriteMinidump();
+}
+
+#if defined(OS_ANDROID)
+const char kGoogleBreakpad[] = "google-breakpad";
+#endif
+
+size_t WriteLog(const char* buf, size_t nbytes) {
+#if defined(OS_ANDROID)
+  return __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, buf);
+#else
+  return sys_write(2, buf, nbytes);
+#endif
+}
+
+#if defined(OS_ANDROID)
+void AndroidLogWriteHorizontalRule() {
+  __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad,
+                      "### ### ### ### ### ### ### ### ### ### ### ### ###");
+}
+
+// Android's native crash handler outputs a diagnostic tombstone to the device
+// log. By returning false from the HandlerCallbacks, breakpad will reinstall
+// the previous (i.e. native) signal handlers before returning from its own
+// handler. A Mojo shell build fingerprint is written to the log, so that the
+// specific build of the shell and the location of the archived shell symbols
+// can be determined directly from it.
+bool FinalizeCrashDoneAndroid() {
+  base::android::BuildInfo* android_build_info =
+      base::android::BuildInfo::GetInstance();
+
+  AndroidLogWriteHorizontalRule();
+  __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad,
+                      "Mojo shell build fingerprint:");
+  __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad,
+                      android_build_info->package_version_name());
+  __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad,
+                      android_build_info->package_version_code());
+  AndroidLogWriteHorizontalRule();
+
+  return false;
+}
+#endif
+
+void LoadDataFromFD(google_breakpad::PageAllocator& allocator,
+                    int fd,
+                    bool close_fd,
+                    uint8_t** file_data,
+                    size_t* size) {
+  STAT_STRUCT st;
+  if (FSTAT_FUNC(fd, &st) != 0) {
+    static const char msg[] = "Cannot upload crash dump: stat failed\n";
+    WriteLog(msg, sizeof(msg) - 1);
+    if (close_fd)
+      IGNORE_RET(sys_close(fd));
+    return;
+  }
+
+  *file_data = reinterpret_cast<uint8_t*>(allocator.Alloc(st.st_size));
+  if (!(*file_data)) {
+    static const char msg[] = "Cannot upload crash dump: cannot alloc\n";
+    WriteLog(msg, sizeof(msg) - 1);
+    if (close_fd)
+      IGNORE_RET(sys_close(fd));
+    return;
+  }
+  my_memset(*file_data, 0xf, st.st_size);
+
+  *size = st.st_size;
+  int byte_read = sys_read(fd, *file_data, *size);
+  if (byte_read == -1) {
+    static const char msg[] = "Cannot upload crash dump: read failed\n";
+    WriteLog(msg, sizeof(msg) - 1);
+    if (close_fd)
+      IGNORE_RET(sys_close(fd));
+    return;
+  }
+
+  if (close_fd)
+    IGNORE_RET(sys_close(fd));
+}
+
+void LoadDataFromFile(google_breakpad::PageAllocator& allocator,
+                      const char* filename,
+                      int* fd,
+                      uint8_t** file_data,
+                      size_t* size) {
+  // WARNING: this code runs in a compromised context. It may not call into
+  // libc nor allocate memory normally.
+  *fd = sys_open(filename, O_RDONLY, 0);
+  *size = 0;
+
+  if (*fd < 0) {
+    static const char msg[] = "Cannot upload crash dump: failed to open\n";
+    WriteLog(msg, sizeof(msg) - 1);
+    return;
+  }
+
+  LoadDataFromFD(allocator, *fd, true, file_data, size);
+}
+
+void HandleCrashDump(const BreakpadInfo& info) {
+  int dumpfd;
+  bool keep_fd = false;
+  size_t dump_size;
+  uint8_t* dump_data;
+  google_breakpad::PageAllocator allocator;
+
+  if (info.fd != -1) {
+    // Dump is provided with an open FD.
+    keep_fd = true;
+    dumpfd = info.fd;
+
+    // The FD is pointing to the end of the file.
+    // Rewind, we'll read the data next.
+    if (lseek(dumpfd, 0, SEEK_SET) == -1) {
+      static const char msg[] =
+          "Cannot upload crash dump: failed to "
+          "reposition minidump FD\n";
+      WriteLog(msg, sizeof(msg) - 1);
+      IGNORE_RET(sys_close(dumpfd));
+      return;
+    }
+    LoadDataFromFD(allocator, info.fd, false, &dump_data, &dump_size);
+  } else {
+    // Dump is provided with a path.
+    keep_fd = false;
+    LoadDataFromFile(allocator, info.filename, &dumpfd, &dump_data, &dump_size);
+  }
+
+  // We need to build a MIME block for uploading to the server. Since we are
+  // going to fork and run wget, it needs to be written to a temp file.
+  const int ufd = sys_open("/dev/urandom", O_RDONLY, 0);
+  if (ufd < 0) {
+    static const char msg[] =
+        "Cannot upload crash dump because /dev/urandom"
+        " is missing\n";
+    WriteLog(msg, sizeof(msg) - 1);
+    return;
+  }
+
+  int temp_file_fd = -1;
+  if (keep_fd) {
+    LOG(WARNING) << "Keeping fd...";
+    temp_file_fd = dumpfd;
+    // Rewind the destination, we are going to overwrite it.
+    if (lseek(dumpfd, 0, SEEK_SET) == -1) {
+      static const char msg[] =
+          "Cannot upload crash dump: failed to "
+          "reposition minidump FD (2)\n";
+      WriteLog(msg, sizeof(msg) - 1);
+      IGNORE_RET(sys_close(dumpfd));
+      return;
+    }
+  } else {
+    LOG(WARNING) << "Opening file: " << info.filename;
+    temp_file_fd = sys_open(info.filename, O_WRONLY, 0600);
+    if (temp_file_fd < 0) {
+      static const char msg[] = "Failed to save crash dump: failed to open\n";
+      WriteLog(msg, sizeof(msg) - 1);
+      IGNORE_RET(sys_close(ufd));
+      return;
+    }
+  }
+
+  // The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL.
+  char mime_boundary[28 + 16 + 1];
+  my_memset(mime_boundary, '-', 28);
+  uint64_t boundary_rand;
+  sys_read(ufd, &boundary_rand, sizeof(boundary_rand));
+  write_uint64_hex(mime_boundary + 28, boundary_rand);
+  mime_boundary[28 + 16] = 0;
+  IGNORE_RET(sys_close(ufd));
+
+  // The MIME block looks like this:
+  //   BOUNDARY \r\n
+  //   Content-Disposition: form-data; name="prod" \r\n \r\n
+  //   MojoShell \r\n
+  //   BOUNDARY \r\n
+  //   Content-Disposition: form-data; name="ver" \r\n \r\n
+  //   1.2.3.4 \r\n
+  //   BOUNDARY \r\n
+  //
+  //   zero or one:
+  //   Content-Disposition: form-data; name="ptime" \r\n \r\n
+  //   abcdef \r\n
+  //   BOUNDARY \r\n
+  //
+  //   zero or one:
+  //   Content-Disposition: form-data; name="ptype" \r\n \r\n
+  //   abcdef \r\n
+  //   BOUNDARY \r\n
+  //
+  //   zero or one:
+  //   Content-Disposition: form-data; name="lsb-release" \r\n \r\n
+  //   abcdef \r\n
+  //   BOUNDARY \r\n
+  //
+  //   zero or one:
+  //   Content-Disposition: form-data; name="oom-size" \r\n \r\n
+  //   1234567890 \r\n
+  //   BOUNDARY \r\n
+  //
+  //   zero or more (up to CrashKeyStorage::num_entries = 64):
+  //   Content-Disposition: form-data; name=crash-key-name \r\n
+  //   crash-key-value \r\n
+  //   BOUNDARY \r\n
+  //
+  //   Content-Disposition: form-data; name="dump"; filename="dump" \r\n
+  //   Content-Type: application/octet-stream \r\n \r\n
+  //   <dump contents>
+  //   \r\n BOUNDARY -- \r\n
+
+  MimeWriter writer(temp_file_fd, mime_boundary);
+  {
+    writer.AddBoundary();
+    writer.AddPairString("prod", kProductName);
+    writer.AddBoundary();
+    writer.AddPairString("ver", kVersion);
+    writer.AddBoundary();
+    if (info.pid > 0) {
+      char pid_value_buf[kUint64StringSize];
+      uint64_t pid_value_len = my_uint64_len(info.pid);
+      my_uint64tos(pid_value_buf, info.pid, pid_value_len);
+      static const char pid_key_name[] = "pid";
+      writer.AddPairData(pid_key_name, sizeof(pid_key_name) - 1, pid_value_buf,
+                         pid_value_len);
+      writer.AddBoundary();
+    }
+#if defined(OS_ANDROID)
+    // Addtional MIME blocks are added for logging on Android devices.
+    static const char android_build_id[] = "android_build_id";
+    static const char android_build_fp[] = "android_build_fp";
+    static const char device[] = "device";
+    static const char model[] = "model";
+    static const char brand[] = "brand";
+    static const char exception_info[] = "exception_info";
+
+    base::android::BuildInfo* android_build_info =
+        base::android::BuildInfo::GetInstance();
+    writer.AddPairString(android_build_id,
+                         android_build_info->android_build_id());
+    writer.AddBoundary();
+    writer.AddPairString(android_build_fp,
+                         android_build_info->android_build_fp());
+    writer.AddBoundary();
+    writer.AddPairString(device, android_build_info->device());
+    writer.AddBoundary();
+    writer.AddPairString(model, android_build_info->model());
+    writer.AddBoundary();
+    writer.AddPairString(brand, android_build_info->brand());
+    writer.AddBoundary();
+    if (android_build_info->java_exception_info() != nullptr) {
+      writer.AddPairString(exception_info,
+                           android_build_info->java_exception_info());
+      writer.AddBoundary();
+    }
+#endif
+    writer.Flush();
+  }
+
+  if (info.process_start_time > 0) {
+    struct kernel_timeval tv;
+    if (!sys_gettimeofday(&tv, nullptr)) {
+      uint64_t time = kernel_timeval_to_ms(&tv);
+      if (time > info.process_start_time) {
+        time -= info.process_start_time;
+        char time_str[kUint64StringSize];
+        const unsigned time_len = my_uint64_len(time);
+        my_uint64tos(time_str, time, time_len);
+
+        static const char process_time_msg[] = "ptime";
+        writer.AddPairData(process_time_msg, sizeof(process_time_msg) - 1,
+                           time_str, time_len);
+        writer.AddBoundary();
+        writer.Flush();
+      }
+    }
+  }
+
+  if (info.process_type_length) {
+    writer.AddPairString("ptype", info.process_type);
+    writer.AddBoundary();
+    writer.Flush();
+  }
+
+  if (info.distro_length) {
+    static const char distro_msg[] = "lsb-release";
+    writer.AddPairString(distro_msg, info.distro);
+    writer.AddBoundary();
+    writer.Flush();
+  }
+
+  if (info.oom_size) {
+    char oom_size_str[kUint64StringSize];
+    const unsigned oom_size_len = my_uint64_len(info.oom_size);
+    my_uint64tos(oom_size_str, info.oom_size, oom_size_len);
+    static const char oom_size_msg[] = "oom-size";
+    writer.AddPairData(oom_size_msg, sizeof(oom_size_msg) - 1, oom_size_str,
+                       oom_size_len);
+    writer.AddBoundary();
+    writer.Flush();
+  }
+
+  if (info.crash_keys) {
+    CrashKeyStorage::Iterator crash_key_iterator(*info.crash_keys);
+    const CrashKeyStorage::Entry* entry;
+    while ((entry = crash_key_iterator.Next())) {
+      writer.AddPairString(entry->key, entry->value);
+      writer.AddBoundary();
+      writer.Flush();
+    }
+  }
+
+  writer.AddFileContents(g_dump_msg, dump_data, dump_size);
+  writer.AddEnd();
+  writer.Flush();
+
+  IGNORE_RET(sys_close(temp_file_fd));
+}
+
+bool CrashDone(const MinidumpDescriptor& minidump, const bool succeeded) {
+  // WARNING: this code runs in a compromised context. It may not call into
+  // libc nor allocate memory normally.
+  if (!succeeded) {
+    const char msg[] = "Failed to generate minidump.";
+    WriteLog(msg, sizeof(msg) - 1);
+    return false;
+  }
+
+  DCHECK(!minidump.IsFD());
+
+  BreakpadInfo info = {0};
+  info.filename = minidump.path();
+  info.fd = minidump.fd();
+  info.process_type = "shell";
+  info.process_type_length = 7;
+  info.distro = base::g_linux_distro;
+  info.distro_length = my_strlen(base::g_linux_distro);
+  info.process_start_time = g_process_start_time;
+  info.oom_size = base::g_oom_size;
+  info.pid = g_pid;
+  info.crash_keys = g_crash_keys;
+  HandleCrashDump(info);
+#if defined(OS_ANDROID)
+  return FinalizeCrashDoneAndroid();
+#else
+  return true;
+#endif
+}
+
+// Wrapper function, do not add more code here.
+bool CrashDoneNoUpload(const MinidumpDescriptor& minidump,
+                       void* context,
+                       bool succeeded) {
+  LOG(WARNING) << "CrashDoneNoUpload";
+  return CrashDone(minidump, succeeded);
+}
+
+void EnableCrashDumping(const base::FilePath& dumps_path) {
+  g_is_crash_reporter_enabled = true;
+
+  DCHECK(!g_breakpad);
+  DCHECK(base::CreateDirectoryAndGetError(dumps_path, nullptr));
+  MinidumpDescriptor minidump_descriptor(dumps_path.value());
+  minidump_descriptor.set_size_limit(kMaxMinidumpFileSize);
+  g_breakpad = new ExceptionHandler(
+      minidump_descriptor, nullptr, CrashDoneNoUpload, nullptr,
+      true,  // Install handlers.
+      -1);   // Server file descriptor. -1 for in-process.
+}
+
+void SetCrashKeyValue(const base::StringPiece& key,
+                      const base::StringPiece& value) {
+  g_crash_keys->SetKeyValue(key.data(), value.data());
+}
+
+void ClearCrashKey(const base::StringPiece& key) {
+  g_crash_keys->RemoveKey(key.data());
+}
+
+void RegisterCrashKeys() {
+  // TODO(qsr): Is there any key to register here?
+}
+
+void InitCrashKeys() {
+  g_crash_keys = new CrashKeyStorage;
+  RegisterCrashKeys();
+  base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValue, &ClearCrashKey);
+}
+
+// Miscellaneous initialization functions to call after Breakpad has been
+// enabled.
+void PostEnableBreakpadInitialization() {
+  SetProcessStartTime();
+  g_pid = getpid();
+
+  base::debug::SetDumpWithoutCrashingFunction(&DumpProcess);
+}
+
+}  // namespace
+
+void InitCrashReporter(const base::FilePath& dumps_path) {
+#if defined(OS_ANDROID)
+  // This will guarantee that the BuildInfo has been initialized and subsequent
+  // calls will not require memory allocation.
+  base::android::BuildInfo::GetInstance();
+#endif
+
+  InitCrashKeys();
+  EnableCrashDumping(dumps_path);
+
+  PostEnableBreakpadInitialization();
+}
+
+bool IsCrashReporterEnabled() {
+  return g_is_crash_reporter_enabled;
+}
+
+}  // namespace breakpad
diff --git a/shell/crash/breakpad.h b/shell/crash/breakpad.h
new file mode 100644
index 0000000..e180ca2
--- /dev/null
+++ b/shell/crash/breakpad.h
@@ -0,0 +1,24 @@
+// Copyright 2013 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.
+
+// This file was forked from components/crash/app/breakpad_linux.h in chromium.
+
+// Public interface for enabling Breakpad on Linux systems.
+
+#ifndef SHELL_CRASH_BREAKPAD_H_
+#define SHELL_CRASH_BREAKPAD_H_
+
+#include "base/files/file_path.h"
+
+namespace breakpad {
+
+// Turns on the crash reporter.
+void InitCrashReporter(const base::FilePath& dumps_path);
+
+// Checks if crash reporting is enabled.
+bool IsCrashReporterEnabled();
+
+}  // namespace breakpad
+
+#endif  // SHELL_CRASH_BREAKPAD_H_
diff --git a/third_party/breakpad/BUILD.gn b/third_party/breakpad/BUILD.gn
new file mode 100644
index 0000000..ad1011e
--- /dev/null
+++ b/third_party/breakpad/BUILD.gn
@@ -0,0 +1,796 @@
+# Copyright 2014 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.
+
+# This file was forked from breakpad/BUILD.gn in chromium.
+
+import("//testing/test.gni")
+
+config("tools_config") {
+  include_dirs = [
+    "src",
+    "src/third_party",
+  ]
+  if (is_android) {
+    defines = [ "__ANDROID__" ]
+  }
+  if (is_clang) {
+    cflags = [ "-Wno-tautological-constant-out-of-range-compare" ]
+  }
+}
+
+config("internal_config") {
+  include_dirs = [ "src" ]
+  defines = []
+  if (is_debug) {
+    # This is needed for GTMLogger to work correctly.
+    defines += [ "DEBUG" ]
+  }
+  if (is_android) {
+    defines += [ "__ANDROID__" ]
+  }
+}
+
+config("client_config") {
+  include_dirs = [ "src" ]
+  if (is_android) {
+    include_dirs += [ "src/common/android/include" ]
+  }
+}
+
+config("handler_config") {
+  include_dirs = [ "src" ]
+}
+
+config("sender_config") {
+  include_dirs = [ "src" ]
+}
+
+# {micro,mini}dump_stackwalk and minidump_dump are tool-type executables that do
+# not build on iOS.
+if (current_toolchain == host_toolchain && !is_win) {
+  # Contains the code shared by both {micro,mini}dump_stackwalk.
+  static_library("stackwalk_common") {
+    sources = [
+      "src/processor/basic_code_module.h",
+      "src/processor/basic_code_modules.cc",
+      "src/processor/basic_code_modules.h",
+      "src/processor/basic_source_line_resolver.cc",
+      "src/processor/binarystream.cc",
+      "src/processor/binarystream.h",
+      "src/processor/call_stack.cc",
+      "src/processor/cfi_frame_info.cc",
+      "src/processor/cfi_frame_info.h",
+      "src/processor/disassembler_x86.cc",
+      "src/processor/disassembler_x86.h",
+      "src/processor/dump_context.cc",
+      "src/processor/dump_object.cc",
+      "src/processor/logging.cc",
+      "src/processor/logging.h",
+      "src/processor/pathname_stripper.cc",
+      "src/processor/pathname_stripper.h",
+      "src/processor/process_state.cc",
+      "src/processor/simple_symbol_supplier.cc",
+      "src/processor/simple_symbol_supplier.h",
+      "src/processor/source_line_resolver_base.cc",
+      "src/processor/stack_frame_cpu.cc",
+      "src/processor/stack_frame_symbolizer.cc",
+      "src/processor/stackwalk_common.cc",
+      "src/processor/stackwalker.cc",
+      "src/processor/stackwalker_amd64.cc",
+      "src/processor/stackwalker_amd64.h",
+      "src/processor/stackwalker_arm.cc",
+      "src/processor/stackwalker_arm.h",
+      "src/processor/stackwalker_arm64.cc",
+      "src/processor/stackwalker_arm64.h",
+      "src/processor/stackwalker_mips.cc",
+      "src/processor/stackwalker_mips.h",
+      "src/processor/stackwalker_ppc.cc",
+      "src/processor/stackwalker_ppc.h",
+      "src/processor/stackwalker_ppc64.cc",
+      "src/processor/stackwalker_ppc64.h",
+      "src/processor/stackwalker_sparc.cc",
+      "src/processor/stackwalker_sparc.h",
+      "src/processor/stackwalker_x86.cc",
+      "src/processor/stackwalker_x86.h",
+      "src/processor/tokenize.cc",
+      "src/processor/tokenize.h",
+
+      # libdisasm
+      "src/third_party/libdisasm/ia32_implicit.c",
+      "src/third_party/libdisasm/ia32_implicit.h",
+      "src/third_party/libdisasm/ia32_insn.c",
+      "src/third_party/libdisasm/ia32_insn.h",
+      "src/third_party/libdisasm/ia32_invariant.c",
+      "src/third_party/libdisasm/ia32_invariant.h",
+      "src/third_party/libdisasm/ia32_modrm.c",
+      "src/third_party/libdisasm/ia32_modrm.h",
+      "src/third_party/libdisasm/ia32_opcode_tables.c",
+      "src/third_party/libdisasm/ia32_opcode_tables.h",
+      "src/third_party/libdisasm/ia32_operand.c",
+      "src/third_party/libdisasm/ia32_operand.h",
+      "src/third_party/libdisasm/ia32_reg.c",
+      "src/third_party/libdisasm/ia32_reg.h",
+      "src/third_party/libdisasm/ia32_settings.c",
+      "src/third_party/libdisasm/ia32_settings.h",
+      "src/third_party/libdisasm/libdis.h",
+      "src/third_party/libdisasm/qword.h",
+      "src/third_party/libdisasm/x86_disasm.c",
+      "src/third_party/libdisasm/x86_format.c",
+      "src/third_party/libdisasm/x86_imm.c",
+      "src/third_party/libdisasm/x86_imm.h",
+      "src/third_party/libdisasm/x86_insn.c",
+      "src/third_party/libdisasm/x86_misc.c",
+      "src/third_party/libdisasm/x86_operand_list.c",
+      "src/third_party/libdisasm/x86_operand_list.h",
+    ]
+
+    defines = [ "BPLOG_MINIMUM_SEVERITY=SEVERITY_ERROR" ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ ":tools_config" ]
+  }
+
+  executable("microdump_stackwalk") {
+    sources = [
+      "src/processor/microdump.cc",
+      "src/processor/microdump_processor.cc",
+      "src/processor/microdump_stackwalk.cc",
+    ]
+
+    deps = [
+      ":stackwalk_common",
+    ]
+
+    defines = [ "BPLOG_MINIMUM_SEVERITY=SEVERITY_ERROR" ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ ":tools_config" ]
+  }
+
+  executable("minidump_stackwalk") {
+    sources = [
+      "src/processor/exploitability.cc",
+      "src/processor/minidump.cc",
+      "src/processor/minidump_processor.cc",
+      "src/processor/minidump_stackwalk.cc",
+    ]
+
+    deps = [
+      ":stackwalk_common",
+    ]
+
+    defines = [ "BPLOG_MINIMUM_SEVERITY=SEVERITY_ERROR" ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ ":tools_config" ]
+
+    # Always want these files included regardless of platform.
+    set_sources_assignment_filter([])
+    sources += [
+      "src/processor/exploitability_linux.cc",
+      "src/processor/exploitability_linux.h",
+      "src/processor/exploitability_win.cc",
+      "src/processor/exploitability_win.h",
+      "src/processor/symbolic_constants_win.cc",
+      "src/processor/symbolic_constants_win.h",
+    ]
+  }
+
+  executable("minidump_dump") {
+    sources = [
+      "src/processor/basic_code_module.h",
+      "src/processor/basic_code_modules.cc",
+      "src/processor/basic_code_modules.h",
+      "src/processor/dump_context.cc",
+      "src/processor/dump_object.cc",
+      "src/processor/logging.cc",
+      "src/processor/logging.h",
+      "src/processor/minidump.cc",
+      "src/processor/minidump_dump.cc",
+      "src/processor/pathname_stripper.cc",
+      "src/processor/pathname_stripper.h",
+    ]
+
+    configs += [ ":tools_config" ]
+
+    # There are some warnings in this code.
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+  }
+}
+
+# Mac --------------------------------------------------------------------------
+
+if (current_toolchain == host_toolchain && is_mac) {
+  # TODO(GYP) This should be only 64-bit on Mac. From .gypi:
+  # Like ld, dump_syms needs to operate on enough data that it may
+  # actually need to be able to address more than 4GB. Use x86_64.
+  # Don't worry! An x86_64 dump_syms is perfectly able to dump
+  # 32-bit files.
+  executable("dump_syms") {
+    sources = [
+      "src/common/dwarf/bytereader.cc",
+      "src/common/dwarf/dwarf2diehandler.cc",
+      "src/common/dwarf/dwarf2reader.cc",
+      "src/common/dwarf_cfi_to_module.cc",
+      "src/common/dwarf_cu_to_module.cc",
+      "src/common/dwarf_line_to_module.cc",
+      "src/common/language.cc",
+      "src/common/mac/arch_utilities.cc",
+      "src/common/mac/arch_utilities.h",
+      "src/common/mac/dump_syms.mm",
+      "src/common/mac/file_id.cc",
+      "src/common/mac/macho_id.cc",
+      "src/common/mac/macho_reader.cc",
+      "src/common/mac/macho_utilities.cc",
+      "src/common/mac/macho_walker.cc",
+      "src/common/md5.cc",
+      "src/common/module.cc",
+      "src/common/stabs_reader.cc",
+      "src/common/stabs_to_module.cc",
+      "src/tools/mac/dump_syms/dump_syms_tool.mm",
+    ]
+
+    # For src/common/stabs_reader.h.
+    defines = [ "HAVE_MACH_O_NLIST_H" ]
+    include_dirs = [ "src/common/mac" ]
+
+    # The DWARF utilities require -funsigned-char.
+    cflags = [ "-funsigned-char" ]
+
+    configs += [ ":internal_config" ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+
+    # dwarf2reader.cc uses dynamic_cast.
+    configs -= [ "//build/config/compiler:no_rtti" ]
+    configs += [ "//build/config/compiler:rtti" ]
+
+    libs = [ "Foundation.framework" ]
+
+    if (!is_debug) {
+      # dump_syms crashes when built at -O1, -O2, and -O3.  It does
+      # not crash at -Os.  To play it safe, dump_syms is always built
+      # at -O0 until this can be sorted out.
+      # http://code.google.com/p/google-breakpad/issues/detail?id=329
+      configs -= [ "//build/config/compiler:optimize" ]
+      cflags += [ "-O0" ]
+    }
+  }
+
+  executable("symupload") {
+    sources = [
+      "src/common/mac/HTTPMultipartUpload.m",
+      "src/tools/mac/symupload/symupload.m",
+    ]
+
+    include_dirs = [ "src/common/mac" ]
+
+    libs = [ "Foundation.framework" ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+  }
+}
+
+if (is_mac) {
+  static_library("utilities") {
+    sources = [
+      "src/client/mac/crash_generation/ConfigFile.mm",
+      "src/client/mac/handler/breakpad_nlist_64.cc",
+      "src/client/mac/handler/dynamic_images.cc",
+      "src/client/mac/handler/minidump_generator.cc",
+      "src/client/minidump_file_writer.cc",
+      "src/common/convert_UTF.c",
+      "src/common/mac/MachIPC.mm",
+      "src/common/mac/arch_utilities.cc",
+      "src/common/mac/bootstrap_compat.cc",
+      "src/common/mac/file_id.cc",
+      "src/common/mac/launch_reporter.cc",
+      "src/common/mac/macho_id.cc",
+      "src/common/mac/macho_utilities.cc",
+      "src/common/mac/macho_walker.cc",
+      "src/common/mac/string_utilities.cc",
+      "src/common/md5.cc",
+      "src/common/simple_string_dictionary.cc",
+      "src/common/string_conversion.cc",
+    ]
+
+    configs += [ ":internal_config" ]
+
+    # There are some warnings in this code.
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+  }
+
+  executable("crash_inspector") {
+    sources = [
+      "src/client/mac/crash_generation/Inspector.mm",
+      "src/client/mac/crash_generation/InspectorMain.mm",
+    ]
+
+    # TODO(GYP): 'mac_real_dsym': 1,
+
+    include_dirs = [
+      "src/client/apple/Framework",
+      "src/common/mac",
+      "src",
+    ]
+    libs = [
+      "CoreServices.framework",
+      "Foundation.framework",
+    ]
+
+    deps = [
+      ":utilities",
+    ]
+  }
+
+  # TODO(GYP) this target has some mac_bundle_resources stuff.
+  # executable("crash_report_sender") {
+  # }
+  group("crash_report_sender") {
+  }
+
+  config("breakpad_config") {
+    include_dirs = [ "src/client/apple/Framework" ]
+  }
+
+  static_library("breakpad") {
+    sources = [
+      "src/client/mac/Framework/Breakpad.mm",
+      "src/client/mac/Framework/OnDemandServer.mm",
+      "src/client/mac/crash_generation/crash_generation_client.cc",
+      "src/client/mac/crash_generation/crash_generation_client.h",
+      "src/client/mac/handler/exception_handler.cc",
+      "src/client/mac/handler/protected_memory_allocator.cc",
+    ]
+
+    configs += [ ":internal_config" ]
+    public_configs = [ ":breakpad_config" ]
+
+    defines = [ "USE_PROTECTED_ALLOCATIONS=1" ]
+    include_dirs = [ "src/client/apple/Framework" ]
+
+    deps = [
+      ":utilities",
+      ":crash_inspector",
+      ":crash_report_sender",
+    ]
+  }
+
+  group("client") {
+    public_configs = [ ":client_config" ]
+  }
+}
+
+if (is_linux) {
+  executable("symupload") {
+    sources = [
+      "src/common/linux/http_upload.cc",
+      "src/common/linux/http_upload.h",
+      "src/tools/linux/symupload/sym_upload.cc",
+    ]
+
+    include_dirs = [
+      "src",
+      "src/third_party",
+    ]
+
+    configs += [ ":tools_config" ]
+
+    libs = [ "dl" ]
+  }
+}
+
+if (is_linux || is_android) {
+  if (current_toolchain == host_toolchain) {
+    # dump_syms is a host tool, so only compile it for the host system.
+    executable("dump_syms") {
+      sources = [
+        "src/common/dwarf/bytereader.cc",
+        "src/common/dwarf/dwarf2diehandler.cc",
+        "src/common/dwarf/dwarf2reader.cc",
+        "src/common/dwarf_cfi_to_module.cc",
+        "src/common/dwarf_cfi_to_module.h",
+        "src/common/dwarf_cu_to_module.cc",
+        "src/common/dwarf_cu_to_module.h",
+        "src/common/dwarf_line_to_module.cc",
+        "src/common/dwarf_line_to_module.h",
+        "src/common/language.cc",
+        "src/common/language.h",
+        "src/common/linux/crc32.cc",
+        "src/common/linux/crc32.h",
+        "src/common/linux/dump_symbols.cc",
+        "src/common/linux/dump_symbols.h",
+        "src/common/linux/elf_symbols_to_module.cc",
+        "src/common/linux/elf_symbols_to_module.h",
+        "src/common/linux/elfutils.cc",
+        "src/common/linux/elfutils.h",
+        "src/common/linux/file_id.cc",
+        "src/common/linux/file_id.h",
+        "src/common/linux/guid_creator.h",
+        "src/common/linux/linux_libc_support.cc",
+        "src/common/linux/linux_libc_support.h",
+        "src/common/linux/memory_mapped_file.cc",
+        "src/common/linux/memory_mapped_file.h",
+        "src/common/module.cc",
+        "src/common/module.h",
+        "src/common/stabs_reader.cc",
+        "src/common/stabs_reader.h",
+        "src/common/stabs_to_module.cc",
+        "src/common/stabs_to_module.h",
+        "src/tools/linux/dump_syms/dump_syms.cc",
+      ]
+
+      # There are some warnings in this code.
+      configs -= [ "//build/config/compiler:chromium_code" ]
+      configs += [ "//build/config/compiler:no_chromium_code" ]
+
+      # dwarf2reader.cc uses dynamic_cast. Because we don't typically
+      # don't support RTTI, we enable it for this single target. Since
+      # dump_syms doesn't share any object files with anything else,
+      # this doesn't end up polluting Chrome itself.
+      configs -= [ "//build/config/compiler:no_rtti" ]
+      configs += [ "//build/config/compiler:rtti" ]
+
+      # Breakpad rev 583 introduced this flag.
+      # Using this define, stabs_reader.h will include a.out.h to
+      # build on Linux.
+      defines = [ "HAVE_A_OUT_H" ]
+
+      include_dirs = [ "src" ]
+    }
+  }
+
+  static_library("client") {
+    # Want all these sources for both Linux and Android.
+    set_sources_assignment_filter([])
+    sources = [
+      "src/client/linux/crash_generation/crash_generation_client.cc",
+      "src/client/linux/crash_generation/crash_generation_client.h",
+      "src/client/linux/dump_writer_common/mapping_info.h",
+      "src/client/linux/dump_writer_common/seccomp_unwinder.cc",
+      "src/client/linux/dump_writer_common/seccomp_unwinder.h",
+      "src/client/linux/dump_writer_common/thread_info.cc",
+      "src/client/linux/dump_writer_common/thread_info.h",
+      "src/client/linux/dump_writer_common/ucontext_reader.cc",
+      "src/client/linux/dump_writer_common/ucontext_reader.h",
+      "src/client/linux/handler/exception_handler.cc",
+      "src/client/linux/handler/exception_handler.h",
+      "src/client/linux/handler/minidump_descriptor.cc",
+      "src/client/linux/handler/minidump_descriptor.h",
+      "src/client/linux/log/log.cc",
+      "src/client/linux/log/log.h",
+      "src/client/linux/microdump_writer/microdump_writer.cc",
+      "src/client/linux/microdump_writer/microdump_writer.h",
+      "src/client/linux/minidump_writer/cpu_set.h",
+      "src/client/linux/minidump_writer/directory_reader.h",
+      "src/client/linux/minidump_writer/line_reader.h",
+      "src/client/linux/minidump_writer/linux_core_dumper.cc",
+      "src/client/linux/minidump_writer/linux_core_dumper.h",
+      "src/client/linux/minidump_writer/linux_dumper.cc",
+      "src/client/linux/minidump_writer/linux_dumper.h",
+      "src/client/linux/minidump_writer/linux_ptrace_dumper.cc",
+      "src/client/linux/minidump_writer/linux_ptrace_dumper.h",
+      "src/client/linux/minidump_writer/minidump_writer.cc",
+      "src/client/linux/minidump_writer/minidump_writer.h",
+      "src/client/linux/minidump_writer/proc_cpuinfo_reader.h",
+      "src/client/minidump_file_writer-inl.h",
+      "src/client/minidump_file_writer.cc",
+      "src/client/minidump_file_writer.h",
+      "src/common/convert_UTF.c",
+      "src/common/convert_UTF.h",
+      "src/common/linux/elf_core_dump.cc",
+      "src/common/linux/elf_core_dump.h",
+      "src/common/linux/elfutils.cc",
+      "src/common/linux/elfutils.h",
+      "src/common/linux/file_id.cc",
+      "src/common/linux/file_id.h",
+      "src/common/linux/google_crashdump_uploader.cc",
+      "src/common/linux/google_crashdump_uploader.h",
+      "src/common/linux/guid_creator.cc",
+      "src/common/linux/guid_creator.h",
+      "src/common/linux/libcurl_wrapper.cc",
+      "src/common/linux/libcurl_wrapper.h",
+      "src/common/linux/linux_libc_support.cc",
+      "src/common/linux/linux_libc_support.h",
+      "src/common/linux/memory_mapped_file.cc",
+      "src/common/linux/memory_mapped_file.h",
+      "src/common/linux/safe_readlink.cc",
+      "src/common/linux/safe_readlink.h",
+      "src/common/memory.h",
+      "src/common/simple_string_dictionary.cc",
+      "src/common/simple_string_dictionary.h",
+      "src/common/string_conversion.cc",
+      "src/common/string_conversion.h",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    public_configs = [ ":client_config" ]
+
+    if (current_cpu == "arm" && is_chromeos) {
+      # Avoid running out of registers in
+      # linux_syscall_support.h:sys_clone()'s inline assembly.
+      cflags = [ "-marm" ]
+    }
+
+    if (is_android) {
+      sources += [ "src/common/android/breakpad_getcontext.S" ]
+    }
+
+    libs = [ "dl" ]
+
+    include_dirs = [
+      ".",
+      "src",
+      "src/client",
+      "src/third_party/linux/include",
+    ]
+  }
+
+  static_library("processor_support") {
+    sources = [
+      "src/common/scoped_ptr.h",
+      "src/processor/basic_code_modules.cc",
+      "src/processor/basic_code_modules.h",
+      "src/processor/dump_context.cc",
+      "src/processor/dump_object.cc",
+      "src/processor/logging.cc",
+      "src/processor/logging.h",
+      "src/processor/minidump.cc",
+      "src/processor/pathname_stripper.cc",
+      "src/processor/pathname_stripper.h",
+    ]
+
+    include_dirs = [
+      "src",
+      "src/client",
+      "src/third_party/linux/include",
+      ".",
+    ]
+
+    # There are some warnings in this code.
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+  }
+}
+
+if (is_linux) {
+  test("breakpad_unittests") {
+    sources = [
+      "linux/breakpad_googletest_includes.h",
+      "src/client/linux/handler/exception_handler_unittest.cc",
+      "src/client/linux/minidump_writer/cpu_set_unittest.cc",
+      "src/client/linux/minidump_writer/directory_reader_unittest.cc",
+      "src/client/linux/minidump_writer/line_reader_unittest.cc",
+      "src/client/linux/minidump_writer/linux_core_dumper_unittest.cc",
+      "src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc",
+      "src/client/linux/minidump_writer/minidump_writer_unittest.cc",
+      "src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc",
+      "src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc",
+      "src/common/linux/elf_core_dump_unittest.cc",
+      "src/common/linux/file_id_unittest.cc",
+      "src/common/linux/linux_libc_support_unittest.cc",
+      "src/common/linux/synth_elf.cc",
+      "src/common/linux/tests/auto_testfile.h",
+      "src/common/linux/tests/crash_generator.cc",
+      "src/common/linux/tests/crash_generator.h",
+      "src/common/memory_range.h",
+      "src/common/memory_unittest.cc",
+      "src/common/simple_string_dictionary_unittest.cc",
+      "src/common/test_assembler.cc",
+      "src/common/tests/file_utils.cc",
+      "src/common/tests/file_utils.h",
+      "src/tools/linux/md2core/minidump_memory_range.h",
+      "src/tools/linux/md2core/minidump_memory_range_unittest.cc",
+    ]
+
+    deps = [
+      ":client",
+      ":processor_support",
+      ":linux_dumper_unittest_helper",
+      "//testing/gtest",
+      "//testing/gtest:gtest_main",
+      "//testing/gmock",
+    ]
+
+    include_dirs = [
+      "linux",  # Use our copy of breakpad_googletest_includes.h
+      "src",
+      ".",
+    ]
+
+    # There are some warnings in this code.
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+
+    if (is_clang) {
+      # See http://crbug.com/138571#c18
+      cflags = [ "-Wno-unused-value" ]
+    }
+
+    if (is_android) {
+      sources += [ "src/common/android/breakpad_getcontext_unittest.cc" ]
+      libs = [ "log" ]
+      include_dirs += [ "src/common/android/include" ]
+      isolate_file = "breakpad_unittests.isolate"
+    }
+  }
+
+  executable("linux_dumper_unittest_helper") {
+    testonly = true
+    sources = [
+      "src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc",
+    ]
+    deps = [
+      ":processor_support",
+    ]
+
+    include_dirs = [ "src" ]
+
+    if (current_cpu == "mipsel" && is_android) {
+      include_dirs += [ "src/common/android/include" ]
+    }
+  }
+
+  executable("generate_test_dump") {
+    testonly = true
+    sources = [
+      "linux/generate-test-dump.cc",
+    ]
+
+    # This file has an unused variable warning.
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+
+    deps = [
+      ":client",
+    ]
+
+    include_dirs = [ "src" ]
+
+    if (is_android) {
+      libs = [ "log" ]
+      include_dirs += [ "src/common/android/include" ]
+    }
+  }
+
+  executable("minidump-2-core") {
+    sources = [
+      "src/tools/linux/md2core/minidump-2-core.cc",
+    ]
+
+    include_dirs = [ "src" ]
+
+    deps = [
+      ":client",
+    ]
+  }
+
+  executable("core-2-minidump") {
+    sources = [
+      "src/tools/linux/core2md/core2md.cc",
+    ]
+
+    deps = [
+      ":client",
+    ]
+
+    include_dirs = [ "src" ]
+  }
+}
+
+if (is_ios) {
+  static_library("client") {
+    sources = [
+      "src/client/ios/Breakpad.h",
+      "src/client/ios/Breakpad.mm",
+      "src/client/ios/BreakpadController.h",
+      "src/client/ios/BreakpadController.mm",
+      "src/client/ios/handler/ios_exception_minidump_generator.h",
+      "src/client/ios/handler/ios_exception_minidump_generator.mm",
+      "src/client/mac/crash_generation/ConfigFile.h",
+      "src/client/mac/crash_generation/ConfigFile.mm",
+      "src/client/mac/handler/breakpad_nlist_64.cc",
+      "src/client/mac/handler/breakpad_nlist_64.h",
+      "src/client/mac/handler/dynamic_images.cc",
+      "src/client/mac/handler/dynamic_images.h",
+      "src/client/mac/handler/exception_handler.cc",
+      "src/client/mac/handler/exception_handler.h",
+      "src/client/mac/handler/minidump_generator.cc",
+      "src/client/mac/handler/minidump_generator.h",
+      "src/client/mac/handler/protected_memory_allocator.cc",
+      "src/client/mac/handler/protected_memory_allocator.h",
+      "src/client/mac/sender/uploader.h",
+      "src/client/mac/sender/uploader.mm",
+      "src/client/minidump_file_writer-inl.h",
+      "src/client/minidump_file_writer.cc",
+      "src/client/minidump_file_writer.h",
+      "src/common/convert_UTF.c",
+      "src/common/convert_UTF.h",
+      "src/common/mac/HTTPMultipartUpload.m",
+      "src/common/mac/file_id.cc",
+      "src/common/mac/file_id.h",
+      "src/common/mac/macho_id.cc",
+      "src/common/mac/macho_id.h",
+      "src/common/mac/macho_utilities.cc",
+      "src/common/mac/macho_utilities.h",
+      "src/common/mac/macho_walker.cc",
+      "src/common/mac/macho_walker.h",
+      "src/common/mac/string_utilities.cc",
+      "src/common/mac/string_utilities.h",
+      "src/common/md5.cc",
+      "src/common/md5.h",
+      "src/common/simple_string_dictionary.cc",
+      "src/common/simple_string_dictionary.h",
+      "src/common/string_conversion.cc",
+      "src/common/string_conversion.h",
+      "src/google_breakpad/common/minidump_format.h",
+    ]
+
+    include_dirs = [
+      "src",
+      "src/client/mac/Framework",
+      "src/common/mac",
+    ]
+  }
+  # TODO(GYP) There is some XCode-only targets like ninja-breakpad.
+}
+
+if (is_win) {
+  group("client") {
+    public_configs = [ ":client_config" ]
+  }
+
+  source_set("breakpad_handler") {
+    configs += [ ":handler_config" ]
+    if (is_win) {
+      public_configs = [ ":handler_config" ]
+    }
+
+    defines = [ "BREAKPAD_NO_TERMINATE_THREAD" ]
+
+    sources = [
+      "src/client/windows/crash_generation/client_info.cc",
+      "src/client/windows/crash_generation/client_info.h",
+      "src/client/windows/crash_generation/crash_generation_client.cc",
+      "src/client/windows/crash_generation/crash_generation_client.h",
+      "src/client/windows/crash_generation/crash_generation_server.cc",
+      "src/client/windows/crash_generation/crash_generation_server.h",
+      "src/client/windows/crash_generation/minidump_generator.cc",
+      "src/client/windows/crash_generation/minidump_generator.h",
+      "src/client/windows/handler/exception_handler.cc",
+      "src/client/windows/handler/exception_handler.h",
+      "src/common/windows/guid_string.cc",
+      "src/common/windows/guid_string.h",
+      "src/common/windows/string_utils-inl.h",
+      "src/google_breakpad/common/minidump_format.h",
+    ]
+    config("breakpad_handler_warnings") {
+      if (is_clang) {
+        # See https://code.google.com/p/google-breakpad/issues/detail?id=658.
+        cflags = [ "-Wno-reorder" ]
+      }
+    }
+
+    configs += [ ":breakpad_handler_warnings" ]
+  }
+
+  source_set("breakpad_sender") {
+    sources = [
+      "src/client/windows/sender/crash_report_sender.cc",
+      "src/client/windows/sender/crash_report_sender.h",
+      "src/common/windows/http_upload.cc",
+      "src/common/windows/http_upload.h",
+    ]
+    configs += [ ":sender_config" ]
+    public_configs = [ ":sender_config" ]
+  }
+}