bug 514188 - sync to breakpad revision 437 to pick up Linux client rewrite. r=bsmedberg
authorTed Mielczarek <ted.mielczarek@gmail.com>
Mon, 14 Dec 2009 06:55:28 -0500
changeset 35721 2138d22ff018ee07711742904eb080b676574f4f
parent 35720 4354c4d85277168e35e819f8a956c960672bbf99
child 35722 09e8d73fda8b65342872cb2740918edf26990719
child 36285 5694f93345f71aab0cb90eab7a8ce59bdbf76bb1
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs514188
milestone1.9.3a1pre
bug 514188 - sync to breakpad revision 437 to pick up Linux client rewrite. r=bsmedberg
toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym
toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym
toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h
toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc
toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc
toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc
toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h
toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc
toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc
toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h
toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc
toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc
toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj
toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h
toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm
toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm
toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc
toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h
toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib
toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib
toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib
toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/Localizable.strings
toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h
toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m
toolkit/crashreporter/google-breakpad/src/client/mac/sender/goArrow.png
toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm
toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc
toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln
toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc
toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc
toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc
toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj
toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc
toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc
toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc
toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h
toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc
toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h
toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc
toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc
toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h
toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h
toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc
toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h
toolkit/crashreporter/google-breakpad/src/common/linux/memory.h
toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc
toolkit/crashreporter/google-breakpad/src/common/linux/module.cc
toolkit/crashreporter/google-breakpad/src/common/linux/module.h
toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc
toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h
toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm
toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h
toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm
toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h
toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc
toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc
toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h
toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc
toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc
toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h
toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h
toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h
toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h
toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h
toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h
toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h
toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h
toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc
toolkit/crashreporter/google-breakpad/src/processor/minidump.cc
toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc
toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc
toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc
toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc
toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h
toolkit/crashreporter/google-breakpad/src/processor/process_state.cc
toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc
toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc
toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out
toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h
toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h
toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h
toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h
toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h
toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h
toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h
toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/gflags/libgflags.a
toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/glog/libglog.a
toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc
toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym
@@ -0,0 +1,3 @@
+MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so
+PUBLIC 400 0 __kernel_vsyscall
+STACK WIN 4 400 100 1 1 0 0 0 0 0 1
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym
@@ -0,0 +1,3 @@
+MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so
+PUBLIC 400 0 __kernel_vsyscall
+STACK WIN 4 400 200 3 3 0 0 0 0 0 1
\ No newline at end of file
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
@@ -1,13 +1,11 @@
-// Copyright (c) 2006, Google Inc.
+// Copyright (c) 2009, Google Inc.
 // All rights reserved.
 //
-// Author: Li Liu
-//
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
 //
 //     * Redistributions of source code must retain the above copyright
 // notice, this list of conditions and the following disclaimer.
 //     * Redistributions in binary form must reproduce the above
 // copyright notice, this list of conditions and the following disclaimer
@@ -24,273 +22,177 @@
 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+// The ExceptionHandler object installs signal handlers for a number of
+// signals. We rely on the signal handler running on the thread which crashed
+// in order to identify it. This is true of the synchronous signals (SEGV etc),
+// but not true of ABRT. Thus, if you send ABRT to yourself in a program which
+// uses ExceptionHandler, you need to use tgkill to direct it to the current
+// thread.
+//
+// The signal flow looks like this:
+//
+//   SignalHandler (uses a global stack of ExceptionHandler objects to find
+//        |         one to handle the signal. If the first rejects it, try
+//        |         the second etc...)
+//        V
+//   HandleSignal ----------------------------| (clones a new process which
+//        |                                   |  shares an address space with
+//   (wait for cloned                         |  the crashed process. This
+//     process)                               |  allows us to ptrace the crashed
+//        |                                   |  process)
+//        V                                   V
+//   (set signal handler to             ThreadEntry (static function to bounce
+//    SIG_DFL and rethrow,                    |      back into the object)
+//    killing the crashed                     |
+//    process)                                V
+//                                          DoDump  (writes minidump)
+//                                            |
+//                                            V
+//                                         sys_exit
+//
+
+// This code is a little fragmented. Different functions of the ExceptionHandler
+// class run in a number of different contexts. Some of them run in a normal
+// context and are easy to code, others run in a compromised context and the
+// restrictions at the top of minidump_writer.cc apply: no libc and use the
+// alternative malloc. Each function should have comment above it detailing the
+// context which it runs in.
+
+#include "client/linux/handler/exception_handler.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <sched.h>
 #include <signal.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <string.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/signal.h>
+#include <sys/syscall.h>
+#include <sys/ucontext.h>
+#include <sys/user.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
-#include <cassert>
-#include <cstdlib>
-#include <cstdio>
-#include <ctime>
-#include <linux/limits.h>
+#include "common/linux/linux_libc_support.h"
+#include "common/linux/linux_syscall_support.h"
+#include "common/linux/memory.h"
+#include "client/linux/minidump_writer//minidump_writer.h"
+#include "common/linux/guid_creator.h"
 
-#include "client/linux/handler/exception_handler.h"
-#include "common/linux/guid_creator.h"
-#include "google_breakpad/common/minidump_format.h"
+// A wrapper for the tgkill syscall: send a signal to a specific thread.
+static int tgkill(pid_t tgid, pid_t tid, int sig) {
+  syscall(__NR_tgkill, tgid, tid, sig);
+  return 0;
+}
 
 namespace google_breakpad {
 
-// Signals that we are interested.
-int SigTable[] = {
-#if defined(SIGSEGV)
-  SIGSEGV,
-#endif
-#ifdef SIGABRT
-  SIGABRT,
-#endif
-#ifdef SIGFPE
-  SIGFPE,
-#endif
-#ifdef SIGILL
-  SIGILL,
-#endif
-#ifdef SIGBUS
-  SIGBUS,
-#endif
+// The list of signals which we consider to be crashes. The default action for
+// all these signals must be Core (see man 7 signal) because we rethrow the
+// signal after handling it and expect that it'll be fatal.
+static const int kExceptionSignals[] = {
+  SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1
 };
 
-std::vector<ExceptionHandler*> *ExceptionHandler::handler_stack_ = NULL;
-int ExceptionHandler::handler_stack_index_ = 0;
+// We can stack multiple exception handlers. In that case, this is the global
+// which holds the stack.
+std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
+unsigned ExceptionHandler::handler_stack_index_ = 0;
 pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
-PTHREAD_MUTEX_INITIALIZER;
+    PTHREAD_MUTEX_INITIALIZER;
 
-ExceptionHandler::ExceptionHandler(const string &dump_path,
+// Runs before crashing: normal context.
+ExceptionHandler::ExceptionHandler(const std::string &dump_path,
                                    FilterCallback filter,
                                    MinidumpCallback callback,
                                    void *callback_context,
                                    bool install_handler)
     : filter_(filter),
       callback_(callback),
       callback_context_(callback_context),
       dump_path_(),
-      installed_handler_(install_handler) {
+      handler_installed_(install_handler),
+      crash_handler_(NULL) {
   set_dump_path(dump_path);
 
-  act_.sa_handler = HandleException;
-  act_.sa_flags = SA_ONSTACK;
-  sigemptyset(&act_.sa_mask);
-  // now, make sure we're blocking all the signals we are handling
-  // when we're handling any of them
-  for ( size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) {
-    sigaddset(&act_.sa_mask, SigTable[i]);
-  }
+  if (install_handler) {
+    InstallHandlers();
 
-  if (install_handler) {
-    SetupHandler();
     pthread_mutex_lock(&handler_stack_mutex_);
-    if (handler_stack_ == NULL)
-      handler_stack_ = new std::vector<ExceptionHandler *>;
-    handler_stack_->push_back(this);
+      if (handler_stack_ == NULL)
+        handler_stack_ = new std::vector<ExceptionHandler *>;
+      handler_stack_->push_back(this);
     pthread_mutex_unlock(&handler_stack_mutex_);
   }
 }
 
+// Runs before crashing: normal context.
 ExceptionHandler::~ExceptionHandler() {
-  TeardownAllHandler();
-  pthread_mutex_lock(&handler_stack_mutex_);
-  if (handler_stack_->back() == this) {
-    handler_stack_->pop_back();
-  } else {
-    fprintf(stderr, "warning: removing Breakpad handler out of order\n");
-    for (std::vector<ExceptionHandler *>::iterator iterator =
-         handler_stack_->begin();
-         iterator != handler_stack_->end();
-         ++iterator) {
-      if (*iterator == this) {
-        handler_stack_->erase(iterator);
-      }
-    }
-  }
-
-  if (handler_stack_->empty()) {
-    // When destroying the last ExceptionHandler that installed a handler,
-    // clean up the handler stack.
-    delete handler_stack_;
-    handler_stack_ = NULL;
-  }
-  pthread_mutex_unlock(&handler_stack_mutex_);
-}
-
-bool ExceptionHandler::WriteMinidump() {
-  bool success = InternalWriteMinidump(0, 0, NULL);
-  UpdateNextID();
-  return success;
-}
-
-// static
-bool ExceptionHandler::WriteMinidump(const string &dump_path,
-                   MinidumpCallback callback,
-                   void *callback_context) {
-  ExceptionHandler handler(dump_path, NULL, callback,
-                           callback_context, false);
-  return handler.InternalWriteMinidump(0, 0, NULL);
-}
-
-void ExceptionHandler::SetupHandler() {
-  // Signal on a different stack to avoid using the stack
-  // of the crashing thread.
-  struct sigaltstack sig_stack;
-  sig_stack.ss_sp = malloc(MINSIGSTKSZ);
-  if (sig_stack.ss_sp == NULL)
-    return;
-  sig_stack.ss_size = MINSIGSTKSZ;
-  sig_stack.ss_flags = 0;
-
-  if (sigaltstack(&sig_stack, NULL) < 0)
-    return;
-  for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i)
-    SetupHandler(SigTable[i]);
-}
-
-void ExceptionHandler::SetupHandler(int signo) {
-
-  // We're storing pointers to the old signal action
-  // structure, rather than copying the structure
-  // because we can't count on the sa_mask field to
-  // be scalar.
-  struct sigaction *old_act = &old_actions_[signo];
-
-  if (sigaction(signo, &act_, old_act) < 0)
-   return;
-}
-
-void ExceptionHandler::TeardownHandler(int signo) {
-  TeardownHandler(signo, NULL);
-}
-
-void ExceptionHandler::TeardownHandler(int signo, struct sigaction *final_handler) {
-  if (old_actions_[signo].sa_handler) {
-    struct sigaction *act = &old_actions_[signo];
-    sigaction(signo, act, final_handler);
-    memset(&old_actions_[signo], 0x0, sizeof(struct sigaction));
-  }
-}
-
-void ExceptionHandler::TeardownAllHandler() {
-  for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) {
-    TeardownHandler(SigTable[i]);
-  }
+  UninstallHandlers();
 }
 
-// static
-void ExceptionHandler::HandleException(int signo) {
-  // In Linux, the context information about the signal is put on the stack of
-  // the signal handler frame as value parameter. For some reasons, the
-  // prototype of the handler doesn't declare this information as parameter, we
-  // will do it by hand. It is the second parameter above the signal number.
-  // However, if we are being called by another signal handler passing the
-  // signal up the chain, then we may not have this random extra parameter,
-  // so we may have to walk the stack to find it.  We do the actual work
-  // on another thread, where it's a little safer, but we want the ebp
-  // from this frame to find it.
-  uintptr_t current_ebp = 0;
-  asm volatile ("movl %%ebp, %0"
-                :"=m"(current_ebp));
+// Runs before crashing: normal context.
+bool ExceptionHandler::InstallHandlers() {
+  // We run the signal handlers on an alternative stack because we might have
+  // crashed because of a stack overflow.
+
+  // We use this value rather than SIGSTKSZ because we would end up overrunning
+  // such a small stack.
+  static const unsigned kSigStackSize = 8192;
 
-  pthread_mutex_lock(&handler_stack_mutex_);
-  ExceptionHandler *current_handler =
-    handler_stack_->at(handler_stack_->size() - ++handler_stack_index_);
-  pthread_mutex_unlock(&handler_stack_mutex_);
+  signal_stack = malloc(kSigStackSize);
+  stack_t stack;
+  memset(&stack, 0, sizeof(stack));
+  stack.ss_sp = signal_stack;
+  stack.ss_size = kSigStackSize;
 
-  // Restore original handler.
-  struct sigaction old_action;
-  current_handler->TeardownHandler(signo, &old_action);
+  if (sigaltstack(&stack, NULL) == -1)
+    return false;
+
+  struct sigaction sa;
+  memset(&sa, 0, sizeof(sa));
+  sigemptyset(&sa.sa_mask);
 
-  struct sigcontext *sig_ctx = NULL;
-  if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) {
-    // Fully handled this exception, safe to exit.
-    exit(EXIT_FAILURE);
-  } else {
-    // Exception not fully handled, will call the next handler in stack to
-    // process it.
-    if (old_action.sa_handler != NULL && sig_ctx != NULL) {
+  // mask all exception signals when we're handling one of them.
+  for (unsigned i = 0; kExceptionSignals[i] != -1; ++i)
+    sigaddset(&sa.sa_mask, kExceptionSignals[i]);
+
+  sa.sa_sigaction = SignalHandler;
+  sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
 
-      // Have our own typedef, because of the comment above w.r.t signal
-      // context on the stack
-      typedef void (*SignalHandler)(int signo, struct sigcontext);
-
-      SignalHandler old_handler =
-          reinterpret_cast<SignalHandler>(old_action.sa_handler);
+  for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) {
+    struct sigaction* old = new struct sigaction;
+    if (sigaction(kExceptionSignals[i], &sa, old) == -1)
+      return false;
+    old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old));
+  }
+  return true;
+}
 
-      sigset_t old_set;
-      // Use SIG_BLOCK here because we don't want to unblock a signal
-      // that the signal handler we're currently in needs to block
-      sigprocmask(SIG_BLOCK, &old_action.sa_mask, &old_set);
-      old_handler(signo, *sig_ctx);
-      sigprocmask(SIG_SETMASK, &old_set, NULL);
-    }
-
+// Runs before crashing: normal context.
+void ExceptionHandler::UninstallHandlers() {
+  for (unsigned i = 0; i < old_handlers_.size(); ++i) {
+    struct sigaction *action =
+        reinterpret_cast<struct sigaction*>(old_handlers_[i].second);
+    sigaction(old_handlers_[i].first, action, NULL);
+    delete action;
   }
 
-  pthread_mutex_lock(&handler_stack_mutex_);
-  current_handler->SetupHandler(signo);
-  --handler_stack_index_;
-  // All the handlers in stack have been invoked to handle the exception,
-  // normally the process should be terminated and should not reach here.
-  // In case we got here, ask the OS to handle it to avoid endless loop,
-  // normally the OS will generate a core and termiate the process. This
-  // may be desired to debug the program.
-  if (handler_stack_index_ == 0)
-    signal(signo, SIG_DFL);
-  pthread_mutex_unlock(&handler_stack_mutex_);
+  old_handlers_.clear();
 }
 
-bool ExceptionHandler::InternalWriteMinidump(int signo,
-                                             uintptr_t sighandler_ebp,
-                                             struct sigcontext **sig_ctx) {
-  if (filter_ && !filter_(callback_context_))
-    return false;
-
-  bool success = false;
-  // Block all the signals we want to process when writting minidump.
-  // We don't want it to be interrupted.
-  sigset_t sig_blocked, sig_old;
-  bool blocked = true;
-  sigfillset(&sig_blocked);
-  for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i)
-    sigdelset(&sig_blocked, SigTable[i]);
-  if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old) != 0) {
-    blocked = false;
-    fprintf(stderr, "google_breakpad::ExceptionHandler::HandleException: "
-                    "failed to block signals.\n");
-  }
-
-  success = minidump_generator_.WriteMinidumpToFile(
-                     next_minidump_path_c_, signo, sighandler_ebp, sig_ctx);
-
-  // Unblock the signals.
-  if (blocked) {
-    sigprocmask(SIG_SETMASK, &sig_old, NULL);
-  }
-
-  if (callback_)
-    success = callback_(dump_path_c_, next_minidump_id_c_,
-                          callback_context_, success);
-  return success;
-}
-
+// Runs before crashing: normal context.
 void ExceptionHandler::UpdateNextID() {
   GUID guid;
   char guid_str[kGUIDStringLength + 1];
   if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) {
     next_minidump_id_ = guid_str;
     next_minidump_id_c_ = next_minidump_id_.c_str();
 
     char minidump_path[PATH_MAX];
@@ -298,9 +200,125 @@ void ExceptionHandler::UpdateNextID() {
              dump_path_c_,
              guid_str);
 
     next_minidump_path_ = minidump_path;
     next_minidump_path_c_ = next_minidump_path_.c_str();
   }
 }
 
+// This function runs in a compromised context: see the top of the file.
+// Runs on the crashing thread.
+// static
+void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
+  // All the exception signals are blocked at this point.
+
+  pthread_mutex_lock(&handler_stack_mutex_);
+
+  if (!handler_stack_->size()) {
+    pthread_mutex_unlock(&handler_stack_mutex_);
+    return;
+  }
+
+  for (int i = handler_stack_->size() - 1; i >= 0; --i) {
+    if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) {
+      // successfully handled: We are in an invalid state since an exception
+      // signal has been delivered. We don't call the exit handlers because
+      // they could end up corrupting on-disk state.
+      break;
+    }
+  }
+
+  pthread_mutex_unlock(&handler_stack_mutex_);
+
+  // Terminate ourselves with the same signal so that our parent knows that we
+  // crashed. The default action for all the signals which we catch is Core, so
+  // this is the end of us.
+  signal(sig, SIG_DFL);
+  tgkill(getpid(), sys_gettid(), sig);
+
+  // not reached.
+}
+
+struct ThreadArgument {
+  pid_t pid;  // the crashing process
+  ExceptionHandler* handler;
+  const void* context;  // a CrashContext structure
+  size_t context_size;
+};
+
+// This is the entry function for the cloned process. We are in a compromised
+// context here: see the top of the file.
+// static
+int ExceptionHandler::ThreadEntry(void *arg) {
+  const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
+  return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
+                                     thread_arg->context_size) == false;
+}
+
+// This function runs in a compromised context: see the top of the file.
+// Runs on the crashing thread.
+bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
+  if (filter_ && !filter_(callback_context_))
+    return false;
+
+  // Allow ourselves to be dumped.
+  sys_prctl(PR_SET_DUMPABLE, 1);
+
+  CrashContext context;
+  memcpy(&context.siginfo, info, sizeof(siginfo_t));
+  memcpy(&context.context, uc, sizeof(struct ucontext));
+  memcpy(&context.float_state, ((struct ucontext *)uc)->uc_mcontext.fpregs,
+         sizeof(context.float_state));
+  context.tid = sys_gettid();
+
+  if (crash_handler_ && crash_handler_(&context, sizeof(context),
+                                       callback_context_))
+    return true;
+
+  static const unsigned kChildStackSize = 8000;
+  PageAllocator allocator;
+  uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize);
+  if (!stack)
+    return false;
+  // clone() needs the top-most address. (scrub just to be safe)
+  stack += kChildStackSize;
+  my_memset(stack - 16, 0, 16);
+
+  ThreadArgument thread_arg;
+  thread_arg.handler = this;
+  thread_arg.pid = getpid();
+  thread_arg.context = &context;
+  thread_arg.context_size = sizeof(context);
+
+  const pid_t child = sys_clone(
+      ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
+      &thread_arg, NULL, NULL, NULL);
+  int r, status;
+  do {
+    r = sys_waitpid(child, &status, __WALL);
+  } while (r == -1 && errno == EINTR);
+
+  if (r == -1) {
+    static const char msg[] = "ExceptionHandler::HandleSignal: waitpid failed:";
+    sys_write(2, msg, sizeof(msg) - 1);
+    sys_write(2, strerror(errno), strlen(strerror(errno)));
+    sys_write(2, "\n", 1);
+  }
+
+  bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
+
+  if (callback_)
+    success = callback_(dump_path_c_, next_minidump_id_c_,
+                        callback_context_, success);
+
+  return success;
+}
+
+// This function runs in a compromised context: see the top of the file.
+// Runs on the cloned process.
+bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
+                              size_t context_size) {
+  return google_breakpad::WriteMinidump(
+      next_minidump_path_c_, crashing_process, context, context_size);
+}
+
 }  // namespace google_breakpad
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h
@@ -1,13 +1,11 @@
-// Copyright (c) 2006, Google Inc.
+// Copyright (c) 2009, Google Inc.
 // All rights reserved.
 //
-// Author: Li Liu
-//
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
 //
 //     * Redistributions of source code must retain the above copyright
 // notice, this list of conditions and the following disclaimer.
 //     * Redistributions in binary form must reproduce the above
 // copyright notice, this list of conditions and the following disclaimer
@@ -24,36 +22,26 @@
 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__
-#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__
+#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
+#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
 
-#include <pthread.h>
-
-#include <map>
+#include <vector>
 #include <string>
+
 #include <signal.h>
-#include <vector>
-
-#include "client/linux/handler/minidump_generator.h"
-
-// Context information when exception occured.
-struct sigcontex;
 
 namespace google_breakpad {
 
-using std::string;
-
-//
 // ExceptionHandler
 //
 // ExceptionHandler can write a minidump file when an exception occurs,
 // or when WriteMinidump() is called explicitly by your program.
 //
 // To have the exception handler write minidumps when an uncaught exception
 // (crash) occurs, you should create an instance early in the execution
 // of your program, and keep it around for the entire time you want to
@@ -68,17 +56,16 @@ using std::string;
 //
 // In either case, a callback function is called when a minidump is written,
 // which receives the unqiue id of the minidump.  The caller can use this
 // id to collect and write additional application state, and to launch an
 // external crash-reporting application.
 //
 // Caller should try to make the callbacks as crash-friendly as possible,
 // it should avoid use heap memory allocation as much as possible.
-//
 class ExceptionHandler {
  public:
   // A callback function to run before Breakpad performs any substantial
   // processing of an exception.  A FilterCallback is called before writing
   // a minidump.  context is the parameter supplied by the user as
   // callback_context when the handler was created.
   //
   // If a FilterCallback returns true, Breakpad will continue processing,
@@ -103,124 +90,109 @@ class ExceptionHandler {
   // should normally return the value of |succeeded|, or when they wish to
   // not report an exception of handled, false.  Callbacks will rarely want to
   // return true directly (unless |succeeded| is true).
   typedef bool (*MinidumpCallback)(const char *dump_path,
                                    const char *minidump_id,
                                    void *context,
                                    bool succeeded);
 
+  // In certain cases, a user may wish to handle the generation of the minidump
+  // themselves. In this case, they can install a handler callback which is
+  // called when a crash has occured. If this function returns true, no other
+  // processing of occurs and the process will shortly be crashed. If this
+  // returns false, the normal processing continues.
+  typedef bool (*HandlerCallback)(const void* crash_context,
+                                  size_t crash_context_size,
+                                  void* context);
+
   // Creates a new ExceptionHandler instance to handle writing minidumps.
   // Before writing a minidump, the optional filter callback will be called.
   // Its return value determines whether or not Breakpad should write a
   // minidump.  Minidump files will be written to dump_path, and the optional
   // callback is called after writing the dump file, as described above.
   // If install_handler is true, then a minidump will be written whenever
   // an unhandled exception occurs.  If it is false, minidumps will only
   // be written when WriteMinidump is called.
-  ExceptionHandler(const string &dump_path,
+  ExceptionHandler(const std::string &dump_path,
                    FilterCallback filter, MinidumpCallback callback,
                    void *callback_context,
                    bool install_handler);
   ~ExceptionHandler();
 
   // Get and set the minidump path.
-  string dump_path() const { return dump_path_; }
-  void set_dump_path(const string &dump_path) {
+  std::string dump_path() const { return dump_path_; }
+  void set_dump_path(const std::string &dump_path) {
     dump_path_ = dump_path;
     dump_path_c_ = dump_path_.c_str();
     UpdateNextID();
   }
 
+  void set_crash_handler(HandlerCallback callback) {
+    crash_handler_ = callback;
+  }
+
   // Writes a minidump immediately.  This can be used to capture the
   // execution state independently of a crash.  Returns true on success.
   bool WriteMinidump();
 
   // Convenience form of WriteMinidump which does not require an
   // ExceptionHandler instance.
-  static bool WriteMinidump(const string &dump_path,
+  static bool WriteMinidump(const std::string &dump_path,
                             MinidumpCallback callback,
                             void *callback_context);
 
- private:
-  // Setup crash handler.
-  void SetupHandler();
-  // Setup signal handler for a signal.
-  void SetupHandler(int signo);
-  // Teardown the handler for a signal.
-  void TeardownHandler(int signo);
-  // Teardown the handler for a signal.
-  void TeardownHandler(int signo, struct sigaction *old);
-  // Teardown all handlers.
-  void TeardownAllHandler();
-
-  // Signal handler.
-  static void HandleException(int signo);
-
-  // If called from a signal handler, sighandler_ebp is the ebp of
-  // that signal handler's frame, and sig_ctx is an out parameter
-  // that will be set to point at the sigcontext that was placed
-  // on the stack by the kernel.  You can pass zero and NULL
-  // for the second and third parameters if you are not calling
-  // this from a signal handler.
-  bool InternalWriteMinidump(int signo, uintptr_t sighandler_ebp,
-                             struct sigcontext **sig_ctx);
-
-  // Generates a new ID and stores it in next_minidump_id, and stores the
-  // path of the next minidump to be written in next_minidump_path_.
-  void UpdateNextID();
+  // This structure is passed to minidump_writer.h:WriteMinidump via an opaque
+  // blob. It shouldn't be needed in any user code.
+  struct CrashContext {
+    siginfo_t siginfo;
+    pid_t tid;  // the crashing thread.
+    struct ucontext context;
+    struct _libc_fpstate float_state;
+  };
 
  private:
-  FilterCallback filter_;
-  MinidumpCallback callback_;
-  void *callback_context_;
+  bool InstallHandlers();
+  void UninstallHandlers();
+  void PreresolveSymbols();
 
-  // The directory in which a minidump will be written, set by the dump_path
-  // argument to the constructor, or set_dump_path.
-  string dump_path_;
+  void UpdateNextID();
+  static void SignalHandler(int sig, siginfo_t* info, void* uc);
+  bool HandleSignal(int sig, siginfo_t* info, void* uc);
+  static int ThreadEntry(void* arg);
+  bool DoDump(pid_t crashing_process, const void* context,
+              size_t context_size);
 
-  // The basename of the next minidump to be written, without the extension
-  string next_minidump_id_;
+  const FilterCallback filter_;
+  const MinidumpCallback callback_;
+  void* const callback_context_;
 
-  // The full pathname of the next minidump to be written, including the file
-  // extension
-  string next_minidump_path_;
+  std::string dump_path_;
+  std::string next_minidump_path_;
+  std::string next_minidump_id_;
 
   // Pointers to C-string representations of the above. These are set
   // when the above are set so we can avoid calling c_str during
   // an exception.
-  const char *dump_path_c_;
-  const char *next_minidump_id_c_;
-  const char *next_minidump_path_c_;
+  const char* dump_path_c_;
+  const char* next_minidump_path_c_;
+  const char* next_minidump_id_c_;
 
-  // True if the ExceptionHandler installed an unhandled exception filter
-  // when created (with an install_handler parameter set to true).
-  bool installed_handler_;
+  const bool handler_installed_;
+  void* signal_stack;  // the handler stack.
+  HandlerCallback crash_handler_;
 
   // The global exception handler stack. This is need becuase there may exist
   // multiple ExceptionHandler instances in a process. Each will have itself
   // registered in this stack.
-  static std::vector<ExceptionHandler *> *handler_stack_;
+  static std::vector<ExceptionHandler*> *handler_stack_;
   // The index of the handler that should handle the next exception.
-  static int handler_stack_index_;
+  static unsigned handler_stack_index_;
   static pthread_mutex_t handler_stack_mutex_;
 
-  // The minidump generator.
-  MinidumpGenerator minidump_generator_;
-
-  // disallow copy ctor and operator=
-  explicit ExceptionHandler(const ExceptionHandler &);
-  void operator=(const ExceptionHandler &);
-
-  // The sigactions structure we use for each signal
-  struct sigaction act_;
-
-
-  // Keep the previous handlers for the signal.
-  // We're wasting a bit of memory here since we only change
-  // the handler for some signals but i want to avoid allocating
-  // memory in the signal handler
-  struct sigaction old_actions_[NSIG];
+  // A vector of the old signal handlers. The void* is a pointer to a newly
+  // allocated sigaction structure to avoid pulling in too many includes.
+  std::vector<std::pair<int, void *> > old_handlers_;
 };
 
 }  // namespace google_breakpad
 
-#endif  // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__
+#endif  // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
deleted file mode 100644
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (c) 2006, Google Inc.
-// All rights reserved.
-//
-// Author: Li Liu
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <pthread.h>
-#include <unistd.h>
-
-#include <cassert>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include "client/linux/handler/exception_handler.h"
-#include "client/linux/handler/linux_thread.h"
-
-using namespace google_breakpad;
-
-// Thread use this to see if it should stop working.
-static bool should_exit = false;
-
-static int foo2(int arg) {
-  // Stack variable, used for debugging stack dumps.
-  /*DDDebug*/printf("%s:%d\n", __FUNCTION__, __LINE__);
-  int c = 0xcccccccc;
-  fprintf(stderr, "Thread trying to crash: %x\n", getpid());
-  c = *reinterpret_cast<int *>(0x5);
-  return c;
-}
-
-static int foo(int arg) {
-  // Stack variable, used for debugging stack dumps.
-  int b = 0xbbbbbbbb;
-  b = foo2(b);
-  return b;
-}
-
-static void *thread_crash(void *) {
-  // Stack variable, used for debugging stack dumps.
-  int a = 0xaaaaaaaa;
-  sleep(1);
-  a = foo(a);
-  printf("%x\n", a);
-  return NULL;
-}
-
-static void *thread_main(void *) {
-  while (!should_exit)
-    sleep(1);
-  return NULL;
-}
-
-static void CreateCrashThread() {
-  pthread_t h;
-  pthread_create(&h, NULL, thread_crash, NULL);
-  pthread_detach(h);
-}
-
-// Create working threads.
-static void CreateThread(int num) {
-  pthread_t h;
-  for (int i = 0; i < num; ++i) {
-    pthread_create(&h, NULL, thread_main, NULL);
-    pthread_detach(h);
-  }
-}
-
-// Callback when minidump written.
-static bool MinidumpCallback(const char *dump_path,
-                             const char *minidump_id,
-                             void *context,
-                             bool succeeded) {
-  int index = reinterpret_cast<int>(context);
-  printf("%d %s: %s is dumped\n", index, __FUNCTION__, minidump_id);
-  if (index == 0) {
-    should_exit = true;
-    return true;
-  }
-  // Don't process it.
-  return false;
-}
-
-int main(int argc, char *argv[]) {
-  int handler_index = 0;
-  ExceptionHandler handler_ignore(".", NULL, MinidumpCallback,
-                           (void*)handler_index, true);
-  ++handler_index;
-  ExceptionHandler handler_process(".", NULL, MinidumpCallback,
-                           (void*)handler_index, true);
-  CreateCrashThread();
-  CreateThread(10);
-
-  while (true)
-    sleep(1);
-  should_exit = true;
-
-  return 0;
-}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc
@@ -0,0 +1,256 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+
+#include <stdint.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include "client/linux/handler//exception_handler.h"
+#include "client/linux/minidump_writer/minidump_writer.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/linux/linux_syscall_support.h"
+#include "breakpad_googletest_includes.h"
+
+// This provides a wrapper around system calls which may be
+// interrupted by a signal and return EINTR. See man 7 signal.
+#define HANDLE_EINTR(x) ({ \
+  typeof(x) __eintr_result__; \
+  do { \
+    __eintr_result__ = x; \
+  } while (__eintr_result__ == -1 && errno == EINTR); \
+  __eintr_result__;\
+})
+
+using namespace google_breakpad;
+
+static void sigchld_handler(int signo) { }
+
+class ExceptionHandlerTest : public ::testing::Test {
+ protected:
+  void SetUp() {
+    // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN.
+    struct sigaction sa;
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = sigchld_handler;
+    ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1);
+  }
+
+  void TearDown() {
+    sigaction(SIGCHLD, &old_action, NULL);
+  }
+
+  struct sigaction old_action;
+};
+
+TEST(ExceptionHandlerTest, Simple) {
+  ExceptionHandler handler("/tmp", NULL, NULL, NULL, true);
+}
+
+static bool DoneCallback(const char* dump_path,
+                         const char* minidump_id,
+                         void* context,
+                         bool succeeded) {
+  if (!succeeded)
+    return succeeded;
+
+  int fd = (intptr_t) context;
+  uint32_t len = my_strlen(minidump_id);
+  HANDLE_EINTR(sys_write(fd, &len, sizeof(len)));
+  HANDLE_EINTR(sys_write(fd, minidump_id, len));
+  sys_close(fd);
+
+  return true;
+}
+
+TEST(ExceptionHandlerTest, ChildCrash) {
+  int fds[2];
+  ASSERT_NE(pipe(fds), -1);
+
+  const pid_t child = fork();
+  if (child == 0) {
+    close(fds[0]);
+    ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
+                             true);
+    *reinterpret_cast<int*>(NULL) = 0;
+  }
+  close(fds[1]);
+
+  int status;
+  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
+  ASSERT_TRUE(WIFSIGNALED(status));
+  ASSERT_EQ(WTERMSIG(status), SIGSEGV);
+
+  struct pollfd pfd;
+  memset(&pfd, 0, sizeof(pfd));
+  pfd.fd = fds[0];
+  pfd.events = POLLIN | POLLERR;
+
+  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
+  ASSERT_EQ(r, 1);
+  ASSERT_TRUE(pfd.revents & POLLIN);
+
+  uint32_t len;
+  ASSERT_EQ(read(fds[0], &len, sizeof(len)), sizeof(len));
+  ASSERT_LT(len, 2048);
+  char* filename = reinterpret_cast<char*>(malloc(len + 1));
+  ASSERT_EQ(read(fds[0], filename, len), len);
+  filename[len] = 0;
+  close(fds[0]);
+
+  const std::string minidump_filename = std::string("/tmp/") + filename +
+                                        ".dmp";
+
+  struct stat st;
+  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
+  ASSERT_GT(st.st_size, 0u);
+  unlink(minidump_filename.c_str());
+}
+
+static const unsigned kControlMsgSize =
+    CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
+
+static bool
+CrashHandler(const void* crash_context, size_t crash_context_size,
+             void* context) {
+  const int fd = (intptr_t) context;
+  int fds[2];
+  pipe(fds);
+
+  struct kernel_msghdr msg = {0};
+  struct kernel_iovec iov;
+  iov.iov_base = const_cast<void*>(crash_context);
+  iov.iov_len = crash_context_size;
+
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  char cmsg[kControlMsgSize];
+  memset(cmsg, 0, kControlMsgSize);
+  msg.msg_control = cmsg;
+  msg.msg_controllen = sizeof(cmsg);
+
+  struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
+  hdr->cmsg_level = SOL_SOCKET;
+  hdr->cmsg_type = SCM_RIGHTS;
+  hdr->cmsg_len = CMSG_LEN(sizeof(int));
+  *((int*) CMSG_DATA(hdr)) = fds[1];
+  hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr);
+  hdr->cmsg_level = SOL_SOCKET;
+  hdr->cmsg_type = SCM_CREDENTIALS;
+  hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+  struct ucred *cred = reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
+  cred->uid = getuid();
+  cred->gid = getgid();
+  cred->pid = getpid();
+
+  HANDLE_EINTR(sys_sendmsg(fd, &msg, 0));
+  sys_close(fds[1]);
+
+  char b;
+  HANDLE_EINTR(sys_read(fds[0], &b, 1));
+
+  return true;
+}
+
+TEST(ExceptionHandlerTest, ExternalDumper) {
+  int fds[2];
+  ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
+  static const int on = 1;
+  setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+  setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+
+  const pid_t child = fork();
+  if (child == 0) {
+    close(fds[0]);
+    ExceptionHandler handler("/tmp", NULL, NULL, (void*) fds[1], true);
+    handler.set_crash_handler(CrashHandler);
+    *reinterpret_cast<int*>(NULL) = 0;
+  }
+
+  close(fds[1]);
+  struct msghdr msg = {0};
+  struct iovec iov;
+  static const unsigned kCrashContextSize =
+      sizeof(ExceptionHandler::CrashContext);
+  char context[kCrashContextSize];
+  char control[kControlMsgSize];
+  iov.iov_base = context;
+  iov.iov_len = kCrashContextSize;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = control;
+  msg.msg_controllen = kControlMsgSize;
+
+  const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0));
+  ASSERT_EQ(n, kCrashContextSize);
+  ASSERT_EQ(msg.msg_controllen, kControlMsgSize);
+  ASSERT_EQ(msg.msg_flags, 0);
+
+  pid_t crashing_pid = -1;
+  int signal_fd = -1;
+  for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
+       hdr = CMSG_NXTHDR(&msg, hdr)) {
+    if (hdr->cmsg_level != SOL_SOCKET)
+      continue;
+    if (hdr->cmsg_type == SCM_RIGHTS) {
+      const unsigned len = hdr->cmsg_len -
+          (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
+      ASSERT_EQ(len, sizeof(int));
+      signal_fd = *((int *) CMSG_DATA(hdr));
+    } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
+      const struct ucred *cred =
+          reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
+      crashing_pid = cred->pid;
+    }
+  }
+
+  ASSERT_NE(crashing_pid, -1);
+  ASSERT_NE(signal_fd, -1);
+
+  char templ[] = "/tmp/exception-handler-unittest-XXXXXX";
+  mktemp(templ);
+  ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context,
+                            kCrashContextSize));
+  static const char b = 0;
+  HANDLE_EINTR(write(signal_fd, &b, 1));
+
+  int status;
+  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
+  ASSERT_TRUE(WIFSIGNALED(status));
+  ASSERT_EQ(WTERMSIG(status), SIGSEGV);
+
+  struct stat st;
+  ASSERT_EQ(stat(templ, &st), 0);
+  ASSERT_GT(st.st_size, 0u);
+  unlink(templ);
+}
deleted file mode 100644
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc
+++ /dev/null
@@ -1,411 +0,0 @@
-// Copyright (c) 2006, Google Inc.
-// All rights reserved.
-//
-// Author: Li Liu
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/ptrace.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <string.h>
-
-#include <algorithm>
-#include <cassert>
-#include <cstdio>
-#include <cstdlib>
-#include <functional>
-
-#include "client/linux/handler/linux_thread.h"
-
-using namespace google_breakpad;
-
-// This unamed namespace contains helper function.
-namespace {
-
-// Context information for the callbacks when validating address by listing
-// modules.
-struct AddressValidatingContext {
-  uintptr_t address;
-  bool is_mapped;
-
-  AddressValidatingContext() : address(0UL), is_mapped(false) {
-  }
-};
-
-// Convert from string to int.
-bool LocalAtoi(char *s, int *r) {
-  assert(s != NULL);
-  assert(r != NULL);
-  char *endptr = NULL;
-  int ret = strtol(s, &endptr, 10);
-  if (endptr == s)
-    return false;
-  *r = ret;
-  return true;
-}
-
-// Fill the proc path of a thread given its id.
-void FillProcPath(int pid, char *path, int path_size) {
-  char pid_str[32];
-  snprintf(pid_str, sizeof(pid_str), "%d", pid);
-  snprintf(path, path_size, "/proc/%s/", pid_str);
-}
-
-// Read thread info from /proc/$pid/status.
-bool ReadThreadInfo(int pid, ThreadInfo *info) {
-  assert(info != NULL);
-  char status_path[80];
-  // Max size we want to read from status file.
-  static const int kStatusMaxSize = 1024;
-  char status_content[kStatusMaxSize];
-
-  FillProcPath(pid, status_path, sizeof(status_path));
-  strcat(status_path, "status");
-  int fd = open(status_path, O_RDONLY, 0);
-  if (fd < 0)
-    return false;
-
-  int num_read = read(fd, status_content, kStatusMaxSize - 1);
-  if (num_read < 0) {
-    close(fd);
-    return false;
-  }
-  close(fd);
-  status_content[num_read] = '\0';
-
-  char *tgid_start = strstr(status_content, "Tgid:");
-  if (tgid_start)
-    sscanf(tgid_start, "Tgid:\t%d\n", &(info->tgid));
-  else
-    // tgid not supported by kernel??
-    info->tgid = 0;
-
-  tgid_start = strstr(status_content, "Pid:");
-  if (tgid_start) {
-    sscanf(tgid_start, "Pid:\t%d\n" "PPid:\t%d\n", &(info->pid),
-           &(info->ppid));
-    return true;
-  }
-  return false;
-}
-
-// Callback invoked for each mapped module.
-// It use the module's adderss range to validate the address.
-bool IsAddressInModuleCallback(const ModuleInfo &module_info,
-                               void *context) {
-  AddressValidatingContext *addr =
-    reinterpret_cast<AddressValidatingContext *>(context);
-  addr->is_mapped = ((addr->address >= module_info.start_addr) &&
-                     (addr->address <= module_info.start_addr +
-                      module_info.size));
-  return !addr->is_mapped;
-}
-
-#if defined(__i386__) && !defined(NO_FRAME_POINTER)
-void *GetNextFrame(void **last_ebp) {
-  void *sp = *last_ebp;
-  if ((unsigned long)sp == (unsigned long)last_ebp)
-    return NULL;
-  if ((unsigned long)sp & (sizeof(void *) - 1))
-    return NULL;
-  if ((unsigned long)sp - (unsigned long)last_ebp > 100000)
-    return NULL;
-  return sp;
-}
-#else
-void *GetNextFrame(void **last_ebp) {
-  return reinterpret_cast<void*>(last_ebp);
-}
-#endif
-
-// Suspend a thread by attaching to it.
-bool SuspendThread(int pid, void *context) {
-  // This may fail if the thread has just died or debugged.
-  errno = 0;
-  if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
-      errno != 0) {
-    return false;
-  }
-  while (waitpid(pid, NULL, __WALL) < 0) {
-    if (errno != EINTR) {
-      ptrace(PTRACE_DETACH, pid, NULL, NULL);
-      return false;
-    }
-  }
-  return true;
-}
-
-// Resume a thread by detaching from it.
-bool ResumeThread(int pid, void *context) {
-  return ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
-}
-
-// Callback to get the thread information.
-// Will be called for each thread found.
-bool ThreadInfoCallback(int pid, void *context) {
-  CallbackParam<ThreadCallback> *thread_callback =
-    reinterpret_cast<CallbackParam<ThreadCallback> *>(context);
-  ThreadInfo thread_info;
-  if (ReadThreadInfo(pid, &thread_info) && thread_callback) {
-    // Invoke callback from caller.
-    return (thread_callback->call_back)(thread_info, thread_callback->context);
-  }
-  return false;
-}
-
-}  // namespace
-
-namespace google_breakpad {
-
-LinuxThread::LinuxThread(int pid) : pid_(pid) , threads_suspened_(false) {
-}
-
-LinuxThread::~LinuxThread() {
-  if (threads_suspened_)
-    ResumeAllThreads();
-}
-
-int LinuxThread::SuspendAllThreads() {
-  CallbackParam<PidCallback> callback_param(SuspendThread, NULL);
-  int thread_count = 0;
-  if ((thread_count = IterateProcSelfTask(pid_, &callback_param)) > 0)
-    threads_suspened_ = true;
-  return thread_count;
-}
-
-void LinuxThread::ResumeAllThreads() const {
-  CallbackParam<PidCallback> callback_param(ResumeThread, NULL);
-  IterateProcSelfTask(pid_, &callback_param);
-}
-
-int LinuxThread::GetThreadCount() const {
-  return IterateProcSelfTask(pid_, NULL);
-}
-
-int LinuxThread::ListThreads(
-    CallbackParam<ThreadCallback> *thread_callback_param) const {
-  CallbackParam<PidCallback> callback_param(ThreadInfoCallback,
-                                            thread_callback_param);
-  return IterateProcSelfTask(pid_, &callback_param);
-}
-
-bool LinuxThread::GetRegisters(int pid, user_regs_struct *regs) const {
-  assert(regs);
-  return (regs != NULL &&
-          (ptrace(PTRACE_GETREGS, pid, NULL, regs) == 0) &&
-          errno == 0);
-}
-
-// Get the floating-point registers of a thread.
-// The caller must get the thread pid by ListThreads.
-bool LinuxThread::GetFPRegisters(int pid, user_fpregs_struct *regs) const {
-  assert(regs);
-  return (regs != NULL &&
-          (ptrace(PTRACE_GETREGS, pid, NULL, regs) ==0) &&
-          errno == 0);
-}
-
-bool LinuxThread::GetFPXRegisters(int pid, user_fpxregs_struct *regs) const {
-  assert(regs);
-  return (regs != NULL &&
-          (ptrace(PTRACE_GETFPREGS, pid, NULL, regs) != 0) &&
-          errno == 0);
-}
-
-bool LinuxThread::GetDebugRegisters(int pid, DebugRegs *regs) const {
-  assert(regs);
-
-#define GET_DR(name, num)\
-  name->dr##num = ptrace(PTRACE_PEEKUSER, pid,\
-                         offsetof(struct user, u_debugreg[num]), NULL)
-  GET_DR(regs, 0);
-  GET_DR(regs, 1);
-  GET_DR(regs, 2);
-  GET_DR(regs, 3);
-  GET_DR(regs, 4);
-  GET_DR(regs, 5);
-  GET_DR(regs, 6);
-  GET_DR(regs, 7);
-  return true;
-}
-
-int LinuxThread::GetThreadStackDump(uintptr_t current_ebp,
-                                    uintptr_t current_esp,
-                                    void *buf,
-                                    int buf_size) const {
-  assert(buf);
-  assert(buf_size > 0);
-
-  uintptr_t stack_bottom = GetThreadStackBottom(current_ebp);
-  int size = stack_bottom - current_esp;
-  size = buf_size > size ? size : buf_size;
-  if (size > 0)
-    memcpy(buf, reinterpret_cast<void*>(current_esp), size);
-  return size;
-}
-
-// Get the stack bottom of a thread by stack walking. It works
-// unless the stack has been corrupted or the frame pointer has been omited.
-// This is just a temporary solution before we get better ideas about how
-// this can be done.
-//
-// We will check each frame address by checking into module maps.
-// TODO(liuli): Improve it.
-uintptr_t LinuxThread::GetThreadStackBottom(uintptr_t current_ebp) const {
-  void **sp = reinterpret_cast<void **>(current_ebp);
-  void **previous_sp = sp;
-  while (sp && IsAddressMapped((uintptr_t)sp)) {
-    previous_sp = sp;
-    sp = reinterpret_cast<void **>(GetNextFrame(sp));
-  }
-  return (uintptr_t)previous_sp;
-}
-
-int LinuxThread::GetModuleCount() const {
-  return ListModules(NULL);
-}
-
-int LinuxThread::ListModules(
-    CallbackParam<ModuleCallback> *callback_param) const {
-  char line[512];
-  const char *maps_path = "/proc/self/maps";
-
-  int module_count = 0;
-  FILE *fp = fopen(maps_path, "r");
-  if (fp == NULL)
-    return -1;
-
-  uintptr_t start_addr;
-  uintptr_t end_addr;
-  while (fgets(line, sizeof(line), fp) != NULL) {
-    if (sscanf(line, "%x-%x", &start_addr, &end_addr) == 2) {
-      ModuleInfo module;
-      memset(&module, 0, sizeof(module));
-      module.start_addr = start_addr;
-      module.size = end_addr - start_addr;
-      char *name = NULL;
-      assert(module.size > 0);
-      // Only copy name if the name is a valid path name.
-      if ((name = strchr(line, '/')) != NULL) {
-        // Get rid of the last '\n' in line
-        char *last_return = strchr(line, '\n');
-        if (last_return != NULL)
-          *last_return = '\0';
-        // Keep a space for the ending 0.
-        strncpy(module.name, name, sizeof(module.name) - 1);
-        ++module_count;
-      }
-      if (callback_param &&
-          !(callback_param->call_back(module, callback_param->context)))
-        break;
-    }
-  }
-  fclose(fp);
-  return module_count;
-}
-
-// Parse /proc/$pid/tasks to list all the threads of the process identified by
-// pid.
-int LinuxThread::IterateProcSelfTask(int pid,
-                          CallbackParam<PidCallback> *callback_param) const {
-  char task_path[80];
-  FillProcPath(pid, task_path, sizeof(task_path));
-  strcat(task_path, "task");
-
-  DIR *dir = opendir(task_path);
-  if (dir == NULL)
-    return -1;
-
-  int pid_number = 0;
-  // Record the last pid we've found. This is used for duplicated thread
-  // removal. Duplicated thread information can be found in /proc/$pid/tasks.
-  int last_pid = -1;
-  struct dirent *entry = NULL;
-  while ((entry = readdir(dir)) != NULL) {
-    if (strcmp(entry->d_name, ".") &&
-        strcmp(entry->d_name, "..")) {
-      int tpid = 0;
-      if (LocalAtoi(entry->d_name, &tpid) &&
-          last_pid != tpid) {
-        last_pid = tpid;
-        ++pid_number;
-        // Invoke the callback.
-        if (callback_param &&
-            !(callback_param->call_back)(tpid, callback_param->context))
-          break;
-      }
-    }
-  }
-  closedir(dir);
-  return pid_number;
-}
-
-// Check if the address is a valid virtual address.
-// If the address is in any of the mapped modules, we take it as valid.
-// Otherwise it is invalid.
-bool LinuxThread::IsAddressMapped(uintptr_t address) const {
-  AddressValidatingContext addr;
-  addr.address = address;
-  CallbackParam<ModuleCallback> callback_param(IsAddressInModuleCallback,
-                                               &addr);
-  ListModules(&callback_param);
-  return addr.is_mapped;
-}
-
-bool LinuxThread::FindSigContext(uintptr_t sighandler_ebp,
-                                 struct sigcontext **sig_ctx) {
-  uintptr_t previous_ebp;
-  const int MAX_STACK_DEPTH = 10;
-  int depth_counter = 0;
-
-  do {
-    // We're looking for a |struct sigcontext| as the second parameter
-    // to a signal handler function call.  Luckily, the sigcontext
-    // has an ebp member which should match the ebp pointed to
-    // by the ebp of the signal handler frame.
-    previous_ebp = reinterpret_cast<uintptr_t>(GetNextFrame(
-                                  reinterpret_cast<void**>(sighandler_ebp)));
-    // The stack looks like this:
-    // | previous ebp | previous eip | first param | second param |,
-    // so we need to offset by 3 to get to the second parameter.
-    *sig_ctx = reinterpret_cast<struct sigcontext*>(sighandler_ebp +
-                                                    3 * sizeof(uintptr_t));
-    sighandler_ebp = previous_ebp;
-    depth_counter++;
-  } while(previous_ebp != (*sig_ctx)->ebp && sighandler_ebp != 0 &&
-          IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH);
-
-  return previous_ebp == (*sig_ctx)->ebp && previous_ebp != 0;
-}
-
-}  // namespace google_breakpad
deleted file mode 100644
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright (c) 2006, Google Inc.
-// All rights reserved.
-//
-// Author: Li Liu
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-#ifndef CLIENT_LINUX_HANDLER_LINUX_THREAD_H__
-#define CLIENT_LINUX_HANDLER_LINUX_THREAD_H__
-
-#include <stdint.h>
-#include <sys/user.h>
-
-namespace google_breakpad {
-
-// Max module path name length.
-#define kMaxModuleNameLength 256
-
-// Holding information about a thread in the process.
-struct ThreadInfo {
-  // Id of the thread group.
-  int tgid;
-  // Id of the thread.
-  int pid;
-  // Id of the parent process.
-  int ppid;
-};
-
-// Holding infomaton about a module in the process.
-struct ModuleInfo {
-  char name[kMaxModuleNameLength];
-  uintptr_t start_addr;
-  int size;
-};
-
-// Holding debug registers.
-struct DebugRegs {
-  int dr0;
-  int dr1;
-  int dr2;
-  int dr3;
-  int dr4;
-  int dr5;
-  int dr6;
-  int dr7;
-};
-
-// A callback to run when got a thread in the process.
-// Return true will go on to the next thread while return false will stop the
-// iteration.
-typedef bool (*ThreadCallback)(const ThreadInfo &thread_info, void *context);
-
-// A callback to run when a new module is found in the process.
-// Return true will go on to the next module while return false will stop the
-// iteration.
-typedef bool (*ModuleCallback)(const ModuleInfo &module_info, void *context);
-
-// Holding the callback information.
-template<class CallbackFunc>
-struct CallbackParam {
-  // Callback function address.
-  CallbackFunc call_back;
-  // Callback context;
-  void *context;
-
-  CallbackParam() : call_back(NULL), context(NULL) {
-  }
-
-  CallbackParam(CallbackFunc func, void *func_context) :
-    call_back(func), context(func_context) {
-  }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-//
-// LinuxThread
-//
-// Provides handy support for operation on linux threads.
-// It uses ptrace to get thread registers. Since ptrace only works in a
-// different process other than the one being ptraced, user of this class
-// should create another process before using the class.
-//
-// The process should be created in the following way:
-//    int cloned_pid = clone(ProcessEntryFunction, stack_address,
-//                           CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
-//                           (void*)&arguments);
-//    waitpid(cloned_pid, NULL, __WALL);
-//
-// If CLONE_VM is not used, GetThreadStackBottom, GetThreadStackDump
-// will not work since it just use memcpy to get the stack dump.
-//
-class LinuxThread {
- public:
-  // Create a LinuxThread instance to list all the threads in a process.
-  explicit LinuxThread(int pid);
-  ~LinuxThread();
-
-  // Stop all the threads in the process.
-  // Return the number of stopped threads in the process.
-  // Return -1 means failed to stop threads.
-  int SuspendAllThreads();
-
-  // Resume all the suspended threads.
-  void ResumeAllThreads() const;
-
-  // Get the count of threads in the process.
-  // Return -1 means error.
-  int GetThreadCount() const;
-
-  // List the threads of process.
-  // Whenever there is a thread found, the callback will be invoked to process
-  // the information.
-  // Return number of threads listed.
-  int ListThreads(CallbackParam<ThreadCallback> *thread_callback_param) const;
-
-  // Get the general purpose registers of a thread.
-  // The caller must get the thread pid by ListThreads.
-  bool GetRegisters(int pid, user_regs_struct *regs) const;
-
-  // Get the floating-point registers of a thread.
-  // The caller must get the thread pid by ListThreads.
-  bool GetFPRegisters(int pid, user_fpregs_struct *regs) const;
-
-  // Get all the extended floating-point registers. May not work on all
-  // machines.
-  // The caller must get the thread pid by ListThreads.
-  bool GetFPXRegisters(int pid, user_fpxregs_struct *regs) const;
-
-  // Get the debug registers.
-  // The caller must get the thread pid by ListThreads.
-  bool GetDebugRegisters(int pid, DebugRegs *regs) const;
-
-  // Get the stack memory dump.
-  int GetThreadStackDump(uintptr_t current_ebp,
-                         uintptr_t current_esp,
-                         void *buf,
-                         int buf_size) const;
-
-  // Get the module count of the current process.
-  int GetModuleCount() const;
-
-  // Get the mapped modules in the address space.
-  // Whenever a module is found, the callback will be invoked to process the
-  // information.
-  // Return how may modules are found.
-  int ListModules(CallbackParam<ModuleCallback> *callback_param) const;
-
-  // Get the bottom of the stack from ebp.
-  uintptr_t GetThreadStackBottom(uintptr_t current_ebp) const;
-
-  // Finds a sigcontext on the stack given the ebp of our signal handler.
-  bool FindSigContext(uintptr_t sighandler_ebp, struct sigcontext **sig_ctx);
-
- private:
-  // This callback will run when a new thread has been found.
-  typedef bool (*PidCallback)(int pid, void *context);
-
-  // Read thread information from /proc/$pid/task.
-  // Whenever a thread has been found, and callback will be invoked with
-  // the pid of the thread.
-  // Return number of threads found.
-  // Return -1 means the directory doesn't exist.
-  int IterateProcSelfTask(int pid,
-                          CallbackParam<PidCallback> *callback_param) const;
-
-  // Check if the address is a valid virtual address.
-  bool IsAddressMapped(uintptr_t address) const;
-
- private:
-  // The pid of the process we are listing threads.
-  int pid_;
-
-  // Mark if we have suspended the threads.
-  bool threads_suspened_;
-};
-
-}  // namespace google_breakpad
-
-#endif  // CLIENT_LINUX_HANDLER_LINUX_THREAD_H__
deleted file mode 100644
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright (c) 2006, Google Inc.
-// All rights reserved.
-//
-// Author: Li Liu
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <pthread.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/wait.h>
-
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include "client/linux/handler/linux_thread.h"
-
-using namespace google_breakpad;
-
-// Thread use this to see if it should stop working.
-static bool should_exit = false;
-
-static void foo2(int *a) {
-  // Stack variable, used for debugging stack dumps.
-  int c = 0xcccccccc;
-  c = c;
-  while (!should_exit)
-    sleep(1);
-}
-
-static void foo() {
-  // Stack variable, used for debugging stack dumps.
-  int a = 0xaaaaaaaa;
-  foo2(&a);
-}
-
-static void *thread_main(void *) {
-  // Stack variable, used for debugging stack dumps.
-  int b = 0xbbbbbbbb;
-  b = b;
-  while (!should_exit) {
-    foo();
-  }
-  return NULL;
-}
-
-static void CreateThreads(int num) {
-  pthread_t handle;
-  for (int i = 0; i < num; i++) {
-    if (0 != pthread_create(&handle, NULL, thread_main, NULL))
-      fprintf(stderr, "Failed to create thread.\n");
-    else
-      pthread_detach(handle);
-  }
-}
-
-static bool ProcessOneModule(const struct ModuleInfo &module_info,
-                             void *context) {
-  printf("0x%x[%8d]         %s\n", module_info.start_addr, module_info.size,
-         module_info.name);
-  return true;
-}
-
-static bool ProcessOneThread(const struct ThreadInfo &thread_info,
-                             void *context) {
-  printf("\n\nPID: %d, TGID: %d, PPID: %d\n",
-         thread_info.pid,
-         thread_info.tgid,
-         thread_info.ppid);
-
-  struct user_regs_struct regs;
-  struct user_fpregs_struct fp_regs;
-  struct user_fpxregs_struct fpx_regs;
-  struct DebugRegs dbg_regs;
-
-  LinuxThread *threads = reinterpret_cast<LinuxThread *>(context);
-  memset(&regs, 0, sizeof(regs));
-  if (threads->GetRegisters(thread_info.pid, &regs)) {
-    printf("  gs                           = 0x%lx\n", regs.xgs);
-    printf("  fs                           = 0x%lx\n", regs.xfs);
-    printf("  es                           = 0x%lx\n", regs.xes);
-    printf("  ds                           = 0x%lx\n", regs.xds);
-    printf("  edi                          = 0x%lx\n", regs.edi);
-    printf("  esi                          = 0x%lx\n", regs.esi);
-    printf("  ebx                          = 0x%lx\n", regs.ebx);
-    printf("  edx                          = 0x%lx\n", regs.edx);
-    printf("  ecx                          = 0x%lx\n", regs.ecx);
-    printf("  eax                          = 0x%lx\n", regs.eax);
-    printf("  ebp                          = 0x%lx\n", regs.ebp);
-    printf("  eip                          = 0x%lx\n", regs.eip);
-    printf("  cs                           = 0x%lx\n", regs.xcs);
-    printf("  eflags                       = 0x%lx\n", regs.eflags);
-    printf("  esp                          = 0x%lx\n", regs.esp);
-    printf("  ss                           = 0x%lx\n", regs.xss);
-  } else {
-    fprintf(stderr, "ERROR: Failed to get general purpose registers\n");
-  }
-  memset(&fp_regs, 0, sizeof(fp_regs));
-  if (threads->GetFPRegisters(thread_info.pid, &fp_regs)) {
-    printf("\n Floating point registers:\n");
-    printf("  fctl                         = 0x%lx\n", fp_regs.cwd);
-    printf("  fstat                        = 0x%lx\n", fp_regs.swd);
-    printf("  ftag                         = 0x%lx\n", fp_regs.twd);
-    printf("  fioff                        = 0x%lx\n", fp_regs.fip);
-    printf("  fiseg                        = 0x%lx\n", fp_regs.fcs);
-    printf("  fooff                        = 0x%lx\n", fp_regs.foo);
-    printf("  foseg                        = 0x%lx\n", fp_regs.fos);
-    int st_space_size = sizeof(fp_regs.st_space) / sizeof(fp_regs.st_space[0]);
-    printf("  st_space[%2d]                 = 0x", st_space_size);
-    for (int i = 0; i < st_space_size; ++i)
-      printf("%02lx", fp_regs.st_space[i]);
-    printf("\n");
-  } else {
-    fprintf(stderr, "ERROR: Failed to get floating-point registers\n");
-  }
-  memset(&fpx_regs, 0, sizeof(fpx_regs));
-  if (threads->GetFPXRegisters(thread_info.pid, &fpx_regs)) {
-    printf("\n Extended floating point registers:\n");
-    printf("  fctl                         = 0x%x\n", fpx_regs.cwd);
-    printf("  fstat                        = 0x%x\n", fpx_regs.swd);
-    printf("  ftag                         = 0x%x\n", fpx_regs.twd);
-    printf("  fioff                        = 0x%lx\n", fpx_regs.fip);
-    printf("  fiseg                        = 0x%lx\n", fpx_regs.fcs);
-    printf("  fooff                        = 0x%lx\n", fpx_regs.foo);
-    printf("  foseg                        = 0x%lx\n", fpx_regs.fos);
-    printf("  fop                          = 0x%x\n", fpx_regs.fop);
-    printf("  mxcsr                        = 0x%lx\n", fpx_regs.mxcsr);
-    int space_size = sizeof(fpx_regs.st_space) / sizeof(fpx_regs.st_space[0]);
-    printf("  st_space[%2d]                 = 0x", space_size);
-    for (int i = 0; i < space_size; ++i)
-      printf("%02lx", fpx_regs.st_space[i]);
-    printf("\n");
-    space_size = sizeof(fpx_regs.xmm_space) / sizeof(fpx_regs.xmm_space[0]);
-    printf("  xmm_space[%2d]                = 0x", space_size);
-    for (int i = 0; i < space_size; ++i)
-      printf("%02lx", fpx_regs.xmm_space[i]);
-    printf("\n");
-  }
-  if (threads->GetDebugRegisters(thread_info.pid, &dbg_regs)) {
-    printf("\n Debug registers:\n");
-    printf("  dr0                          = 0x%x\n", dbg_regs.dr0);
-    printf("  dr1                          = 0x%x\n", dbg_regs.dr1);
-    printf("  dr2                          = 0x%x\n", dbg_regs.dr2);
-    printf("  dr3                          = 0x%x\n", dbg_regs.dr3);
-    printf("  dr4                          = 0x%x\n", dbg_regs.dr4);
-    printf("  dr5                          = 0x%x\n", dbg_regs.dr5);
-    printf("  dr6                          = 0x%x\n", dbg_regs.dr6);
-    printf("  dr7                          = 0x%x\n", dbg_regs.dr7);
-    printf("\n");
-  }
-  if (regs.esp != 0) {
-    // Print the stack content.
-    int size = 1024 * 2;
-    char *buf = new char[size];
-    size = threads->GetThreadStackDump(regs.ebp,
-                                       regs.esp,
-                                      (void*)buf, size);
-    printf(" Stack content:                 = 0x");
-    size /= sizeof(unsigned long);
-    unsigned long *p_buf = (unsigned long *)(buf);
-    for (int i = 0; i < size; i += 1)
-      printf("%.8lx ", p_buf[i]);
-    delete []buf;
-    printf("\n");
-  }
-  return true;
-}
-
-static int PrintAllThreads(void *argument) {
-  int pid = (int)argument;
-
-  LinuxThread threads(pid);
-  int total_thread = threads.SuspendAllThreads();
-  printf("There are %d threads in the process: %d\n", total_thread, pid);
-  int total_module = threads.GetModuleCount();
-  printf("There are %d modules in the process: %d\n", total_module, pid);
-  CallbackParam<ModuleCallback> module_callback(ProcessOneModule, &threads);
-  threads.ListModules(&module_callback);
-  CallbackParam<ThreadCallback> thread_callback(ProcessOneThread, &threads);
-  threads.ListThreads(&thread_callback);
-  return 0;
-}
-
-int main(int argc, char **argv) {
-  int pid = getpid();
-  printf("Main thread is %d\n", pid);
-  CreateThreads(1);
-  // Create stack for the process.
-  char *stack = new char[1024 * 100];
-  int cloned_pid = clone(PrintAllThreads, stack + 1024 * 100,
-                           CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
-                           (void*)getpid());
-  waitpid(cloned_pid, NULL, __WALL);
-  should_exit = true;
-  printf("Test finished.\n");
-
-  delete []stack;
-  return 0;
-}
deleted file mode 100644
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc
+++ /dev/null
@@ -1,816 +0,0 @@
-// Copyright (c) 2006, Google Inc.
-// All rights reserved.
-//
-// Author: Li Liu
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <fcntl.h>
-#include <pthread.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/utsname.h>
-#include <sys/wait.h>
-
-#include <cstdlib>
-#include <cstdio>
-#include <ctime>
-#include <string.h>
-
-#include "common/linux/file_id.h"
-#include "client/linux/handler/linux_thread.h"
-#include "client/minidump_file_writer.h"
-#include "client/minidump_file_writer-inl.h"
-#include "google_breakpad/common/minidump_format.h"
-#include "client/linux/handler/minidump_generator.h"
-
-#ifndef CLONE_UNTRACED
-#define CLONE_UNTRACED 0x00800000
-#endif
-
-// This unnamed namespace contains helper functions.
-namespace {
-
-using namespace google_breakpad;
-
-// Argument for the writer function.
-struct WriterArgument {
-  MinidumpFileWriter *minidump_writer;
-
-  // Context for the callback.
-  void *version_context;
-
-  // Pid of the thread who called WriteMinidumpToFile
-  int requester_pid;
-
-  // The stack bottom of the thread which caused the dump.
-  // Mainly used to find the thread id of the crashed thread since signal
-  // handler may not be called in the thread who caused it.
-  uintptr_t crashed_stack_bottom;
-
-  // Pid of the crashing thread.
-  int crashed_pid;
-
-  // Signal number when crash happed. Can be 0 if this is a requested dump.
-  int signo;
-
-  // The ebp of the signal handler frame.  Can be zero if this
-  // is a requested dump.
-  uintptr_t sighandler_ebp;
-
-  // Signal context when crash happed. Can be NULL if this is a requested dump.
-  // This is actually an out parameter, but it will be filled in at the start
-  // of the writer thread.
-  struct sigcontext *sig_ctx;
-
-  // Used to get information about the threads.
-  LinuxThread *thread_lister;
-};
-
-// Holding context information for the callback of finding the crashing thread.
-struct FindCrashThreadContext {
-  const LinuxThread *thread_lister;
-  uintptr_t crashing_stack_bottom;
-  int crashing_thread_pid;
-
-  FindCrashThreadContext() :
-    thread_lister(NULL),
-    crashing_stack_bottom(0UL),
-    crashing_thread_pid(-1) {
-  }
-};
-
-// Callback for list threads.
-// It will compare the stack bottom of the provided thread with the stack
-// bottom of the crashed thread, it they are eqaul, this is thread is the one
-// who crashed.
-bool IsThreadCrashedCallback(const ThreadInfo &thread_info, void *context) {
-  FindCrashThreadContext *crashing_context =
-    static_cast<FindCrashThreadContext *>(context);
-  const LinuxThread *thread_lister = crashing_context->thread_lister;
-  struct user_regs_struct regs;
-  if (thread_lister->GetRegisters(thread_info.pid, &regs)) {
-    uintptr_t last_ebp = regs.ebp;
-    uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp);
-    if (stack_bottom > last_ebp &&
-        stack_bottom == crashing_context->crashing_stack_bottom) {
-      // Got it. Stop iteration.
-      crashing_context->crashing_thread_pid = thread_info.pid;
-      return false;
-    }
-  }
-  return true;
-}
-
-// Find the crashing thread id.
-// This is done based on stack bottom comparing.
-int FindCrashingThread(uintptr_t crashing_stack_bottom,
-                       int requester_pid,
-                       const LinuxThread *thread_lister) {
-  FindCrashThreadContext context;
-  context.thread_lister = thread_lister;
-  context.crashing_stack_bottom = crashing_stack_bottom;
-  CallbackParam<ThreadCallback> callback_param(IsThreadCrashedCallback,
-                                               &context);
-  thread_lister->ListThreads(&callback_param);
-  return context.crashing_thread_pid;
-}
-
-// Write the thread stack info minidump.
-bool WriteThreadStack(uintptr_t last_ebp,
-                      uintptr_t last_esp,
-                      const LinuxThread *thread_lister,
-                      UntypedMDRVA *memory,
-                      MDMemoryDescriptor *loc) {
-  // Maximum stack size for a thread.
-  uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp);
-  if (stack_bottom > last_esp) {
-    int size = stack_bottom - last_esp;
-    if (size > 0) {
-      if (!memory->Allocate(size))
-        return false;
-      memory->Copy(reinterpret_cast<void*>(last_esp), size);
-      loc->start_of_memory_range = 0 | last_esp;
-      loc->memory = memory->location();
-    }
-    return true;
-  }
-  return false;
-}
-
-// Write CPU context based on signal context.
-bool WriteContext(MDRawContextX86 *context, const struct sigcontext *sig_ctx,
-                  const DebugRegs *debug_regs) {
-  assert(sig_ctx != NULL);
-  context->context_flags = MD_CONTEXT_X86_FULL;
-  context->gs = sig_ctx->gs;
-  context->fs = sig_ctx->fs;
-  context->es = sig_ctx->es;
-  context->ds = sig_ctx->ds;
-  context->cs = sig_ctx->cs;
-  context->ss = sig_ctx->ss;
-  context->edi = sig_ctx->edi;
-  context->esi = sig_ctx->esi;
-  context->ebp = sig_ctx->ebp;
-  context->esp = sig_ctx->esp;
-  context->ebx = sig_ctx->ebx;
-  context->edx = sig_ctx->edx;
-  context->ecx = sig_ctx->ecx;
-  context->eax = sig_ctx->eax;
-  context->eip = sig_ctx->eip;
-  context->eflags = sig_ctx->eflags;
-  if (sig_ctx->fpstate != NULL) {
-    context->context_flags = MD_CONTEXT_X86_FULL |
-      MD_CONTEXT_X86_FLOATING_POINT;
-    context->float_save.control_word = sig_ctx->fpstate->cw;
-    context->float_save.status_word = sig_ctx->fpstate->sw;
-    context->float_save.tag_word = sig_ctx->fpstate->tag;
-    context->float_save.error_offset = sig_ctx->fpstate->ipoff;
-    context->float_save.error_selector = sig_ctx->fpstate->cssel;
-    context->float_save.data_offset = sig_ctx->fpstate->dataoff;
-    context->float_save.data_selector = sig_ctx->fpstate->datasel;
-    memcpy(context->float_save.register_area, sig_ctx->fpstate->_st,
-           sizeof(context->float_save.register_area));
-  }
-
-  if (debug_regs != NULL) {
-    context->context_flags |= MD_CONTEXT_X86_DEBUG_REGISTERS;
-    context->dr0 = debug_regs->dr0;
-    context->dr1 = debug_regs->dr1;
-    context->dr2 = debug_regs->dr2;
-    context->dr3 = debug_regs->dr3;
-    context->dr6 = debug_regs->dr6;
-    context->dr7 = debug_regs->dr7;
-  }
-  return true;
-}
-
-// Write CPU context based on provided registers.
-bool WriteContext(MDRawContextX86 *context,
-                  const struct user_regs_struct *regs,
-                  const struct user_fpregs_struct *fp_regs,
-                  const DebugRegs *dbg_regs) {
-  if (!context || !regs)
-    return false;
-
-  context->context_flags = MD_CONTEXT_X86_FULL;
-
-  context->cs = regs->xcs;
-  context->ds = regs->xds;
-  context->es = regs->xes;
-  context->fs = regs->xfs;
-  context->gs = regs->xgs;
-  context->ss = regs->xss;
-  context->edi = regs->edi;
-  context->esi = regs->esi;
-  context->ebx = regs->ebx;
-  context->edx = regs->edx;
-  context->ecx = regs->ecx;
-  context->eax = regs->eax;
-  context->ebp = regs->ebp;
-  context->eip = regs->eip;
-  context->esp = regs->esp;
-  context->eflags = regs->eflags;
-
-  if (dbg_regs != NULL) {
-    context->context_flags |= MD_CONTEXT_X86_DEBUG_REGISTERS;
-    context->dr0 = dbg_regs->dr0;
-    context->dr1 = dbg_regs->dr1;
-    context->dr2 = dbg_regs->dr2;
-    context->dr3 = dbg_regs->dr3;
-    context->dr6 = dbg_regs->dr6;
-    context->dr7 = dbg_regs->dr7;
-  }
-
-  if (fp_regs != NULL) {
-    context->context_flags |= MD_CONTEXT_X86_FLOATING_POINT;
-    context->float_save.control_word = fp_regs->cwd;
-    context->float_save.status_word = fp_regs->swd;
-    context->float_save.tag_word = fp_regs->twd;
-    context->float_save.error_offset = fp_regs->fip;
-    context->float_save.error_selector = fp_regs->fcs;
-    context->float_save.data_offset = fp_regs->foo;
-    context->float_save.data_selector = fp_regs->fos;
-    context->float_save.data_selector = fp_regs->fos;
-
-    memcpy(context->float_save.register_area, fp_regs->st_space,
-           sizeof(context->float_save.register_area));
-  }
-  return true;
-}
-
-// Write information about a crashed thread.
-// When a thread crash, kernel will write something on the stack for processing
-// signal. This makes the current stack not reliable, and our stack walker
-// won't figure out the whole call stack for this. So we write the stack at the
-// time of the crash into the minidump file, not the current stack.
-bool WriteCrashedThreadStream(MinidumpFileWriter *minidump_writer,
-                       const WriterArgument *writer_args,
-                       const ThreadInfo &thread_info,
-                       MDRawThread *thread) {
-  assert(writer_args->sig_ctx != NULL);
-
-  thread->thread_id = thread_info.pid;
-
-  UntypedMDRVA memory(minidump_writer);
-  if (!WriteThreadStack(writer_args->sig_ctx->ebp,
-                        writer_args->sig_ctx->esp,
-                        writer_args->thread_lister,
-                        &memory,
-                        &thread->stack))
-    return false;
-
-  TypedMDRVA<MDRawContextX86> context(minidump_writer);
-  if (!context.Allocate())
-    return false;
-  thread->thread_context = context.location();
-  memset(context.get(), 0, sizeof(MDRawContextX86));
-  return WriteContext(context.get(), writer_args->sig_ctx, NULL);
-}
-
-// Write information about a thread.
-// This function only processes thread running normally at the crash.
-bool WriteThreadStream(MinidumpFileWriter *minidump_writer,
-                       const LinuxThread *thread_lister,
-                       const ThreadInfo &thread_info,
-                       MDRawThread *thread) {
-  thread->thread_id = thread_info.pid;
-
-  struct user_regs_struct regs;
-  memset(&regs, 0, sizeof(regs));
-  if (!thread_lister->GetRegisters(thread_info.pid, &regs)) {
-    perror(NULL);
-    return false;
-  }
-
-  UntypedMDRVA memory(minidump_writer);
-  if (!WriteThreadStack(regs.ebp,
-                   regs.esp,
-                   thread_lister,
-                   &memory,
-                   &thread->stack))
-    return false;
-
-  struct user_fpregs_struct fp_regs;
-  DebugRegs dbg_regs;
-  memset(&fp_regs, 0, sizeof(fp_regs));
-  // Get all the registers.
-  thread_lister->GetFPRegisters(thread_info.pid, &fp_regs);
-  thread_lister->GetDebugRegisters(thread_info.pid, &dbg_regs);
-
-  // Write context
-  TypedMDRVA<MDRawContextX86> context(minidump_writer);
-  if (!context.Allocate())
-    return false;
-  thread->thread_context = context.location();
-  memset(context.get(), 0, sizeof(MDRawContextX86));
-  return WriteContext(context.get(), &regs, &fp_regs, &dbg_regs);
-}
-
-bool WriteCPUInformation(MDRawSystemInfo *sys_info) {
-  const char *proc_cpu_path = "/proc/cpuinfo";
-  char line[128];
-  char vendor_id[13];
-  const char vendor_id_name[] = "vendor_id";
-  const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1;
-
-  struct CpuInfoEntry {
-    const char *info_name;
-    int value;
-  } cpu_info_table[] = {
-    { "processor", -1 },
-    { "model", 0 },
-    { "stepping",  0 },
-    { "cpuid level", 0 },
-    { NULL, -1 },
-  };
-
-  memset(vendor_id, 0, sizeof(vendor_id));
-
-  FILE *fp = fopen(proc_cpu_path, "r");
-  if (fp != NULL) {
-    while (fgets(line, sizeof(line), fp)) {
-      CpuInfoEntry *entry = &cpu_info_table[0];
-      while (entry->info_name != NULL) {
-        if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
-          char *value = strchr(line, ':');
-          value++;
-          if (value != NULL)
-            sscanf(value, " %d", &(entry->value));
-        }
-        entry++;
-      }
-
-      // special case for vendor_id
-      if (!strncmp(line, vendor_id_name, vendor_id_name_length)) {
-        char *value = strchr(line, ':');
-        if (value == NULL)
-          continue;
-
-        value++;
-        while (*value && isspace(*value))
-          value++;
-        if (*value) {
-          size_t length = strlen(value);
-          // we don't want the trailing newline
-          if (value[length - 1] == '\n')
-            length--;
-          // ensure we have space for the value
-          if (length < sizeof(vendor_id))
-            strncpy(vendor_id, value, length);
-        }
-      }
-    }
-    fclose(fp);
-  }
-
-  // /proc/cpuinfo contains cpu id, change it into number by adding one.
-  cpu_info_table[0].value++;
-
-  sys_info->number_of_processors = cpu_info_table[0].value;
-  sys_info->processor_level      = cpu_info_table[3].value;
-  sys_info->processor_revision   = cpu_info_table[1].value << 8 |
-                                   cpu_info_table[2].value;
-
-  sys_info->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
-  struct utsname uts;
-  if (uname(&uts) == 0) {
-    // Match i*86 and x86* as X86 architecture.
-    if ((strstr(uts.machine, "x86") == uts.machine) ||
-        (strlen(uts.machine) == 4 &&
-         uts.machine[0] == 'i' &&
-         uts.machine[2] == '8' &&
-         uts.machine[3] == '6')) {
-      sys_info->processor_architecture = MD_CPU_ARCHITECTURE_X86;
-      if (vendor_id[0] != '\0')
-        memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
-               sizeof(sys_info->cpu.x86_cpu_info.vendor_id));
-    }
-  }
-  return true;
-}
-
-bool WriteOSInformation(MinidumpFileWriter *minidump_writer,
-                        MDRawSystemInfo *sys_info) {
-  sys_info->platform_id = MD_OS_LINUX;
-
-  struct utsname uts;
-  if (uname(&uts) == 0) {
-    char os_version[512];
-    size_t space_left = sizeof(os_version);
-    memset(os_version, 0, space_left);
-    const char *os_info_table[] = {
-      uts.sysname,
-      uts.release,
-      uts.version,
-      uts.machine,
-      "GNU/Linux",
-      NULL
-    };
-    for (const char **cur_os_info = os_info_table;
-         *cur_os_info != NULL;
-         cur_os_info++) {
-      if (cur_os_info != os_info_table && space_left > 1) {
-        strcat(os_version, " ");
-        space_left--;
-      }
-      if (space_left > strlen(*cur_os_info)) {
-        strcat(os_version, *cur_os_info);
-        space_left -= strlen(*cur_os_info);
-      } else {
-        break;
-      }
-    }
-
-    MDLocationDescriptor location;
-    if (!minidump_writer->WriteString(os_version, 0, &location))
-      return false;
-    sys_info->csd_version_rva = location.rva;
-  }
-  return true;
-}
-
-// Callback context for get writting thread information.
-struct ThreadInfoCallbackCtx {
-  MinidumpFileWriter *minidump_writer;
-  const WriterArgument *writer_args;
-  TypedMDRVA<MDRawThreadList> *list;
-  int thread_index;
-};
-
-// Callback run for writing threads information in the process.
-bool ThreadInfomationCallback(const ThreadInfo &thread_info,
-                                 void *context) {
-  ThreadInfoCallbackCtx *callback_context =
-    static_cast<ThreadInfoCallbackCtx *>(context);
-  bool success = true;
-  MDRawThread thread;
-  memset(&thread, 0, sizeof(MDRawThread));
-  if (thread_info.pid != callback_context->writer_args->crashed_pid ||
-      callback_context->writer_args->sig_ctx == NULL) {
-    success = WriteThreadStream(callback_context->minidump_writer,
-                           callback_context->writer_args->thread_lister,
-                           thread_info, &thread);
-  } else {
-    success = WriteCrashedThreadStream(callback_context->minidump_writer,
-                                       callback_context->writer_args,
-                                       thread_info, &thread);
-  }
-  if (success) {
-    callback_context->list->CopyIndexAfterObject(
-        callback_context->thread_index++,
-        &thread, sizeof(MDRawThread));
-  }
-  return success;
-}
-
-// Stream writers
-bool WriteThreadListStream(MinidumpFileWriter *minidump_writer,
-                           const WriterArgument *writer_args,
-                           MDRawDirectory *dir) {
-  // Get the thread information.
-  const LinuxThread *thread_lister = writer_args->thread_lister;
-  int thread_count = thread_lister->GetThreadCount();
-  if (thread_count < 0)
-    return false;
-  TypedMDRVA<MDRawThreadList> list(minidump_writer);
-  if (!list.AllocateObjectAndArray(thread_count, sizeof(MDRawThread)))
-    return false;
-  dir->stream_type = MD_THREAD_LIST_STREAM;
-  dir->location = list.location();
-  list.get()->number_of_threads = thread_count;
-
-  ThreadInfoCallbackCtx context;
-  context.minidump_writer = minidump_writer;
-  context.writer_args = writer_args;
-  context.list = &list;
-  context.thread_index = 0;
-  CallbackParam<ThreadCallback> callback_param(ThreadInfomationCallback,
-                                               &context);
-  int written = thread_lister->ListThreads(&callback_param);
-  return written == thread_count;
-}
-
-bool WriteCVRecord(MinidumpFileWriter *minidump_writer,
-                   MDRawModule *module,
-                   const char *module_path) {
-  TypedMDRVA<MDCVInfoPDB70> cv(minidump_writer);
-
-  // Only return the last path component of the full module path
-  const char *module_name = strrchr(module_path, '/');
-  // Increment past the slash
-  if (module_name)
-    ++module_name;
-  else
-    module_name = "<Unknown>";
-
-  size_t module_name_length = strlen(module_name);
-  if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t)))
-    return false;
-  if (!cv.CopyIndexAfterObject(0, const_cast<char *>(module_name),
-                               module_name_length))
-    return false;
-
-  module->cv_record = cv.location();
-  MDCVInfoPDB70 *cv_ptr = cv.get();
-  memset(cv_ptr, 0, sizeof(MDCVInfoPDB70));
-  cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
-  cv_ptr->age = 0;
-
-  // Get the module identifier
-  FileID file_id(module_path);
-  unsigned char identifier[16];
-
-  if (file_id.ElfFileIdentifier(identifier)) {
-    cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
-      (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
-      (uint32_t)identifier[3];
-    cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
-    cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
-    cv_ptr->signature.data4[0] = identifier[8];
-    cv_ptr->signature.data4[1] = identifier[9];
-    cv_ptr->signature.data4[2] = identifier[10];
-    cv_ptr->signature.data4[3] = identifier[11];
-    cv_ptr->signature.data4[4] = identifier[12];
-    cv_ptr->signature.data4[5] = identifier[13];
-    cv_ptr->signature.data4[6] = identifier[14];
-    cv_ptr->signature.data4[7] = identifier[15];
-  }
-  return true;
-}
-
-struct ModuleInfoCallbackCtx {
-  MinidumpFileWriter *minidump_writer;
-  const WriterArgument *writer_args;
-  TypedMDRVA<MDRawModuleList> *list;
-  int module_index;
-};
-
-bool ModuleInfoCallback(const ModuleInfo &module_info,
-                           void *context) {
-  ModuleInfoCallbackCtx *callback_context =
-    static_cast<ModuleInfoCallbackCtx *>(context);
-  // Skip those modules without name, or those that are not modules.
-  if (strlen(module_info.name) == 0 ||
-      !strchr(module_info.name, '/'))
-    return true;
-
-  MDRawModule module;
-  memset(&module, 0, sizeof(module));
-  MDLocationDescriptor loc;
-  if (!callback_context->minidump_writer->WriteString(module_info.name, 0,
-                                                      &loc))
-    return false;
-  module.base_of_image = (u_int64_t)module_info.start_addr;
-  module.size_of_image = module_info.size;
-  module.module_name_rva = loc.rva;
-
-  if (!WriteCVRecord(callback_context->minidump_writer, &module,
-                     module_info.name))
-    return false;
-  callback_context->list->CopyIndexAfterObject(
-      callback_context->module_index++, &module, MD_MODULE_SIZE);
-  return true;
-}
-
-bool WriteModuleListStream(MinidumpFileWriter *minidump_writer,
-                           const WriterArgument *writer_args,
-                           MDRawDirectory *dir) {
-  TypedMDRVA<MDRawModuleList> list(minidump_writer);
-  int module_count  = writer_args->thread_lister->GetModuleCount();
-  if (module_count <= 0 ||
-      !list.AllocateObjectAndArray(module_count, MD_MODULE_SIZE))
-    return false;
-  dir->stream_type = MD_MODULE_LIST_STREAM;
-  dir->location = list.location();
-  list.get()->number_of_modules = module_count;
-  ModuleInfoCallbackCtx context;
-  context.minidump_writer = minidump_writer;
-  context.writer_args = writer_args;
-  context.list = &list;
-  context.module_index = 0;
-  CallbackParam<ModuleCallback> callback(ModuleInfoCallback, &context);
-  return writer_args->thread_lister->ListModules(&callback) == module_count;
-}
-
-bool WriteSystemInfoStream(MinidumpFileWriter *minidump_writer,
-                           const WriterArgument *writer_args,
-                           MDRawDirectory *dir) {
-  TypedMDRVA<MDRawSystemInfo> sys_info(minidump_writer);
-  if (!sys_info.Allocate())
-    return false;
-  dir->stream_type = MD_SYSTEM_INFO_STREAM;
-  dir->location = sys_info.location();
-
-  return WriteCPUInformation(sys_info.get()) &&
-         WriteOSInformation(minidump_writer, sys_info.get());
-}
-
-bool WriteExceptionStream(MinidumpFileWriter *minidump_writer,
-                           const WriterArgument *writer_args,
-                           MDRawDirectory *dir) {
-  // This happenes when this is not a crash, but a requested dump.
-  if (writer_args->sig_ctx == NULL)
-    return false;
-
-  TypedMDRVA<MDRawExceptionStream> exception(minidump_writer);
-  if (!exception.Allocate())
-    return false;
-
-  dir->stream_type = MD_EXCEPTION_STREAM;
-  dir->location = exception.location();
-  exception.get()->thread_id = writer_args->crashed_pid;
-  exception.get()->exception_record.exception_code = writer_args->signo;
-  exception.get()->exception_record.exception_flags = 0;
-  if (writer_args->sig_ctx != NULL) {
-    exception.get()->exception_record.exception_address =
-      writer_args->sig_ctx->eip;
-  } else {
-    return true;
-  }
-
-  // Write context of the exception.
-  TypedMDRVA<MDRawContextX86> context(minidump_writer);
-  if (!context.Allocate())
-    return false;
-  exception.get()->thread_context = context.location();
-  memset(context.get(), 0, sizeof(MDRawContextX86));
-  return WriteContext(context.get(), writer_args->sig_ctx, NULL);
-}
-
-bool WriteMiscInfoStream(MinidumpFileWriter *minidump_writer,
-                           const WriterArgument *writer_args,
-                           MDRawDirectory *dir) {
-  TypedMDRVA<MDRawMiscInfo> info(minidump_writer);
-  if (!info.Allocate())
-    return false;
-
-  dir->stream_type = MD_MISC_INFO_STREAM;
-  dir->location = info.location();
-  info.get()->size_of_info = sizeof(MDRawMiscInfo);
-  info.get()->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID;
-  info.get()->process_id = writer_args->requester_pid;
-
-  return true;
-}
-
-bool WriteBreakpadInfoStream(MinidumpFileWriter *minidump_writer,
-                           const WriterArgument *writer_args,
-                           MDRawDirectory *dir) {
-  TypedMDRVA<MDRawBreakpadInfo> info(minidump_writer);
-  if (!info.Allocate())
-    return false;
-
-  dir->stream_type = MD_BREAKPAD_INFO_STREAM;
-  dir->location = info.location();
-
-  info.get()->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
-                        MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
-  info.get()->dump_thread_id = getpid();
-  info.get()->requesting_thread_id = writer_args->requester_pid;
-  return true;
-}
-
-// Prototype of writer functions.
-typedef bool (*WriteStringFN)(MinidumpFileWriter *,
-                              const WriterArgument *,
-                              MDRawDirectory *);
-
-// Function table to writer a full minidump.
-WriteStringFN writers[] = {
-  WriteThreadListStream,
-  WriteModuleListStream,
-  WriteSystemInfoStream,
-  WriteExceptionStream,
-  WriteMiscInfoStream,
-  WriteBreakpadInfoStream,
-};
-
-// Will call each writer function in the writers table.
-// It runs in a different process from the crashing process, but sharing
-// the same address space. This enables it to use ptrace functions.
-int Write(void *argument) {
-  WriterArgument *writer_args =
-    static_cast<WriterArgument *>(argument);
-
-  if (!writer_args->thread_lister->SuspendAllThreads())
-    return -1;
-
-  if (writer_args->sighandler_ebp != 0 &&
-      writer_args->thread_lister->FindSigContext(writer_args->sighandler_ebp,
-                                                 &writer_args->sig_ctx)) {
-    writer_args->crashed_stack_bottom =
-      writer_args->thread_lister->GetThreadStackBottom(
-                                             writer_args->sig_ctx->ebp);
-    int crashed_pid =  FindCrashingThread(writer_args->crashed_stack_bottom,
-                                          writer_args->requester_pid,
-                                          writer_args->thread_lister);
-    if (crashed_pid > 0)
-      writer_args->crashed_pid = crashed_pid;
-  }
-
-
-  MinidumpFileWriter *minidump_writer = writer_args->minidump_writer;
-  TypedMDRVA<MDRawHeader> header(minidump_writer);
-  TypedMDRVA<MDRawDirectory> dir(minidump_writer);
-  if (!header.Allocate())
-    return 0;
-
-  int writer_count = sizeof(writers) / sizeof(writers[0]);
-  // Need directory space for all writers.
-  if (!dir.AllocateArray(writer_count))
-    return 0;
-  header.get()->signature = MD_HEADER_SIGNATURE;
-  header.get()->version = MD_HEADER_VERSION;
-  header.get()->time_date_stamp = time(NULL);
-  header.get()->stream_count = writer_count;
-  header.get()->stream_directory_rva = dir.position();
-
-  int dir_index = 0;
-  MDRawDirectory local_dir;
-  for (int i = 0; i < writer_count; ++i) {
-    if (writers[i](minidump_writer, writer_args, &local_dir))
-      dir.CopyIndex(dir_index++, &local_dir);
-  }
-
-  writer_args->thread_lister->ResumeAllThreads();
-  return 0;
-}
-
-}  // namespace
-
-namespace google_breakpad {
-
-MinidumpGenerator::MinidumpGenerator() {
-  AllocateStack();
-}
-
-MinidumpGenerator::~MinidumpGenerator() {
-}
-
-void MinidumpGenerator::AllocateStack() {
-  stack_.reset(new char[kStackSize]);
-}
-
-bool MinidumpGenerator::WriteMinidumpToFile(const char *file_pathname,
-                                   int signo,
-                                   uintptr_t sighandler_ebp,
-                                   struct sigcontext **sig_ctx) const {
-  assert(file_pathname != NULL);
-  assert(stack_ != NULL);
-
-  if (stack_ == NULL || file_pathname == NULL)
-    return false;
-
-  MinidumpFileWriter minidump_writer;
-  if (minidump_writer.Open(file_pathname)) {
-    WriterArgument argument;
-    memset(&argument, 0, sizeof(argument));
-    LinuxThread thread_lister(getpid());
-    argument.thread_lister = &thread_lister;
-    argument.minidump_writer = &minidump_writer;
-    argument.requester_pid = getpid();
-    argument.crashed_pid = getpid();
-    argument.signo = signo;
-    argument.sighandler_ebp = sighandler_ebp;
-    argument.sig_ctx = NULL;
-
-    int cloned_pid = clone(Write, stack_.get() + kStackSize,
-                           CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
-                           (void*)&argument);
-    waitpid(cloned_pid, NULL, __WALL);
-    if (sig_ctx != NULL)
-        *sig_ctx = argument.sig_ctx;
-    return true;
-  }
-
-  return false;
-}
-
-}  // namespace google_breakpad
deleted file mode 100644
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2006, Google Inc.
-// All rights reserved.
-//
-// Author: Li Liu
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__
-#define CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__
-
-#include <stdint.h>
-
-#include "google_breakpad/common/breakpad_types.h"
-#include "processor/scoped_ptr.h"
-
-struct sigcontext;
-
-namespace google_breakpad {
-
-//
-// MinidumpGenerator
-//
-// Write a minidump to file based on the signo and sig_ctx.
-// A minidump generator should be created before any exception happen.
-//
-class MinidumpGenerator {
-  public:
-   MinidumpGenerator();
-
-   ~MinidumpGenerator();
-
-   // Write minidump.
-   bool WriteMinidumpToFile(const char *file_pathname,
-                            int signo,
-                            uintptr_t sighandler_ebp,
-                            struct sigcontext **sig_ctx) const;
-  private:
-   // Allocate memory for stack.
-   void AllocateStack();
-
-  private:
-   // Stack size of the writer thread.
-   static const int kStackSize = 1024 * 1024;
-   scoped_array<char> stack_;
-};
-
-}  // namespace google_breakpad
-
-#endif   // CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__
deleted file mode 100644
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 2006, Google Inc.
-// All rights reserved.
-//
-// Author: Li Liu
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <pthread.h>
-#include <unistd.h>
-
-#include <cassert>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include "client/linux/handler/minidump_generator.h"
-
-using namespace google_breakpad;
-
-// Thread use this to see if it should stop working.
-static bool should_exit = false;
-
-static void foo2(int arg) {
-  // Stack variable, used for debugging stack dumps.
-  int c = arg;
-  c = 0xcccccccc;
-  while (!should_exit)
-    sleep(1);
-}
-
-static void foo(int arg) {
-  // Stack variable, used for debugging stack dumps.
-  int b = arg;
-  b = 0xbbbbbbbb;
-  foo2(b);
-}
-
-static void *thread_main(void *) {
-  // Stack variable, used for debugging stack dumps.
-  int a = 0xaaaaaaaa;
-  foo(a);
-  return NULL;
-}
-
-static void CreateThread(int num) {
-  pthread_t h;
-  for (int i = 0; i < num; ++i) {
-    pthread_create(&h, NULL, thread_main, NULL);
-    pthread_detach(h);
-  }
-}
-
-int main(int argc, char *argv[]) {
-  CreateThread(10);
-  google_breakpad::MinidumpGenerator mg;
-  if (mg.WriteMinidumpToFile("minidump_test.out", -1, 0, NULL))
-    printf("Succeeded written minidump\n");
-  else
-    printf("Failed to write minidump\n");
-  should_exit = true;
-  return 0;
-}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
+
+#include <stdint.h>
+#include <unistd.h>
+#include <limits.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include "common/linux/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+// A class for enumerating a directory without using diropen/readdir or other
+// functions which may allocate memory.
+class DirectoryReader {
+ public:
+  DirectoryReader(int fd)
+      : fd_(fd),
+        buf_used_(0) {
+  }
+
+  // Return the next entry from the directory
+  //   name: (output) the NUL terminated entry name
+  //
+  // Returns true iff successful (false on EOF).
+  //
+  // After calling this, one must call |PopEntry| otherwise you'll get the same
+  // entry over and over.
+  bool GetNextEntry(const char** name) {
+    struct kernel_dirent* const dent =
+      reinterpret_cast<kernel_dirent*>(buf_);
+
+    if (buf_used_ == 0) {
+      // need to read more entries.
+      const int n = sys_getdents(fd_, dent, sizeof(buf_));
+      if (n < 0) {
+        return false;
+      } else if (n == 0) {
+        hit_eof_ = true;
+      } else {
+        buf_used_ += n;
+      }
+    }
+
+    if (buf_used_ == 0 && hit_eof_)
+      return false;
+
+    assert(buf_used_ > 0);
+
+    *name = dent->d_name;
+    return true;
+  }
+
+  void PopEntry() {
+    if (!buf_used_)
+      return;
+
+    const struct kernel_dirent* const dent =
+      reinterpret_cast<kernel_dirent*>(buf_);
+
+    buf_used_ -= dent->d_reclen;
+    memmove(buf_, buf_ + dent->d_reclen, buf_used_);
+  }
+
+ private:
+  const int fd_;
+  bool hit_eof_;
+  unsigned buf_used_;
+  uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1];
+};
+
+}  // namespace google_breakpad
+
+#endif  // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <set>
+#include <string>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "client/linux/minidump_writer/directory_reader.h"
+#include "breakpad_googletest_includes.h"
+
+using namespace google_breakpad;
+
+namespace {
+typedef testing::Test DirectoryReaderTest;
+}
+
+TEST(DirectoryReaderTest, CompareResults) {
+  std::set<std::string> dent_set;
+
+  DIR *const dir = opendir("/proc/self");
+  ASSERT_TRUE(dir != NULL);
+
+  struct dirent* dent;
+  while ((dent = readdir(dir)))
+    dent_set.insert(dent->d_name);
+
+  closedir(dir);
+
+  const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY);
+  ASSERT_GE(fd, 0);
+
+  DirectoryReader dir_reader(fd);
+  unsigned seen = 0;
+
+  const char* name;
+  while (dir_reader.GetNextEntry(&name)) {
+    ASSERT_TRUE(dent_set.find(name) != dent_set.end());
+    seen++;
+    dir_reader.PopEntry();
+  }
+
+  ASSERT_TRUE(dent_set.find("status") != dent_set.end());
+  ASSERT_TRUE(dent_set.find("stat") != dent_set.end());
+  ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end());
+
+  ASSERT_EQ(dent_set.size(), seen);
+  close(fd);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
+
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#include "common/linux/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+// A class for reading a file, line by line, without using fopen/fgets or other
+// functions which may allocate memory.
+class LineReader {
+ public:
+  LineReader(int fd)
+      : fd_(fd),
+        hit_eof_(false),
+        buf_used_(0) {
+  }
+
+  // The maximum length of a line.
+  static const size_t kMaxLineLen = 512;
+
+  // Return the next line from the file.
+  //   line: (output) a pointer to the start of the line. The line is NUL
+  //     terminated.
+  //   len: (output) the length of the line (not inc the NUL byte)
+  //
+  // Returns true iff successful (false on EOF).
+  //
+  // One must call |PopLine| after this function, otherwise you'll continue to
+  // get the same line over and over.
+  bool GetNextLine(const char **line, unsigned *len) {
+    for (;;) {
+      if (buf_used_ == 0 && hit_eof_)
+        return false;
+
+      for (unsigned i = 0; i < buf_used_; ++i) {
+        if (buf_[i] == '\n' || buf_[i] == 0) {
+          buf_[i] = 0;
+          *len = i;
+          *line = buf_;
+          return true;
+        }
+      }
+
+      if (buf_used_ == sizeof(buf_)) {
+        // we scanned the whole buffer and didn't find an end-of-line marker.
+        // This line is too long to process.
+        return false;
+      }
+
+      // We didn't find any end-of-line terminators in the buffer. However, if
+      // this is the last line in the file it might not have one:
+      if (hit_eof_) {
+        assert(buf_used_);
+        // There's room for the NUL because of the buf_used_ == sizeof(buf_)
+        // check above.
+        buf_[buf_used_] = 0;
+        *len = buf_used_;
+        buf_used_ += 1;  // since we appended the NUL.
+        *line = buf_;
+        return true;
+      }
+
+      // Otherwise, we should pull in more data from the file
+      const ssize_t n = sys_read(fd_, buf_ + buf_used_,
+                                 sizeof(buf_) - buf_used_);
+      if (n < 0) {
+        return false;
+      } else if (n == 0) {
+        hit_eof_ = true;
+      } else {
+        buf_used_ += n;
+      }
+
+      // At this point, we have either set the hit_eof_ flag, or we have more
+      // data to process...
+    }
+  }
+
+  void PopLine(unsigned len) {
+    // len doesn't include the NUL byte at the end.
+
+    assert(buf_used_ >= len + 1);
+    buf_used_ -= len + 1;
+    memmove(buf_, buf_ + len + 1, buf_used_);
+  }
+
+ private:
+  const int fd_;
+
+  bool hit_eof_;
+  unsigned buf_used_;
+  char buf_[kMaxLineLen];
+};
+
+}  // namespace google_breakpad
+
+#endif  // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "client/linux/minidump_writer/line_reader.h"
+#include "breakpad_googletest_includes.h"
+
+using namespace google_breakpad;
+
+static int TemporaryFile() {
+  static const char templ[] = "/tmp/line-reader-unittest-XXXXXX";
+  char templ_copy[sizeof(templ)];
+  memcpy(templ_copy, templ, sizeof(templ));
+  const int fd = mkstemp(templ_copy);
+  if (fd >= 0)
+    unlink(templ_copy);
+
+  return fd;
+}
+
+namespace {
+typedef testing::Test LineReaderTest;
+}
+
+TEST(LineReaderTest, EmptyFile) {
+  const int fd = TemporaryFile();
+  LineReader reader(fd);
+
+  const char *line;
+  unsigned len;
+  ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+  close(fd);
+}
+
+TEST(LineReaderTest, OneLineTerminated) {
+  const int fd = TemporaryFile();
+  write(fd, "a\n", 2);
+  lseek(fd, 0, SEEK_SET);
+  LineReader reader(fd);
+
+  const char *line;
+  unsigned len;
+  ASSERT_TRUE(reader.GetNextLine(&line, &len));
+  ASSERT_EQ(len, 1);
+  ASSERT_EQ(line[0], 'a');
+  ASSERT_EQ(line[1], 0);
+  reader.PopLine(len);
+
+  ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+  close(fd);
+}
+
+TEST(LineReaderTest, OneLine) {
+  const int fd = TemporaryFile();
+  write(fd, "a", 1);
+  lseek(fd, 0, SEEK_SET);
+  LineReader reader(fd);
+
+  const char *line;
+  unsigned len;
+  ASSERT_TRUE(reader.GetNextLine(&line, &len));
+  ASSERT_EQ(len, 1);
+  ASSERT_EQ(line[0], 'a');
+  ASSERT_EQ(line[1], 0);
+  reader.PopLine(len);
+
+  ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+  close(fd);
+}
+
+TEST(LineReaderTest, TwoLinesTerminated) {
+  const int fd = TemporaryFile();
+  write(fd, "a\nb\n", 4);
+  lseek(fd, 0, SEEK_SET);
+  LineReader reader(fd);
+
+  const char *line;
+  unsigned len;
+  ASSERT_TRUE(reader.GetNextLine(&line, &len));
+  ASSERT_EQ(len, 1);
+  ASSERT_EQ(line[0], 'a');
+  ASSERT_EQ(line[1], 0);
+  reader.PopLine(len);
+
+  ASSERT_TRUE(reader.GetNextLine(&line, &len));
+  ASSERT_EQ(len, 1);
+  ASSERT_EQ(line[0], 'b');
+  ASSERT_EQ(line[1], 0);
+  reader.PopLine(len);
+
+  ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+  close(fd);
+}
+
+TEST(LineReaderTest, TwoLines) {
+  const int fd = TemporaryFile();
+  write(fd, "a\nb", 3);
+  lseek(fd, 0, SEEK_SET);
+  LineReader reader(fd);
+
+  const char *line;
+  unsigned len;
+  ASSERT_TRUE(reader.GetNextLine(&line, &len));
+  ASSERT_EQ(len, 1);
+  ASSERT_EQ(line[0], 'a');
+  ASSERT_EQ(line[1], 0);
+  reader.PopLine(len);
+
+  ASSERT_TRUE(reader.GetNextLine(&line, &len));
+  ASSERT_EQ(len, 1);
+  ASSERT_EQ(line[0], 'b');
+  ASSERT_EQ(line[1], 0);
+  reader.PopLine(len);
+
+  ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+  close(fd);
+}
+
+TEST(LineReaderTest, MaxLength) {
+  const int fd = TemporaryFile();
+  char l[LineReader::kMaxLineLen - 1];
+  memset(l, 'a', sizeof(l));
+  write(fd, l, sizeof(l));
+  lseek(fd, 0, SEEK_SET);
+  LineReader reader(fd);
+
+  const char *line;
+  unsigned len;
+  ASSERT_TRUE(reader.GetNextLine(&line, &len));
+  ASSERT_EQ(len, sizeof(l));
+  ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
+  ASSERT_EQ(line[len], 0);
+
+  close(fd);
+}
+
+TEST(LineReaderTest, TooLong) {
+  const int fd = TemporaryFile();
+  char l[LineReader::kMaxLineLen];
+  memset(l, 'a', sizeof(l));
+  write(fd, l, sizeof(l));
+  lseek(fd, 0, SEEK_SET);
+  LineReader reader(fd);
+
+  const char *line;
+  unsigned len;
+  ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+  close(fd);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc
@@ -0,0 +1,421 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This code deals with the mechanics of getting information about a crashed
+// process. Since this code may run in a compromised address space, the same
+// rules apply as detailed at the top of minidump_writer.h: no libc calls and
+// use the alternative allocator.
+
+#include "client/linux/minidump_writer/linux_dumper.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
+#include "client/linux/minidump_writer/directory_reader.h"
+#include "client/linux/minidump_writer/line_reader.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/linux/linux_syscall_support.h"
+
+// Suspend a thread by attaching to it.
+static bool SuspendThread(pid_t pid) {
+  // This may fail if the thread has just died or debugged.
+  errno = 0;
+  if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
+      errno != 0) {
+    return false;
+  }
+  while (sys_waitpid(pid, NULL, __WALL) < 0) {
+    if (errno != EINTR) {
+      sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
+      return false;
+    }
+  }
+  return true;
+}
+
+// Resume a thread by detaching from it.
+static bool ResumeThread(pid_t pid) {
+  return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
+}
+
+namespace google_breakpad {
+
+LinuxDumper::LinuxDumper(int pid)
+    : pid_(pid),
+      threads_suspened_(false),
+      threads_(&allocator_, 8),
+      mappings_(&allocator_) {
+}
+
+bool LinuxDumper::Init() {
+  return EnumerateThreads(&threads_) &&
+         EnumerateMappings(&mappings_);
+}
+
+bool LinuxDumper::ThreadsSuspend() {
+  if (threads_suspened_)
+    return true;
+  bool good = true;
+  for (size_t i = 0; i < threads_.size(); ++i)
+    good &= SuspendThread(threads_[i]);
+  threads_suspened_ = true;
+  return good;
+}
+
+bool LinuxDumper::ThreadsResume() {
+  if (!threads_suspened_)
+    return false;
+  bool good = true;
+  for (size_t i = 0; i < threads_.size(); ++i)
+    good &= ResumeThread(threads_[i]);
+  threads_suspened_ = false;
+  return good;
+}
+
+void
+LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const {
+  assert(path);
+  if (!path) {
+    return;
+  }
+
+  path[0] = '\0';
+
+  const unsigned pid_len = my_int_len(pid);
+
+  assert(node);
+  if (!node) {
+    return;
+  }
+
+  size_t node_len = my_strlen(node);
+  assert(node_len < NAME_MAX);
+  if (node_len >= NAME_MAX) {
+    return;
+  }
+
+  assert(node_len > 0);
+  if (node_len == 0) {
+    return;
+  }
+
+  assert(pid > 0);
+  if (pid <= 0) {
+    return;
+  }
+
+  const size_t total_length = 6 + pid_len + 1 + node_len;
+
+  assert(total_length < NAME_MAX);
+  if (total_length >= NAME_MAX) {
+    return;
+  }
+
+  memcpy(path, "/proc/", 6);
+  my_itos(path + 6, pid, pid_len);
+  memcpy(path + 6 + pid_len, "/", 1);
+  memcpy(path + 6 + pid_len + 1, node, node_len);
+  memcpy(path + total_length, "\0", 1);
+}
+
+void*
+LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const {
+  char auxv_path[80];
+  BuildProcPath(auxv_path, pid, "auxv");
+
+  // If BuildProcPath errors out due to invalid input, we'll handle it when
+  // we try to sys_open the file.
+
+  // Find the AT_SYSINFO_EHDR entry for linux-gate.so
+  // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
+  // information.
+  int fd = sys_open(auxv_path, O_RDONLY, 0);
+  if (fd < 0) {
+    return NULL;
+  }
+
+  elf_aux_entry one_aux_entry;
+  while (sys_read(fd,
+                  &one_aux_entry,
+                  sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
+         one_aux_entry.a_type != AT_NULL) {
+    if (one_aux_entry.a_type == AT_SYSINFO_EHDR) {
+      close(fd);
+      return reinterpret_cast<void*>(one_aux_entry.a_un.a_val);
+    }
+  }
+  close(fd);
+  return NULL;
+}
+
+bool
+LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
+  char maps_path[80];
+  BuildProcPath(maps_path, pid_, "maps");
+
+  // linux_gate_loc is the beginning of the kernel's mapping of
+  // linux-gate.so in the process.  It doesn't actually show up in the
+  // maps list as a filename, so we use the aux vector to find it's
+  // load location and special case it's entry when creating the list
+  // of mappings.
+  const void* linux_gate_loc;
+  linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_);
+
+  const int fd = sys_open(maps_path, O_RDONLY, 0);
+  if (fd < 0)
+    return false;
+  LineReader* const line_reader = new(allocator_) LineReader(fd);
+
+  const char* line;
+  unsigned line_len;
+  while (line_reader->GetNextLine(&line, &line_len)) {
+    uintptr_t start_addr, end_addr, offset;
+
+    const char* i1 = my_read_hex_ptr(&start_addr, line);
+    if (*i1 == '-') {
+      const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1);
+      if (*i2 == ' ') {
+        const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
+        if (*i3 == ' ') {
+          MappingInfo* const module = new(allocator_) MappingInfo;
+          memset(module, 0, sizeof(MappingInfo));
+          module->start_addr = start_addr;
+          module->size = end_addr - start_addr;
+          module->offset = offset;
+          const char* name = NULL;
+          // Only copy name if the name is a valid path name, or if
+          // we've found the VDSO image
+          if ((name = my_strchr(line, '/')) != NULL) {
+            const unsigned l = my_strlen(name);
+            if (l < sizeof(module->name))
+              memcpy(module->name, name, l);
+          } else if (linux_gate_loc &&
+                     reinterpret_cast<void*>(module->start_addr) ==
+                     linux_gate_loc) {
+            memcpy(module->name,
+                   kLinuxGateLibraryName,
+                   my_strlen(kLinuxGateLibraryName));
+            module->offset = 0;
+          }
+          result->push_back(module);
+        }
+      }
+    }
+    line_reader->PopLine(line_len);
+  }
+
+  sys_close(fd);
+
+  return result->size() > 0;
+}
+
+// Parse /proc/$pid/task to list all the threads of the process identified by
+// pid.
+bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
+  char task_path[80];
+  BuildProcPath(task_path, pid_, "task");
+
+  const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
+  if (fd < 0)
+    return false;
+  DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd);
+
+  // The directory may contain duplicate entries which we filter by assuming
+  // that they are consecutive.
+  int last_tid = -1;
+  const char* dent_name;
+  while (dir_reader->GetNextEntry(&dent_name)) {
+    if (my_strcmp(dent_name, ".") &&
+        my_strcmp(dent_name, "..")) {
+      int tid = 0;
+      if (my_strtoui(&tid, dent_name) &&
+          last_tid != tid) {
+        last_tid = tid;
+        result->push_back(tid);
+      }
+    }
+    dir_reader->PopEntry();
+  }
+
+  sys_close(fd);
+  return true;
+}
+
+// Read thread info from /proc/$pid/status.
+// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible,
+// these members are set to -1. Returns true iff all three members are
+// availible.
+bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
+  assert(info != NULL);
+  char status_path[80];
+  BuildProcPath(status_path, tid, "status");
+
+  const int fd = open(status_path, O_RDONLY);
+  if (fd < 0)
+    return false;
+
+  LineReader* const line_reader = new(allocator_) LineReader(fd);
+  const char* line;
+  unsigned line_len;
+
+  info->ppid = info->tgid = -1;
+
+  while (line_reader->GetNextLine(&line, &line_len)) {
+    if (my_strncmp("Tgid:\t", line, 6) == 0) {
+      my_strtoui(&info->tgid, line + 6);
+    } else if (my_strncmp("PPid:\t", line, 6) == 0) {
+      my_strtoui(&info->ppid, line + 6);
+    }
+
+    line_reader->PopLine(line_len);
+  }
+
+  if (info->ppid == -1 || info->tgid == -1)
+    return false;
+
+  if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1 ||
+      sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
+    return false;
+  }
+
+#if defined(__i386)
+  if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
+    return false;
+#endif
+
+#if defined(__i386) || defined(__x86_64)
+  for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
+    if (sys_ptrace(
+        PTRACE_PEEKUSER, tid,
+        reinterpret_cast<void*> (offsetof(struct user,
+                                          u_debugreg[0]) + i *
+                                 sizeof(debugreg_t)),
+        &info->dregs[i]) == -1) {
+      return false;
+    }
+  }
+#endif
+
+  const uint8_t* stack_pointer;
+#if defined(__i386)
+  memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
+#elif defined(__x86_64)
+  memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
+#else
+#error "This code hasn't been ported to your platform yet."
+#endif
+
+  if (!GetStackInfo(&info->stack, &info->stack_len,
+                    (uintptr_t) stack_pointer))
+    return false;
+
+  return true;
+}
+
+// Get information about the stack, given the stack pointer. We don't try to
+// walk the stack since we might not have all the information needed to do
+// unwind. So we just grab, up to, 32k of stack.
+bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
+                               uintptr_t int_stack_pointer) {
+#if defined(__i386) || defined(__x86_64)
+  static const bool stack_grows_down = true;
+  static const uintptr_t page_size = 4096;
+#else
+#error "This code has not been ported to your platform yet."
+#endif
+  // Move the stack pointer to the bottom of the page that it's in.
+  uint8_t* const stack_pointer =
+      reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
+
+  // The number of bytes of stack which we try to capture.
+  static unsigned kStackToCapture = 32 * 1024;
+
+  const MappingInfo* mapping = FindMapping(stack_pointer);
+  if (!mapping)
+    return false;
+  if (stack_grows_down) {
+    const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
+    const ptrdiff_t distance_to_end =
+        static_cast<ptrdiff_t>(mapping->size) - offset;
+    *stack_len = distance_to_end > kStackToCapture ?
+                 kStackToCapture : distance_to_end;
+    *stack = stack_pointer;
+  } else {
+    const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
+    *stack_len = offset > kStackToCapture ? kStackToCapture : offset;
+    *stack = stack_pointer - *stack_len;
+  }
+
+  return true;
+}
+
+// static
+void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
+                                  size_t length) {
+  unsigned long tmp;
+  size_t done = 0;
+  static const size_t word_size = sizeof(tmp);
+  uint8_t* const local = (uint8_t*) dest;
+  uint8_t* const remote = (uint8_t*) src;
+
+  while (done < length) {
+    const size_t l = length - done > word_size ? word_size : length - done;
+    if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1)
+      tmp = 0;
+    memcpy(local + done, &tmp, l);
+    done += l;
+  }
+}
+
+// Find the mapping which the given memory address falls in.
+const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
+  const uintptr_t addr = (uintptr_t) address;
+
+  for (size_t i = 0; i < mappings_.size(); ++i) {
+    const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr);
+    if (addr >= start && addr - start < mappings_[i]->size)
+      return mappings_[i];
+  }
+
+  return NULL;
+}
+
+}  // namespace google_breakpad
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
+
+#include <elf.h>
+#include <linux/limits.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/user.h>
+
+#include "common/linux/memory.h"
+
+namespace google_breakpad {
+
+typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
+
+// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
+#if defined(__i386)
+typedef Elf32_auxv_t elf_aux_entry;
+#elif defined(__x86_64__)
+typedef Elf64_auxv_t elf_aux_entry;
+#endif
+// When we find the VDSO mapping in the process's address space, this
+// is the name we use for it when writing it to the minidump.
+// This should always be less than NAME_MAX!
+const char kLinuxGateLibraryName[] = "linux-gate.so";
+
+// We produce one of these structures for each thread in the crashed process.
+struct ThreadInfo {
+  pid_t tgid;   // thread group id
+  pid_t ppid;   // parent process
+
+  // Even on platforms where the stack grows down, the following will point to
+  // the smallest address in the stack.
+  const void* stack;  // pointer to the stack area
+  size_t stack_len;  // length of the stack to copy
+
+  user_regs_struct regs;
+  user_fpregs_struct fpregs;
+#if defined(__i386)
+  user_fpxregs_struct fpxregs;
+#endif
+
+#if defined(__i386) || defined(__x86_64)
+
+  static const unsigned kNumDebugRegisters = 8;
+  debugreg_t dregs[8];
+#endif
+};
+
+// One of these is produced for each mapping in the process (i.e. line in
+// /proc/$x/maps).
+struct MappingInfo {
+  uintptr_t start_addr;
+  size_t size;
+  size_t offset;  // offset into the backed file.
+  char name[NAME_MAX];
+};
+
+class LinuxDumper {
+ public:
+  explicit LinuxDumper(pid_t pid);
+
+  // Parse the data for |threads| and |mappings|.
+  bool Init();
+
+  // Suspend/resume all threads in the given process.
+  bool ThreadsSuspend();
+  bool ThreadsResume();
+
+  // Read information about the given thread. Returns true on success. One must
+  // have called |ThreadsSuspend| first.
+  bool ThreadInfoGet(pid_t tid, ThreadInfo* info);
+
+  // These are only valid after a call to |Init|.
+  const wasteful_vector<pid_t> &threads() { return threads_; }
+  const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
+  const MappingInfo* FindMapping(const void* address) const;
+
+  // Find a block of memory to take as the stack given the top of stack pointer.
+  //   stack: (output) the lowest address in the memory area
+  //   stack_len: (output) the length of the memory area
+  //   stack_top: the current top of the stack
+  bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);
+
+  PageAllocator* allocator() { return &allocator_; }
+
+  // memcpy from a remote process.
+  static void CopyFromProcess(void* dest, pid_t child, const void* src,
+                              size_t length);
+
+  // Builds a proc path for a certain pid for a node.  path is a
+  // character array that is overwritten, and node is the final node
+  // without any slashes.
+  void BuildProcPath(char* path, pid_t pid, const char* node) const;
+
+  // Utility method to find the location of where the kernel has
+  // mapped linux-gate.so in memory(shows up in /proc/pid/maps as
+  // [vdso], but we can't guarantee that it's the only virtual dynamic
+  // shared object.  Parsing the auxilary vector for AT_SYSINFO_EHDR
+  // is the safest way to go.)
+  void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const;
+ private:
+  bool EnumerateMappings(wasteful_vector<MappingInfo*>* result) const;
+  bool EnumerateThreads(wasteful_vector<pid_t>* result) const;
+
+  const pid_t pid_;
+
+  mutable PageAllocator allocator_;
+
+  bool threads_suspened_;
+  wasteful_vector<pid_t> threads_;  // the ids of all the threads
+  wasteful_vector<MappingInfo*> mappings_;  // info from /proc/<pid>/maps
+};
+
+}  // namespace google_breakpad
+
+#endif  // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <unistd.h>
+
+#include "client/linux/minidump_writer/linux_dumper.h"
+#include "breakpad_googletest_includes.h"
+
+using namespace google_breakpad;
+
+namespace {
+typedef testing::Test LinuxDumperTest;
+}
+
+TEST(LinuxDumperTest, Setup) {
+  LinuxDumper dumper(getpid());
+}
+
+TEST(LinuxDumperTest, FindMappings) {
+  LinuxDumper dumper(getpid());
+  ASSERT_TRUE(dumper.Init());
+
+  ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
+  ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
+  ASSERT_FALSE(dumper.FindMapping(NULL));
+}
+
+TEST(LinuxDumperTest, ThreadList) {
+  LinuxDumper dumper(getpid());
+  ASSERT_TRUE(dumper.Init());
+
+  ASSERT_GE(dumper.threads().size(), 1);
+  bool found = false;
+  for (size_t i = 0; i < dumper.threads().size(); ++i) {
+    if (dumper.threads()[i] == getpid()) {
+      found = true;
+      break;
+    }
+  }
+}
+
+TEST(LinuxDumperTest, BuildProcPath) {
+  const pid_t pid = getpid();
+  LinuxDumper dumper(pid);
+
+  char maps_path[256] = "dummymappath";
+  char maps_path_expected[256];
+  snprintf(maps_path_expected, sizeof(maps_path_expected),
+           "/proc/%d/maps", pid);
+  dumper.BuildProcPath(maps_path, pid, "maps");
+  ASSERT_STREQ(maps_path, maps_path_expected);
+
+  // In release mode, we expect BuildProcPath to handle the invalid
+  // parameters correctly and fill map_path with an empty
+  // NULL-terminated string.
+#ifdef NDEBUG
+  snprintf(maps_path, sizeof(maps_path), "dummymappath");
+  dumper.BuildProcPath(maps_path, 0, "maps");
+  EXPECT_STREQ(maps_path, "");
+
+  snprintf(maps_path, sizeof(maps_path), "dummymappath");
+  dumper.BuildProcPath(maps_path, getpid(), "");
+  EXPECT_STREQ(maps_path, "");
+
+  snprintf(maps_path, sizeof(maps_path), "dummymappath");
+  dumper.BuildProcPath(maps_path, getpid(), NULL);
+  EXPECT_STREQ(maps_path, "");
+#endif
+}
+
+TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
+  LinuxDumper dumper(getpid());
+  ASSERT_TRUE(dumper.Init());
+
+  void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid());
+  if (linux_gate_loc) {
+    bool found_linux_gate = false;
+
+    const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
+    const MappingInfo* mapping;
+    for (unsigned i = 0; i < mappings.size(); ++i) {
+      mapping = mappings[i];
+      if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
+        found_linux_gate = true;
+        break;
+      }
+    }
+    EXPECT_TRUE(found_linux_gate);
+    EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
+    EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
+  }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc
@@ -0,0 +1,872 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This code writes out minidump files:
+//   http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx
+//
+// Minidumps are a Microsoft format which Breakpad uses for recording crash
+// dumps. This code has to run in a compromised environment (the address space
+// may have received SIGSEGV), thus the following rules apply:
+//   * You may not enter the dynamic linker. This means that we cannot call
+//     any symbols in a shared library (inc libc). Because of this we replace
+//     libc functions in linux_libc_support.h.
+//   * You may not call syscalls via the libc wrappers. This rule is a subset
+//     of the first rule but it bears repeating. We have direct wrappers
+//     around the system calls in linux_syscall_support.h.
+//   * You may not malloc. There's an alternative allocator in memory.h and
+//     a canonical instance in the LinuxDumper object. We use the placement
+//     new form to allocate objects and we don't delete them.
+
+#include "client/linux/minidump_writer/minidump_writer.h"
+#include "client/minidump_file_writer-inl.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ucontext.h>
+#include <sys/user.h>
+#include <sys/utsname.h>
+
+#include "client/minidump_file_writer.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/common/minidump_cpu_amd64.h"
+#include "google_breakpad/common/minidump_cpu_x86.h"
+
+#include "client/linux/handler/exception_handler.h"
+#include "client/linux/minidump_writer/line_reader.h"
+#include "client/linux/minidump_writer//linux_dumper.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/linux/linux_syscall_support.h"
+
+// These are additional minidump stream values which are specific to the linux
+// breakpad implementation.
+enum {
+  MD_LINUX_CPU_INFO              = 0x47670003,    /* /proc/cpuinfo    */
+  MD_LINUX_PROC_STATUS           = 0x47670004,    /* /proc/$x/status  */
+  MD_LINUX_LSB_RELEASE           = 0x47670005,    /* /etc/lsb-release */
+  MD_LINUX_CMD_LINE              = 0x47670006,    /* /proc/$x/cmdline */
+  MD_LINUX_ENVIRON               = 0x47670007,    /* /proc/$x/environ */
+  MD_LINUX_AUXV                  = 0x47670008     /* /proc/$x/auxv    */
+};
+
+// Minidump defines register structures which are different from the raw
+// structures which we get from the kernel. These are platform specific
+// functions to juggle the ucontext and user structures into minidump format.
+#if defined(__i386)
+typedef MDRawContextX86 RawContextCPU;
+
+// Write a uint16_t to memory
+//   out: memory location to write to
+//   v: value to write.
+static void U16(void* out, uint16_t v) {
+  memcpy(out, &v, sizeof(v));
+}
+
+// Write a uint32_t to memory
+//   out: memory location to write to
+//   v: value to write.
+static void U32(void* out, uint32_t v) {
+  memcpy(out, &v, sizeof(v));
+}
+
+// Juggle an x86 user_(fp|fpx|)regs_struct into minidump format
+//   out: the minidump structure
+//   info: the collection of register structures.
+static void CPUFillFromThreadInfo(MDRawContextX86 *out,
+                                  const google_breakpad::ThreadInfo &info) {
+  out->context_flags = MD_CONTEXT_X86_ALL;
+
+  out->dr0 = info.dregs[0];
+  out->dr1 = info.dregs[1];
+  out->dr2 = info.dregs[2];
+  out->dr3 = info.dregs[3];
+  // 4 and 5 deliberatly omitted because they aren't included in the minidump
+  // format.
+  out->dr6 = info.dregs[6];
+  out->dr7 = info.dregs[7];
+
+  out->gs = info.regs.xgs;
+  out->fs = info.regs.xfs;
+  out->es = info.regs.xes;
+  out->ds = info.regs.xds;
+
+  out->edi = info.regs.edi;
+  out->esi = info.regs.esi;
+  out->ebx = info.regs.ebx;
+  out->edx = info.regs.edx;
+  out->ecx = info.regs.ecx;
+  out->eax = info.regs.eax;
+
+  out->ebp = info.regs.ebp;
+  out->eip = info.regs.eip;
+  out->cs = info.regs.xcs;
+  out->eflags = info.regs.eflags;
+  out->esp = info.regs.esp;
+  out->ss = info.regs.xss;
+
+  out->float_save.control_word = info.fpregs.cwd;
+  out->float_save.status_word = info.fpregs.swd;
+  out->float_save.tag_word = info.fpregs.twd;
+  out->float_save.error_offset = info.fpregs.fip;
+  out->float_save.error_selector = info.fpregs.fcs;
+  out->float_save.data_offset = info.fpregs.foo;
+  out->float_save.data_selector = info.fpregs.fos;
+
+  // 8 registers * 10 bytes per register.
+  memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8);
+
+  // This matches the Intel fpsave format.
+  U16(out->extended_registers + 0, info.fpregs.cwd);
+  U16(out->extended_registers + 2, info.fpregs.swd);
+  U16(out->extended_registers + 4, info.fpregs.twd);
+  U16(out->extended_registers + 6, info.fpxregs.fop);
+  U32(out->extended_registers + 8, info.fpxregs.fip);
+  U16(out->extended_registers + 12, info.fpxregs.fcs);
+  U32(out->extended_registers + 16, info.fpregs.foo);
+  U16(out->extended_registers + 20, info.fpregs.fos);
+  U32(out->extended_registers + 24, info.fpxregs.mxcsr);
+
+  memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128);
+  memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128);
+}
+
+// Juggle an x86 ucontext into minidump format
+//   out: the minidump structure
+//   info: the collection of register structures.
+static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc,
+                                const struct _libc_fpstate* fp) {
+  const greg_t* regs = uc->uc_mcontext.gregs;
+
+  out->context_flags = MD_CONTEXT_X86_FULL |
+                       MD_CONTEXT_X86_FLOATING_POINT;
+
+  out->gs = regs[REG_GS];
+  out->fs = regs[REG_FS];
+  out->es = regs[REG_ES];
+  out->ds = regs[REG_DS];
+
+  out->edi = regs[REG_EDI];
+  out->esi = regs[REG_ESI];
+  out->ebx = regs[REG_EBX];
+  out->edx = regs[REG_EDX];
+  out->ecx = regs[REG_ECX];
+  out->eax = regs[REG_EAX];
+
+  out->ebp = regs[REG_EBP];
+  out->eip = regs[REG_EIP];
+  out->cs = regs[REG_CS];
+  out->eflags = regs[REG_EFL];
+  out->esp = regs[REG_UESP];
+  out->ss = regs[REG_SS];
+
+  out->float_save.control_word = fp->cw;
+  out->float_save.status_word = fp->sw;
+  out->float_save.tag_word = fp->tag;
+  out->float_save.error_offset = fp->ipoff;
+  out->float_save.error_selector = fp->cssel;
+  out->float_save.data_offset = fp->dataoff;
+  out->float_save.data_selector = fp->datasel;
+
+  // 8 registers * 10 bytes per register.
+  memcpy(out->float_save.register_area, fp->_st, 10 * 8);
+}
+
+#elif defined(__x86_64)
+typedef MDRawContextAMD64 RawContextCPU;
+
+static void CPUFillFromThreadInfo(MDRawContextAMD64 *out,
+                                  const google_breakpad::ThreadInfo &info) {
+  out->context_flags = MD_CONTEXT_AMD64_FULL |
+                       MD_CONTEXT_AMD64_SEGMENTS;
+
+  out->cs = info.regs.cs;
+
+  out->ds = info.regs.ds;
+  out->es = info.regs.es;
+  out->fs = info.regs.fs;
+  out->gs = info.regs.gs;
+
+  out->ss = info.regs.ss;
+  out->eflags = info.regs.eflags;
+
+  out->dr0 = info.dregs[0];
+  out->dr1 = info.dregs[1];
+  out->dr2 = info.dregs[2];
+  out->dr3 = info.dregs[3];
+  // 4 and 5 deliberatly omitted because they aren't included in the minidump
+  // format.
+  out->dr6 = info.dregs[6];
+  out->dr7 = info.dregs[7];
+
+  out->rax = info.regs.rax;
+  out->rcx = info.regs.rcx;
+  out->rdx = info.regs.rdx;
+  out->rbx = info.regs.rbx;
+
+  out->rsp = info.regs.rsp;
+
+  out->rbp = info.regs.rbp;
+  out->rsi = info.regs.rsi;
+  out->rdi = info.regs.rdi;
+  out->r8 = info.regs.r8;
+  out->r9 = info.regs.r9;
+  out->r10 = info.regs.r10;
+  out->r11 = info.regs.r11;
+  out->r12 = info.regs.r12;
+  out->r13 = info.regs.r13;
+  out->r14 = info.regs.r14;
+  out->r15 = info.regs.r15;
+
+  out->rip = info.regs.rip;
+
+  out->flt_save.control_word = info.fpregs.cwd;
+  out->flt_save.status_word = info.fpregs.swd;
+  out->flt_save.tag_word = info.fpregs.ftw;
+  out->flt_save.error_opcode = info.fpregs.fop;
+  out->flt_save.error_offset = info.fpregs.rip;
+  out->flt_save.error_selector = 0; // We don't have this.
+  out->flt_save.data_offset = info.fpregs.rdp;
+  out->flt_save.data_selector = 0;  // We don't have this.
+  out->flt_save.mx_csr = info.fpregs.mxcsr;
+  out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask;
+  memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16);
+  memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16);
+}
+
+static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
+                                const struct _libc_fpstate* fpregs) {
+  const greg_t* regs = uc->uc_mcontext.gregs;
+
+  out->context_flags = MD_CONTEXT_AMD64_FULL;
+
+  out->cs = regs[REG_CSGSFS] & 0xffff;
+
+  out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
+  out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
+
+  out->eflags = regs[REG_EFL];
+
+  out->rax = regs[REG_RAX];
+  out->rcx = regs[REG_RCX];
+  out->rdx = regs[REG_RDX];
+  out->rbx = regs[REG_RBX];
+
+  out->rsp = regs[REG_RSP];
+  out->rbp = regs[REG_RBP];
+  out->rsi = regs[REG_RSI];
+  out->rdi = regs[REG_RDI];
+  out->r8 = regs[REG_R8];
+  out->r9 = regs[REG_R9];
+  out->r10 = regs[REG_R10];
+  out->r11 = regs[REG_R11];
+  out->r12 = regs[REG_R12];
+  out->r13 = regs[REG_R13];
+  out->r14 = regs[REG_R14];
+  out->r15 = regs[REG_R15];
+
+  out->rip = regs[REG_RIP];
+
+  out->flt_save.control_word = fpregs->cwd;
+  out->flt_save.status_word = fpregs->swd;
+  out->flt_save.tag_word = fpregs->ftw;
+  out->flt_save.error_opcode = fpregs->fop;
+  out->flt_save.error_offset = fpregs->rip;
+  out->flt_save.data_offset = fpregs->rdp;
+  out->flt_save.error_selector = 0; // We don't have this.
+  out->flt_save.data_selector = 0;  // We don't have this.
+  out->flt_save.mx_csr = fpregs->mxcsr;
+  out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
+  memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
+  memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
+}
+
+#else
+#error "This code has not been ported to your platform yet."
+#endif
+
+namespace google_breakpad {
+
+class MinidumpWriter {
+ public:
+  MinidumpWriter(const char* filename,
+                 pid_t crashing_pid,
+                 const ExceptionHandler::CrashContext* context)
+      : filename_(filename),
+        siginfo_(&context->siginfo),
+        ucontext_(&context->context),
+        float_state_(&context->float_state),
+        crashing_tid_(context->tid),
+        dumper_(crashing_pid) {
+  }
+
+  bool Init() {
+    return dumper_.Init() && minidump_writer_.Open(filename_) &&
+           dumper_.ThreadsSuspend();
+  }
+
+  ~MinidumpWriter() {
+    minidump_writer_.Close();
+    dumper_.ThreadsResume();
+  }
+
+  bool Dump() {
+    // A minidump file contains a number of tagged streams. This is the number
+    // of stream which we write.
+    static const unsigned kNumWriters = 11;
+
+    TypedMDRVA<MDRawHeader> header(&minidump_writer_);
+    TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
+    if (!header.Allocate())
+      return false;
+    if (!dir.AllocateArray(kNumWriters))
+      return false;
+    memset(header.get(), 0, sizeof(MDRawHeader));
+
+    header.get()->signature = MD_HEADER_SIGNATURE;
+    header.get()->version = MD_HEADER_VERSION;
+    header.get()->time_date_stamp = time(NULL);
+    header.get()->stream_count = kNumWriters;
+    header.get()->stream_directory_rva = dir.position();
+
+    unsigned dir_index = 0;
+    MDRawDirectory dirent;
+
+    if (!WriteThreadListStream(&dirent))
+      return false;
+    dir.CopyIndex(dir_index++, &dirent);
+
+    if (!WriteMappings(&dirent))
+      return false;
+    dir.CopyIndex(dir_index++, &dirent);
+
+    if (!WriteExceptionStream(&dirent))
+      return false;
+    dir.CopyIndex(dir_index++, &dirent);
+
+    if (!WriteSystemInfoStream(&dirent))
+      return false;
+    dir.CopyIndex(dir_index++, &dirent);
+
+    dirent.stream_type = MD_LINUX_CPU_INFO;
+    if (!WriteFile(&dirent.location, "/proc/cpuinfo"))
+      NullifyDirectoryEntry(&dirent);
+    dir.CopyIndex(dir_index++, &dirent);
+
+    dirent.stream_type = MD_LINUX_PROC_STATUS;
+    if (!WriteProcFile(&dirent.location, crashing_tid_, "status"))
+      NullifyDirectoryEntry(&dirent);
+    dir.CopyIndex(dir_index++, &dirent);
+
+    dirent.stream_type = MD_LINUX_LSB_RELEASE;
+    if (!WriteFile(&dirent.location, "/etc/lsb-release"))
+      NullifyDirectoryEntry(&dirent);
+    dir.CopyIndex(dir_index++, &dirent);
+
+    dirent.stream_type = MD_LINUX_CMD_LINE;
+    if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline"))
+      NullifyDirectoryEntry(&dirent);
+    dir.CopyIndex(dir_index++, &dirent);
+
+    dirent.stream_type = MD_LINUX_ENVIRON;
+    if (!WriteProcFile(&dirent.location, crashing_tid_, "environ"))
+      NullifyDirectoryEntry(&dirent);
+    dir.CopyIndex(dir_index++, &dirent);
+
+    dirent.stream_type = MD_LINUX_AUXV;
+    if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv"))
+      NullifyDirectoryEntry(&dirent);
+    dir.CopyIndex(dir_index++, &dirent);
+
+    dirent.stream_type = MD_LINUX_AUXV;
+    if (!WriteProcFile(&dirent.location, crashing_tid_, "maps"))
+      NullifyDirectoryEntry(&dirent);
+    dir.CopyIndex(dir_index++, &dirent);
+
+    // If you add more directory entries, don't forget to update kNumWriters,
+    // above.
+
+    dumper_.ThreadsResume();
+    return true;
+  }
+
+  // Write information about the threads.
+  bool WriteThreadListStream(MDRawDirectory* dirent) {
+    const unsigned num_threads = dumper_.threads().size();
+
+    TypedMDRVA<uint32_t> list(&minidump_writer_);
+    if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread)))
+      return false;
+
+    dirent->stream_type = MD_THREAD_LIST_STREAM;
+    dirent->location = list.location();
+
+    *list.get() = num_threads;
+
+    for (unsigned i = 0; i < num_threads; ++i) {
+      MDRawThread thread;
+      my_memset(&thread, 0, sizeof(thread));
+      thread.thread_id = dumper_.threads()[i];
+      // We have a different source of information for the crashing thread. If
+      // we used the actual state of the thread we would find it running in the
+      // signal handler with the alternative stack, which would be deeply
+      // unhelpful.
+      if (thread.thread_id == crashing_tid_) {
+        const void* stack;
+        size_t stack_len;
+        if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer()))
+          return false;
+        UntypedMDRVA memory(&minidump_writer_);
+        if (!memory.Allocate(stack_len))
+          return false;
+        uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len);
+        dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len);
+        memory.Copy(stack_copy, stack_len);
+        thread.stack.start_of_memory_range = (uintptr_t) (stack);
+        thread.stack.memory = memory.location();
+        TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
+        if (!cpu.Allocate())
+          return false;
+        my_memset(cpu.get(), 0, sizeof(RawContextCPU));
+        CPUFillFromUContext(cpu.get(), ucontext_, float_state_);
+        thread.thread_context = cpu.location();
+        crashing_thread_context_ = cpu.location();
+      } else {
+        ThreadInfo info;
+        if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info))
+          return false;
+        UntypedMDRVA memory(&minidump_writer_);
+        if (!memory.Allocate(info.stack_len))
+          return false;
+        uint8_t* stack_copy =
+            (uint8_t*) dumper_.allocator()->Alloc(info.stack_len);
+        dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack,
+                                info.stack_len);
+        memory.Copy(stack_copy, info.stack_len);
+        thread.stack.start_of_memory_range = (uintptr_t)(info.stack);
+        thread.stack.memory = memory.location();
+        TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
+        if (!cpu.Allocate())
+          return false;
+        my_memset(cpu.get(), 0, sizeof(RawContextCPU));
+        CPUFillFromThreadInfo(cpu.get(), info);
+        thread.thread_context = cpu.location();
+      }
+
+      list.CopyIndexAfterObject(i, &thread, sizeof(thread));
+    }
+
+    return true;
+  }
+
+  static bool ShouldIncludeMapping(const MappingInfo& mapping) {
+    if (mapping.name[0] == 0 || // we only want modules with filenames.
+        mapping.offset || // we only want to include one mapping per shared lib.
+        mapping.size < 4096) {  // too small to get a signature for.
+      return false;
+    }
+
+    return true;
+  }
+
+  // Write information about the mappings in effect. Because we are using the
+  // minidump format, the information about the mappings is pretty limited.
+  // Because of this, we also include the full, unparsed, /proc/$x/maps file in
+  // another stream in the file.
+  bool WriteMappings(MDRawDirectory* dirent) {
+    const unsigned num_mappings = dumper_.mappings().size();
+    unsigned num_output_mappings = 0;
+
+    for (unsigned i = 0; i < dumper_.mappings().size(); ++i) {
+      const MappingInfo& mapping = *dumper_.mappings()[i];
+      if (ShouldIncludeMapping(mapping))
+        num_output_mappings++;
+    }
+
+    TypedMDRVA<uint32_t> list(&minidump_writer_);
+    if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE))
+      return false;
+
+    dirent->stream_type = MD_MODULE_LIST_STREAM;
+    dirent->location = list.location();
+    *list.get() = num_output_mappings;
+
+    for (unsigned i = 0, j = 0; i < num_mappings; ++i) {
+      const MappingInfo& mapping = *dumper_.mappings()[i];
+      if (!ShouldIncludeMapping(mapping))
+        continue;
+
+      MDRawModule mod;
+      my_memset(&mod, 0, MD_MODULE_SIZE);
+      mod.base_of_image = mapping.start_addr;
+      mod.size_of_image = mapping.size;
+      const size_t filepath_len = my_strlen(mapping.name);
+
+      // Figure out file name from path
+      const char* filename_ptr = mapping.name + filepath_len - 1;
+      while (filename_ptr >= mapping.name) {
+        if (*filename_ptr == '/')
+          break;
+        filename_ptr--;
+      }
+      filename_ptr++;
+      const size_t filename_len = mapping.name + filepath_len - filename_ptr;
+
+      uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
+      uint8_t* cv_ptr = cv_buf;
+      UntypedMDRVA cv(&minidump_writer_);
+      if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1))
+        return false;
+
+      const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
+      memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
+      cv_ptr += sizeof(cv_signature);
+
+      {
+        // We XOR the first page of the file to get a signature for it.
+        uint8_t xor_buf[sizeof(MDGUID)];
+        size_t done = 0;
+        uint8_t* signature = cv_ptr;
+        cv_ptr += sizeof(xor_buf);
+
+        my_memset(signature, 0, sizeof(xor_buf));
+        while (done < 4096) {
+          dumper_.CopyFromProcess(xor_buf, crashing_tid_,
+                                  (void *) (mod.base_of_image + done),
+                                  sizeof(xor_buf));
+          for (unsigned i = 0; i < sizeof(xor_buf); ++i)
+            signature[i] ^= xor_buf[i];
+          done += sizeof(xor_buf);
+        }
+        my_memset(cv_ptr, 0, sizeof(uint32_t));  // Set age to 0 on Linux.
+        cv_ptr += sizeof(uint32_t);
+      }
+
+      // Write pdb_file_name
+      memcpy(cv_ptr, filename_ptr, filename_len + 1);
+      cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1);
+
+      mod.cv_record = cv.location();
+
+      MDLocationDescriptor ld;
+      if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld))
+        return false;
+      mod.module_name_rva = ld.rva;
+
+      list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
+    }
+
+    return true;
+  }
+
+  bool WriteExceptionStream(MDRawDirectory* dirent) {
+    TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
+    if (!exc.Allocate())
+      return false;
+    my_memset(exc.get(), 0, sizeof(MDRawExceptionStream));
+
+    dirent->stream_type = MD_EXCEPTION_STREAM;
+    dirent->location = exc.location();
+
+    exc.get()->thread_id = crashing_tid_;
+    exc.get()->exception_record.exception_code = siginfo_->si_signo;
+    exc.get()->exception_record.exception_address =
+        (uintptr_t) siginfo_->si_addr;
+    exc.get()->thread_context = crashing_thread_context_;
+
+    return true;
+  }
+
+  bool WriteSystemInfoStream(MDRawDirectory* dirent) {
+    TypedMDRVA<MDRawSystemInfo> si(&minidump_writer_);
+    if (!si.Allocate())
+      return false;
+    my_memset(si.get(), 0, sizeof(MDRawSystemInfo));
+
+    dirent->stream_type = MD_SYSTEM_INFO_STREAM;
+    dirent->location = si.location();
+
+    WriteCPUInformation(si.get());
+    WriteOSInformation(si.get());
+
+    return true;
+  }
+
+ private:
+#if defined(__i386)
+  uintptr_t GetStackPointer() {
+    return ucontext_->uc_mcontext.gregs[REG_ESP];
+  }
+#elif defined(__x86_64)
+  uintptr_t GetStackPointer() {
+    return ucontext_->uc_mcontext.gregs[REG_RSP];
+  }
+#else
+#error "This code has not been ported to your platform yet."
+#endif
+
+  void NullifyDirectoryEntry(MDRawDirectory* dirent) {
+    dirent->stream_type = 0;
+    dirent->location.data_size = 0;
+    dirent->location.rva = 0;
+  }
+
+  bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
+    char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0};
+    static const char vendor_id_name[] = "vendor_id";
+    static const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1;
+
+    struct CpuInfoEntry {
+      const char* info_name;
+      int value;
+      bool found;
+    } cpu_info_table[] = {
+      { "processor", -1, false },
+      { "model", 0, false },
+      { "stepping",  0, false },
+      { "cpu family", 0, false },
+    };
+
+    // processor_architecture should always be set, do this first
+    sys_info->processor_architecture =
+#if defined(__i386)
+        MD_CPU_ARCHITECTURE_X86;
+#elif defined(__x86_64)
+        MD_CPU_ARCHITECTURE_AMD64;
+#else
+#error "Unknown CPU arch"
+#endif
+
+    const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0);
+    if (fd < 0)
+      return false;
+
+    {
+      PageAllocator allocator;
+      LineReader* const line_reader = new(allocator) LineReader(fd);
+      const char* line;
+      unsigned line_len;
+      while (line_reader->GetNextLine(&line, &line_len)) {
+        for (size_t i = 0;
+             i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
+             i++) {
+          CpuInfoEntry* entry = &cpu_info_table[i];
+          if (entry->found)
+            continue;
+          if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
+            const char* value = strchr(line, ':');
+            if (!value)
+              continue;
+
+            // the above strncmp only matches the prefix, it might be the wrong
+            // line. i.e. we matched "model name" instead of "model".
+            // check and make sure there is only spaces between the prefix and
+            // the colon.
+            const char* space_ptr = line + strlen(entry->info_name);
+            for (; space_ptr < value; space_ptr++) {
+              if (!isspace(*space_ptr)) {
+                break;
+              }
+            }
+            if (space_ptr != value)
+              continue;
+
+            sscanf(++value, " %d", &(entry->value));
+            entry->found = true;
+          }
+        }
+
+        // special case for vendor_id
+        if (!strncmp(line, vendor_id_name, vendor_id_name_length)) {
+          const char* value = strchr(line, ':');
+          if (!value)
+            goto popline;
+
+          // skip ':" and all the spaces that follows
+          do {
+            value++;
+          } while (isspace(*value));
+
+          if (*value) {
+            size_t length = strlen(value);
+            if (length == 0)
+              goto popline;
+            // we don't want the trailing newline
+            if (value[length - 1] == '\n')
+              length--;
+            // ensure we have space for the value
+            if (length < sizeof(vendor_id))
+              strncpy(vendor_id, value, length);
+          }
+        }
+
+popline:
+        line_reader->PopLine(line_len);
+      }
+      sys_close(fd);
+    }
+
+    // make sure we got everything we wanted
+    for (size_t i = 0;
+         i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
+         i++) {
+      if (!cpu_info_table[i].found) {
+        return false;
+      }
+    }
+    // /proc/cpuinfo contains cpu id, change it into number by adding one.
+    cpu_info_table[0].value++;
+
+    sys_info->number_of_processors = cpu_info_table[0].value;
+    sys_info->processor_level      = cpu_info_table[3].value;
+    sys_info->processor_revision   = cpu_info_table[1].value << 8 |
+                                     cpu_info_table[2].value;
+
+    if (vendor_id[0] != '\0') {
+      memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
+             sizeof(sys_info->cpu.x86_cpu_info.vendor_id));
+    }
+    return true;
+  }
+
+  bool WriteFile(MDLocationDescriptor* result, const char* filename) {
+    const int fd = sys_open(filename, O_RDONLY, 0);
+    if (fd < 0)
+      return false;
+
+    // We can't stat the files because several of the files that we want to
+    // read are kernel seqfiles, which always have a length of zero. So we have
+    // to read as much as we can into a buffer.
+    static const unsigned kMaxFileSize = 1024;
+    uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize);
+
+    size_t done = 0;
+    while (done < kMaxFileSize) {
+      ssize_t r;
+      do {
+        r = sys_read(fd, data + done, kMaxFileSize - done);
+      } while (r == -1 && errno == EINTR);
+
+      if (r < 1)
+        break;
+      done += r;
+    }
+    sys_close(fd);
+
+    if (!done)
+      return false;
+
+    UntypedMDRVA memory(&minidump_writer_);
+    if (!memory.Allocate(done))
+      return false;
+    memory.Copy(data, done);
+    *result = memory.location();
+    return true;
+  }
+
+  bool WriteOSInformation(MDRawSystemInfo* sys_info) {
+    sys_info->platform_id = MD_OS_LINUX;
+
+    struct utsname uts;
+    if (uname(&uts))
+      return false;
+
+    static const size_t buf_len = 512;
+    char buf[buf_len] = {0};
+    size_t space_left = buf_len - 1;
+    const char* info_table[] = {
+      uts.sysname,
+      uts.release,
+      uts.version,
+      uts.machine,
+      NULL
+    };
+    bool first_item = true;
+    for (const char** cur_info = info_table; *cur_info; cur_info++) {
+      static const char* separator = " ";
+      size_t separator_len = strlen(separator);
+      size_t info_len = strlen(*cur_info);
+      if (info_len == 0)
+        continue;
+
+      if (space_left < info_len + (first_item ? 0 : separator_len))
+        break;
+
+      if (!first_item) {
+        strcat(buf, separator);
+        space_left -= separator_len;
+      }
+
+      first_item = false;
+      strcat(buf, *cur_info);
+      space_left -= info_len;
+    }
+
+    MDLocationDescriptor location;
+    if (!minidump_writer_.WriteString(buf, 0, &location))
+      return false;
+    sys_info->csd_version_rva = location.rva;
+
+    return true;
+  }
+
+  bool WriteProcFile(MDLocationDescriptor* result, pid_t pid,
+                     const char* filename) {
+    char buf[80];
+    memcpy(buf, "/proc/", 6);
+    const unsigned pid_len = my_int_len(pid);
+    my_itos(buf + 6, pid, pid_len);
+    buf[6 + pid_len] = '/';
+    memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1);
+    return WriteFile(result, buf);
+  }
+
+  const char* const filename_;  // output filename
+  const siginfo_t* const siginfo_;  // from the signal handler (see sigaction)
+  const struct ucontext* const ucontext_;  // also from the signal handler
+  const struct _libc_fpstate* const float_state_;  // ditto
+  const pid_t crashing_tid_;  // the process which actually crashed
+  LinuxDumper dumper_;
+  MinidumpFileWriter minidump_writer_;
+  MDLocationDescriptor crashing_thread_context_;
+};
+
+bool WriteMinidump(const char* filename, pid_t crashing_process,
+                   const void* blob, size_t blob_size) {
+  if (blob_size != sizeof(ExceptionHandler::CrashContext))
+    return false;
+  const ExceptionHandler::CrashContext* context =
+      reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
+  MinidumpWriter writer(filename, crashing_process, context);
+  if (!writer.Init())
+    return false;
+  return writer.Dump();
+}
+
+}  // namespace google_breakpad
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
+
+#include <stdint.h>
+#include <unistd.h>
+
+namespace google_breakpad {
+
+// Write a minidump to the filesystem. This function does not malloc nor use
+// libc functions which may. Thus, it can be used in contexts where the state
+// of the heap may be corrupt.
+//   filename: the filename to write to. This is opened O_EXCL and fails if
+//     open fails.
+//   crashing_process: the pid of the crashing process. This must be trusted.
+//   blob: a blob of data from the crashing process. See exception_handler.h
+//   blob_size: the length of |blob|, in bytes
+//
+// Returns true iff successful.
+bool WriteMinidump(const char* filename, pid_t crashing_process,
+                   const void* blob, size_t blob_size);
+
+}  // namespace google_breakpad
+
+#endif  // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include "client/linux/handler/exception_handler.h"
+#include "client/linux/minidump_writer/minidump_writer.h"
+#include "breakpad_googletest_includes.h"
+
+using namespace google_breakpad;
+
+// This provides a wrapper around system calls which may be
+// interrupted by a signal and return EINTR. See man 7 signal.
+#define HANDLE_EINTR(x) ({ \
+  typeof(x) __eintr_result__; \
+  do { \
+    __eintr_result__ = x; \
+  } while (__eintr_result__ == -1 && errno == EINTR); \
+  __eintr_result__;\
+})
+
+namespace {
+typedef testing::Test MinidumpWriterTest;
+}
+
+TEST(MinidumpWriterTest, Setup) {
+  int fds[2];
+  ASSERT_NE(-1, pipe(fds));
+
+  const pid_t child = fork();
+  if (child == 0) {
+    close(fds[1]);
+    char b;
+    HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
+    close(fds[0]);
+    syscall(__NR_exit);
+  }
+  close(fds[0]);
+
+  ExceptionHandler::CrashContext context;
+  memset(&context, 0, sizeof(context));
+
+  char templ[] = "/tmp/minidump-writer-unittest-XXXXXX";
+  mktemp(templ);
+  ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context)));
+  struct stat st;
+  ASSERT_EQ(stat(templ, &st), 0);
+  ASSERT_GT(st.st_size, 0u);
+  unlink(templ);
+
+  close(fds[1]);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "common/linux/google_crashdump_uploader.h"
+#include "third_party/linux/include/glog/logging.h"
+#include "third_party/linux/include/gflags/gflags.h"
+#include <string>
+
+DEFINE_string(crash_server, "http://clients2.google.com/cr",
+              "The crash server to upload minidumps to.");
+DEFINE_string(product_name, "",
+              "The product name that the minidump corresponds to.");
+DEFINE_string(product_version, "",
+              "The version of the product that produced the minidump.");
+DEFINE_string(client_id, "",
+              "The client GUID");
+DEFINE_string(minidump_path, "",
+              "The path of the minidump file.");
+DEFINE_string(ptime, "",
+              "The process uptime in milliseconds.");
+DEFINE_string(ctime, "",
+              "The cumulative process uptime in milliseconds.");
+DEFINE_string(email, "",
+              "The user's email address.");
+DEFINE_string(comments, "",
+              "Extra user comments");
+DEFINE_string(proxy_host, "",
+              "Proxy host");
+DEFINE_string(proxy_userpasswd, "",
+              "Proxy username/password in user:pass format.");
+
+
+bool CheckForRequiredFlagsOrDie() {
+  std::string error_text = "";
+  if (FLAGS_product_name.empty()) {
+    error_text.append("\nProduct name must be specified.");
+  }
+
+  if (FLAGS_product_version.empty()) {
+    error_text.append("\nProduct version must be specified.");
+  }
+
+  if (FLAGS_client_id.empty()) {
+    error_text.append("\nClient ID must be specified.");
+  }
+
+  if (FLAGS_minidump_path.empty()) {
+    error_text.append("\nMinidump pathname must be specified.");
+  }
+
+  if (!error_text.empty()) {
+    LOG(ERROR) << error_text;
+    return false;
+  }
+  return true;
+}
+
+int main(int argc, char *argv[]) {
+  google::InitGoogleLogging(argv[0]);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+  if (!CheckForRequiredFlagsOrDie()) {
+    return 1;
+  }
+  google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name,
+                                             FLAGS_product_version,
+                                             FLAGS_client_id,
+                                             FLAGS_ptime,
+                                             FLAGS_ctime,
+                                             FLAGS_email,
+                                             FLAGS_comments,
+                                             FLAGS_minidump_path,
+                                             FLAGS_crash_server,
+                                             FLAGS_proxy_host,
+                                             FLAGS_proxy_userpasswd);
+  g.Upload();
+}
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj
@@ -14,16 +14,19 @@
 			);
 			dependencies = (
 				F94585880F78232B009A47BF /* PBXTargetDependency */,
 				F945858A0F78232E009A47BF /* PBXTargetDependency */,
 				F945858C0F782330009A47BF /* PBXTargetDependency */,
 				F945858E0F782333009A47BF /* PBXTargetDependency */,
 				F94585900F782336009A47BF /* PBXTargetDependency */,
 				F93DE3A70F830D1D00608B94 /* PBXTargetDependency */,
+				F95BB8B3101F94D300AA053B /* PBXTargetDependency */,
+				F95BB8B5101F94D300AA053B /* PBXTargetDependency */,
+				F95BB8B7101F94D300AA053B /* PBXTargetDependency */,
 			);
 			name = All;
 			productName = All;
 		};
 /* End PBXAggregateTarget section */
 
 /* Begin PBXBuildFile section */
 		3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.nib */; };
@@ -83,16 +86,17 @@
 		F93DE33A0F82C66B00608B94 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; };
 		F93DE33B0F82C66B00608B94 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; };
 		F93DE33C0F82C66B00608B94 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; };
 		F93DE33D0F82C66B00608B94 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; };
 		F93DE33E0F82C66B00608B94 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; };
 		F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; };
 		F93DE3410F82C68300608B94 /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE3400F82C68300608B94 /* exception_handler_test.cc */; };
 		F945849E0F280E3C009A47BF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F945849C0F280E3C009A47BF /* Localizable.strings */; };
+		F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */ = {isa = PBXBuildFile; fileRef = F9B6309F100FF96B00D0F4AC /* goArrow.png */; };
 		F9C44DB20EF07288003AEBAA /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DAC0EF07288003AEBAA /* Controller.m */; };
 		F9C44DB30EF07288003AEBAA /* crashduringload in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAD0EF07288003AEBAA /* crashduringload */; };
 		F9C44DB40EF07288003AEBAA /* crashInMain in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAE0EF07288003AEBAA /* crashInMain */; };
 		F9C44DB60EF07288003AEBAA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB00EF07288003AEBAA /* main.m */; };
 		F9C44DB70EF07288003AEBAA /* TestClass.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB10EF07288003AEBAA /* TestClass.mm */; };
 		F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */; };
 		F9C44DBD0EF072A0003AEBAA /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */; };
 		F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; };
@@ -186,16 +190,65 @@
 		};
 		F945858F0F782336009A47BF /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
 			proxyType = 1;
 			remoteGlobalIDString = F9C44DA40EF060A8003AEBAA;
 			remoteInfo = BreakpadTest;
 		};
+		F95BB884101F949F00AA053B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* crash_report */;
+			remoteInfo = crash_report;
+		};
+		F95BB891101F94AC00AA053B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* dump_syms */;
+			remoteInfo = dump_syms;
+		};
+		F95BB89E101F94C000AA053B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* symupload */;
+			remoteInfo = symupload;
+		};
+		F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 9BD835FB0B0544950055103E /* minidump_upload */;
+			remoteInfo = minidump_upload;
+		};
+		F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* dump_syms */;
+			remoteInfo = dump_syms;
+		};
+		F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* symupload */;
+			remoteInfo = symupload;
+		};
+		F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* crash_report */;
+			remoteInfo = crash_report;
+		};
 		F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
 			proxyType = 1;
 			remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
 			remoteInfo = Breakpad;
 		};
 		F9C44E960EF09F4B003AEBAA /* PBXContainerItemProxy */ = {
@@ -283,16 +336,20 @@
 		F93803BE0F80820F004D428B /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; };
 		F93803D90F8083D8004D428B /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator_test.cc; path = handler/minidump_generator_test.cc; sourceTree = "<group>"; };
 		F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidump_file_writer_unittest; sourceTree = BUILT_PRODUCTS_DIR; };
 		F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../minidump_file_writer_unittest.cc; sourceTree = SOURCE_ROOT; };
 		F93DE32C0F82C55600608B94 /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; };
 		F93DE3400F82C68300608B94 /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler_test.cc; path = handler/exception_handler_test.cc; sourceTree = "<group>"; };
 		F945849D0F280E3C009A47BF /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/Localizable.strings; sourceTree = "<group>"; };
 		F945859D0F78241E009A47BF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = "<group>"; };
+		F95BB87C101F949F00AA053B /* crash_report.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = crash_report.xcodeproj; path = ../../tools/mac/crash_report/crash_report.xcodeproj; sourceTree = SOURCE_ROOT; };
+		F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = dump_syms.xcodeproj; path = ../../tools/mac/dump_syms/dump_syms.xcodeproj; sourceTree = SOURCE_ROOT; };
+		F95BB894101F94C000AA053B /* symupload.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = symupload.xcodeproj; path = ../../tools/mac/symupload/symupload.xcodeproj; sourceTree = SOURCE_ROOT; };
+		F9B6309F100FF96B00D0F4AC /* goArrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = goArrow.png; path = sender/goArrow.png; sourceTree = "<group>"; };
 		F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BreakpadTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		F9C44DAC0EF07288003AEBAA /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Controller.m; path = testapp/Controller.m; sourceTree = "<group>"; };
 		F9C44DAD0EF07288003AEBAA /* crashduringload */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashduringload; path = testapp/crashduringload; sourceTree = "<group>"; };
 		F9C44DAE0EF07288003AEBAA /* crashInMain */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashInMain; path = testapp/crashInMain; sourceTree = "<group>"; };
 		F9C44DAF0EF07288003AEBAA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = "<group>"; };
 		F9C44DB00EF07288003AEBAA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = testapp/main.m; sourceTree = "<group>"; };
 		F9C44DB10EF07288003AEBAA /* TestClass.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestClass.mm; path = testapp/TestClass.mm; sourceTree = "<group>"; };
 		F9C44DB90EF072A0003AEBAA /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = testapp/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -399,16 +456,17 @@
 				F93DE32C0F82C55600608B94 /* handler_test */,
 			);
 			name = Products;
 			sourceTree = "<group>";
 		};
 		0867D691FE84028FC02AAC07 /* Breakpad */ = {
 			isa = PBXGroup;
 			children = (
+				F95BB8A3101F94C300AA053B /* Tools */,
 				32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */,
 				F92C538D0ECCE6F2009BE4BA /* client */,
 				F92C53600ECCE3D6009BE4BA /* common */,
 				0867D69AFE84028FC02AAC07 /* Frameworks */,
 				034768DFFF38A50411DB9C8B /* Products */,
 				F9C77DDB0F7DD5CF0045F7DB /* UnitTests-Info.plist */,
 			);
 			name = Breakpad;
@@ -530,27 +588,63 @@
 				F92C55CF0ECD0064009BE4BA /* Breakpad.mm */,
 			);
 			name = Framework;
 			sourceTree = "<group>";
 		};
 		F92C56A60ECE04B6009BE4BA /* sender */ = {
 			isa = PBXGroup;
 			children = (
+				F9B6309F100FF96B00D0F4AC /* goArrow.png */,
 				F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */,
 				F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */,
 				F945849C0F280E3C009A47BF /* Localizable.strings */,
 				33880C7E0F9E097100817F82 /* InfoPlist.strings */,
 				3329D4EC0FA16D820007BBC5 /* Breakpad.nib */,
 				4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */,
 				F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */,
 			);
 			name = sender;
 			sourceTree = "<group>";
 		};
+		F95BB87D101F949F00AA053B /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				F95BB885101F949F00AA053B /* crash_report */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		F95BB88A101F94AC00AA053B /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				F95BB892101F94AC00AA053B /* dump_syms */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		F95BB895101F94C000AA053B /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				F95BB89F101F94C000AA053B /* symupload */,
+				F95BB8A1101F94C000AA053B /* minidump_upload */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		F95BB8A3101F94C300AA053B /* Tools */ = {
+			isa = PBXGroup;
+			children = (
+				F95BB894101F94C000AA053B /* symupload.xcodeproj */,
+				F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */,
+				F95BB87C101F949F00AA053B /* crash_report.xcodeproj */,
+			);
+			name = Tools;
+			sourceTree = "<group>";
+		};
 		F9C44DAB0EF0726F003AEBAA /* testapp */ = {
 			isa = PBXGroup;
 			children = (
 				F9C44DBF0EF0778F003AEBAA /* Controller.h */,
 				F9C44DC00EF0778F003AEBAA /* TestClass.h */,
 				F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */,
 				F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */,
 				F9C44DAC0EF07288003AEBAA /* Controller.m */,
@@ -773,32 +867,77 @@
 		0867D690FE84028FC02AAC07 /* Project object */ = {
 			isa = PBXProject;
 			buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */;
 			compatibilityVersion = "Xcode 2.4";
 			hasScannedForEncodings = 1;
 			mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */;
 			productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
 			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = F95BB87D101F949F00AA053B /* Products */;
+					ProjectRef = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */;
+				},
+				{
+					ProductGroup = F95BB88A101F94AC00AA053B /* Products */;
+					ProjectRef = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */;
+				},
+				{
+					ProductGroup = F95BB895101F94C000AA053B /* Products */;
+					ProjectRef = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
+				},
+			);
 			projectRoot = "";
 			targets = (
 				8DC2EF4F0486A6940098B216 /* Breakpad */,
 				F92C53530ECCE349009BE4BA /* Inspector */,
 				F92C563B0ECD10B3009BE4BA /* breakpadUtilities */,
 				F92C569F0ECE04A7009BE4BA /* crash_report_sender */,
 				F9C44DA40EF060A8003AEBAA /* BreakpadTest */,
 				F94585840F782326009A47BF /* All */,
 				F9C77DD90F7DD5CF0045F7DB /* UnitTests */,
 				F93803BD0F80820F004D428B /* generator_test */,
 				F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */,
 				F93DE32B0F82C55600608B94 /* handler_test */,
 			);
 		};
 /* End PBXProject section */
 
+/* Begin PBXReferenceProxy section */
+		F95BB885101F949F00AA053B /* crash_report */ = {
+			isa = PBXReferenceProxy;
+			fileType = "compiled.mach-o.executable";
+			path = crash_report;
+			remoteRef = F95BB884101F949F00AA053B /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		F95BB892101F94AC00AA053B /* dump_syms */ = {
+			isa = PBXReferenceProxy;
+			fileType = "compiled.mach-o.executable";
+			path = dump_syms;
+			remoteRef = F95BB891101F94AC00AA053B /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		F95BB89F101F94C000AA053B /* symupload */ = {
+			isa = PBXReferenceProxy;
+			fileType = "compiled.mach-o.executable";
+			path = symupload;
+			remoteRef = F95BB89E101F94C000AA053B /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		F95BB8A1101F94C000AA053B /* minidump_upload */ = {
+			isa = PBXReferenceProxy;
+			fileType = "compiled.mach-o.executable";
+			path = minidump_upload;
+			remoteRef = F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
 /* Begin PBXResourcesBuildPhase section */
 		8DC2EF520486A6940098B216 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				F9C44E980EF09F56003AEBAA /* crash_report_sender.app in Resources */,
 				F92C568A0ECD15F9009BE4BA /* Inspector in Resources */,
 				F92C56650ECD1185009BE4BA /* breakpadUtilities.dylib in Resources */,
@@ -808,16 +947,17 @@
 		F92C569C0ECE04A7009BE4BA /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				F945849E0F280E3C009A47BF /* Localizable.strings in Resources */,
 				4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */,
 				33880C800F9E097100817F82 /* InfoPlist.strings in Resources */,
 				3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */,
+				F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		F9C44DA10EF060A8003AEBAA /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				F9C44DB30EF07288003AEBAA /* crashduringload in Resources */,
@@ -1058,16 +1198,31 @@
 			target = F92C569F0ECE04A7009BE4BA /* crash_report_sender */;
 			targetProxy = F945858D0F782333009A47BF /* PBXContainerItemProxy */;
 		};
 		F94585900F782336009A47BF /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = F9C44DA40EF060A8003AEBAA /* BreakpadTest */;
 			targetProxy = F945858F0F782336009A47BF /* PBXContainerItemProxy */;
 		};
+		F95BB8B3101F94D300AA053B /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = dump_syms;
+			targetProxy = F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */;
+		};
+		F95BB8B5101F94D300AA053B /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = symupload;
+			targetProxy = F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */;
+		};
+		F95BB8B7101F94D300AA053B /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = crash_report;
+			targetProxy = F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */;
+		};
 		F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = 8DC2EF4F0486A6940098B216 /* Breakpad */;
 			targetProxy = F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */;
 		};
 		F9C44E970EF09F4B003AEBAA /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = F92C569F0ECE04A7009BE4BA /* crash_report_sender */;
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h
@@ -84,16 +84,17 @@ extern "C" {
 #define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters"
 
 // The keys below are NOT user supplied, and are used internally.
 #define BREAKPAD_PROCESS_START_TIME       "BreakpadProcStartTime"
 #define BREAKPAD_PROCESS_UP_TIME          "BreakpadProcessUpTime"
 #define BREAKPAD_PROCESS_CRASH_TIME       "BreakpadProcessCrashTime"
 #define BREAKPAD_LOGFILE_KEY_PREFIX       "BreakpadAppLogFile"
 #define BREAKPAD_SERVER_PARAMETER_PREFIX  "BreakpadServerParameterPrefix_"
+#define BREAKPAD_ON_DEMAND                "BreakpadOnDemand"
 
 // Optional user-defined function to dec to decide if we should handle
 // this crash or forward it along.
 // Return true if you want Breakpad to handle it.
 // Return false if you want Breakpad to skip it
 // The exception handler always returns false, as if SEND_AND_EXIT were false
 // (which means the next exception handler will take the exception)
 typedef bool (*BreakpadFilterCallback)(int exception_type,
@@ -210,17 +211,17 @@ typedef bool (*BreakpadFilterCallback)(i
 // It's easiest to use Breakpad via the Framework, but if you're compiling the
 // code in directly, BREAKPAD_INSPECTOR_LOCATION and
 // BREAKPAD_REPORTER_EXE_LOCATION allow you to specify custom paths
 // to the helper executables.
 //
 //=============================================================================
 // The following are NOT user-supplied but are documented here for
 // completeness.  They are calculated by Breakpad during initialization &
-// crash-dump generation.
+// crash-dump generation, or entered in by the user.
 //
 // BREAKPAD_PROCESS_START_TIME       The time the process started.
 //
 // BREAKPAD_PROCESS_CRASH_TIME       The time the process crashed.
 //
 // BREAKPAD_PROCESS_UP_TIME          The total time the process has been
 //                                   running.  This parameter is not set
 //                                   until the crash-dump-generation phase.
@@ -237,16 +238,22 @@ typedef bool (*BreakpadFilterCallback)(i
 //                                   parameters meant to be uploaded
 //                                   to the server.  This string is
 //                                   used internally by Breakpad to
 //                                   prefix user-supplied parameter
 //                                   names so those can be sent to the
 //                                   server without leaking Breakpad's
 //                                   internal values.
 //
+// BREAKPAD_ON_DEMAND                Used internally to indicate to the
+//                                   Reporter that we're sending on-demand,
+//                                   not as result of a crash.
+//
+// BREAKPAD_COMMENTS                 The text the user provided as comments.
+//                                   Only used in crash_report_sender.
 
 // Returns a new BreakpadRef object on success, NULL otherwise.
 BreakpadRef BreakpadCreate(NSDictionary *parameters);
 
 // Uninstall and release the data associated with |ref|.
 void BreakpadRelease(BreakpadRef ref);
 
 // Clients may set an optional callback which gets called when a crash
@@ -281,17 +288,17 @@ void BreakpadSetKeyValue(BreakpadRef ref
 NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key);
 void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key);
 
 // You can use this method to specify parameters that will be uploaded
 // to the crash server.  They will be automatically encoded as
 // necessary.  Note that as mentioned above there are limits on both
 // the number of keys and their length.
 void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key,
-				NSString *value);
+                                NSString *value);
 
 // This method will remove a previously-added parameter from the
 // upload parameter set.
 void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString *key);
 
 // Add a log file for Breakpad to read and send upon crash dump
 void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname);
 
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm
@@ -404,17 +404,16 @@ bool Breakpad::ExtractParameters(NSDicti
   NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL];
   NSString *inspectorPathString =
                 [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
   NSString *reporterPathString =
                 [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
   NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT];
   NSArray  *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
   NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
-  NSString *reportEmail = [parameters objectForKey:@BREAKPAD_EMAIL];
   NSString *requestUserText =
                 [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
   NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
   NSString *vendor =
     [parameters objectForKey:@BREAKPAD_VENDOR];
   NSString *dumpSubdirectory =
     [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
 
@@ -446,17 +445,17 @@ bool Breakpad::ExtractParameters(NSDicti
 
   if (!logFileTailSize)
     logFileTailSize = @"200000";
 
   if (!vendor) {
     vendor = @"Vendor not specified";
   }
 
-  // Normalize the values
+  // Normalize the values.
   if (skipConfirm) {
     skipConfirm = [skipConfirm uppercaseString];
 
     if ([skipConfirm isEqualToString:@"YES"] ||
         [skipConfirm isEqualToString:@"TRUE"] ||
         [skipConfirm isEqualToString:@"1"])
       skipConfirm = @"YES";
     else
@@ -499,30 +498,30 @@ bool Breakpad::ExtractParameters(NSDicti
   }
 
   // Find Inspector.
   if (!inspectorPathString) {
     inspectorPathString =
         [resourcePath stringByAppendingPathComponent:@"Inspector"];
   }
 
-  // Verify that there is an Inspector tool
+  // Verify that there is an Inspector tool.
   if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
     DEBUGLOG(stderr, "Cannot find Inspector tool\n");
     return false;
   }
 
   // Find Reporter.
   if (!reporterPathString) {
     reporterPathString =
       [resourcePath stringByAppendingPathComponent:@"crash_report_sender.app"];
     reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath];
   }
 
-  // Verify that there is a Reporter application
+  // Verify that there is a Reporter application.
   if (![[NSFileManager defaultManager]
              fileExistsAtPath:reporterPathString]) {
     DEBUGLOG(stderr, "Cannot find Reporter tool\n");
     return false;
   }
 
   if (!dumpSubdirectory) {
     dumpSubdirectory = @"";
@@ -583,21 +582,16 @@ bool Breakpad::ExtractParameters(NSDicti
     for(unsigned int i = 0; i < [logFilePaths count]; i++) {
       sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i);
       dictionary.SetKeyValue(logFileKey,
                              [[logFilePaths objectAtIndex:i]
                                fileSystemRepresentation]);
     }
   }
 
-  if (reportEmail) {
-    dictionary.SetKeyValue(BREAKPAD_EMAIL,
-                           [reportEmail UTF8String]);
-  }
-
   if (serverParameters) {
     // For each key-value pair, call BreakpadAddUploadParameter()
     NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
     NSString *aParameter;
     while (aParameter = [keyEnumerator nextObject]) {
       BreakpadAddUploadParameter(this, aParameter,
 				 [serverParameters objectForKey:aParameter]);
     }
@@ -628,17 +622,19 @@ void        Breakpad::RemoveKeyValue(NSS
   if (!config_params_ || !key)
     return;
 
   config_params_->RemoveKey([key UTF8String]);
 }
 
 //=============================================================================
 void        Breakpad::GenerateAndSendReport() {
+  config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES");
   HandleException(0, 0, 0, mach_thread_self()); 
+  config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO");
 }
 
 //=============================================================================
 bool Breakpad::HandleException(int           exception_type,
                                int           exception_code,
                                int           exception_subcode,
                                mach_port_t   crashing_thread) {
   DEBUGLOG(stderr, "Breakpad: an exception occurred\n");
@@ -746,74 +742,68 @@ BreakpadRef BreakpadCreate(NSDictionary 
       new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
 
     gKeyValueAllocator =
       new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
         ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
 
     // Create a mutex for use in accessing the SimpleStringDictionary
     int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
-    if (mutexResult != 0) {
-      throw mutexResult;   // caught down below
-    }
+    if (mutexResult == 0) {
 
-    // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
-    // Let's round up to the nearest page size.
-    //
-    int breakpad_pool_size = 4096;
+      // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
+      // Let's round up to the nearest page size.
+      //
+      int breakpad_pool_size = 4096;
 
-    /*
-     sizeof(Breakpad)
-     + sizeof(google_breakpad::ExceptionHandler)
-     + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
-     */
+      /*
+       sizeof(Breakpad)
+       + sizeof(google_breakpad::ExceptionHandler)
+       + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
+       */
 
-    gBreakpadAllocator =
-      new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
-        ProtectedMemoryAllocator(breakpad_pool_size);
+      gBreakpadAllocator =
+        new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
+          ProtectedMemoryAllocator(breakpad_pool_size);
 
-    // Stack-based autorelease pool for Breakpad::Create() obj-c code.
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    Breakpad *breakpad = Breakpad::Create(parameters);
+      // Stack-based autorelease pool for Breakpad::Create() obj-c code.
+      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+      Breakpad *breakpad = Breakpad::Create(parameters);
 
-    if (breakpad) {
-      // Make read-only to protect against memory smashers
-      gMasterAllocator->Protect();
-      gKeyValueAllocator->Protect();
-      gBreakpadAllocator->Protect();
-    } else {
-      [pool release];
-#ifdef __EXCEPTIONS
-      throw(-1);
-#else
-      return NULL;
-#endif
-    }
+      if (breakpad) {
+        // Make read-only to protect against memory smashers
+        gMasterAllocator->Protect();
+        gKeyValueAllocator->Protect();
+        gBreakpadAllocator->Protect();
+        // Can uncomment this line to figure out how much space was actually
+        // allocated using this allocator
+        //     printf("gBreakpadAllocator allocated size = %d\n",
+        //         gBreakpadAllocator->GetAllocatedSize() );
+        [pool release];
+        return (BreakpadRef)breakpad;
+      }
 
-    // Can uncomment this line to figure out how much space was actually
-    // allocated using this allocator
-    //     printf("gBreakpadAllocator allocated size = %d\n",
-    //         gBreakpadAllocator->GetAllocatedSize() );
-
-    [pool release];
-    return (BreakpadRef)breakpad;
+      [pool release];
+    }
   } catch(...) {    // don't let exceptions leave this C API
-    if (gKeyValueAllocator) {
-      gKeyValueAllocator->~ProtectedMemoryAllocator();
-      gKeyValueAllocator = NULL;
-    }
+    fprintf(stderr, "BreakpadCreate() : error\n");
+  }
+
+  if (gKeyValueAllocator) {
+    gKeyValueAllocator->~ProtectedMemoryAllocator();
+    gKeyValueAllocator = NULL;
+  }
 
-    if (gBreakpadAllocator) {
-      gBreakpadAllocator->~ProtectedMemoryAllocator();
-      gBreakpadAllocator = NULL;
-    }
+  if (gBreakpadAllocator) {
+    gBreakpadAllocator->~ProtectedMemoryAllocator();
+    gBreakpadAllocator = NULL;
+  }
 
-    delete gMasterAllocator;
-    gMasterAllocator = NULL;
-  }
+  delete gMasterAllocator;
+  gMasterAllocator = NULL;
 
   return NULL;
 }
 
 //=============================================================================
 void BreakpadRelease(BreakpadRef ref) {
   try {
     Breakpad *breakpad = (Breakpad *)ref;
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm
@@ -314,33 +314,52 @@ kern_return_t Inspector::ReadMessages() 
     printf("exception_subcode = %d\n", exception_subcode_);
     printf("remote_task = %d\n", remote_task_);
     printf("crashing_thread = %d\n", crashing_thread_);
     printf("handler_thread = %d\n", handler_thread_);
     printf("ack_port_ = %d\n", ack_port_);
     printf("parameter count = %d\n", info.parameter_count);
 #endif
 
+    // In certain situations where multiple crash requests come
+    // through quickly, we can end up with the mach IPC messages not
+    // coming through correctly.  Since we don't know what parameters
+    // we've missed, we can't do much besides abort the crash dump
+    // situation in this case.
+    unsigned int parameters_read = 0;
     // The initial message contains the number of key value pairs that
     // we are expected to read.
     // Read each key/value pair, one mach message per key/value pair.
     for (unsigned int i = 0; i < info.parameter_count; ++i) {
       MachReceiveMessage message;
       result = receive_port.WaitForMessage(&message, 1000);
 
       if(result == KERN_SUCCESS) {
         KeyValueMessageData &key_value_data =
           (KeyValueMessageData&)*message.GetData();
+        // If we get a blank key, make sure we don't increment the
+        // parameter count; in some cases (notably on-demand generation
+        // many times in a short period of time) caused the Mach IPC
+        // messages to not come through correctly.
+        if (strlen(key_value_data.key) == 0) {
+          continue;
+        }
+        parameters_read++;
 
         config_params_.SetKeyValue(key_value_data.key, key_value_data.value);
       } else {
         PRINT_MACH_RESULT(result, "Inspector: key/value message");
         break;
       }
     }
+    if (parameters_read != info.parameter_count) {
+      DEBUGLOG(stderr, "Only read %d parameters instead of %d, aborting crash "
+               "dump generation.", parameters_read, info.parameter_count);
+      return KERN_FAILURE;
+    }
   }
 
   return result;
 }
 
 //=============================================================================
 // Sets keys in the parameters dictionary that are specific to process uptime.
 // The two we set are process up time, and process crash time.
@@ -374,17 +393,17 @@ bool Inspector::InspectTask() {
   NSString *minidumpDir;
 
   const char *minidumpDirectory =
     config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
 
   SetCrashTimeParameters();
   // If the client app has not specified a minidump directory,
   // use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name>
-  if (0 == strlen(minidumpDirectory)) {
+  if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
     NSArray *libraryDirectories =
       NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
                                           NSUserDomainMask,
                                           YES);
 
     NSString *applicationSupportDirectory =
         [libraryDirectories objectAtIndex:0];
     NSString *library_subdirectory = [NSString 
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
@@ -87,22 +87,25 @@ exception_mask_t s_exception_mask = EXC_
 EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
 
 extern "C"
 {
   // Forward declarations for functions that need "C" style compilation
   boolean_t exc_server(mach_msg_header_t *request,
                        mach_msg_header_t *reply);
 
+  // This symbol must be visible to dlsym() - see
+  // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
   kern_return_t catch_exception_raise(mach_port_t target_port,
                                       mach_port_t failed_thread,
                                       mach_port_t task,
                                       exception_type_t exception,
                                       exception_data_t code,
-                                      mach_msg_type_number_t code_count);
+                                      mach_msg_type_number_t code_count)
+      __attribute__((visibility("default")));
 
   kern_return_t ForwardException(mach_port_t task,
                                  mach_port_t failed_thread,
                                  exception_type_t exception,
                                  exception_data_t code,
                                  mach_msg_type_number_t code_count);
 
   kern_return_t exception_raise(mach_port_t target_port,
@@ -430,16 +433,19 @@ kern_return_t ForwardException(mach_port
 }
 
 // Callback from exc_server()
 kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
                                     mach_port_t task,
                                     exception_type_t exception,
                                     exception_data_t code,
                                     mach_msg_type_number_t code_count) {
+  if (task != mach_task_self()) {
+    return KERN_FAILURE;
+  }
   return ForwardException(task, failed_thread, exception, code, code_count);
 }
 
 // static
 void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
   ExceptionHandler *self =
     reinterpret_cast<ExceptionHandler *>(exception_handler_class);
   ExceptionMessage receive;
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc
@@ -632,23 +632,38 @@ bool MinidumpGenerator::WriteSystemInfoS
       int unused, unused2;
       // get vendor id
       cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0],
             info_ptr->cpu.x86_cpu_info.vendor_id[2],
             info_ptr->cpu.x86_cpu_info.vendor_id[1]);
       // get version and feature info
       cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2,
             info_ptr->cpu.x86_cpu_info.feature_information);
+
       // family
       info_ptr->processor_level =
         (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8;
       // 0xMMSS (Model, Stepping)
       info_ptr->processor_revision =
         (info_ptr->cpu.x86_cpu_info.version_information & 0xF) |
         ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4);
+
+      // decode extended model info
+      if (info_ptr->processor_level == 0xF ||
+          info_ptr->processor_level == 0x6) {
+        info_ptr->processor_revision |=
+          ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4);
+      }
+
+      // decode extended family info
+      if (info_ptr->processor_level == 0xF) {
+        info_ptr->processor_level +=
+          ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20);
+      }
+
 #endif // __i386__
       break;
     default:
       info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
       break;
   }
 
   info_ptr->number_of_processors = number_of_processors;
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h
@@ -63,17 +63,17 @@ typedef ppc_thread_state64_t breakpad_th
 typedef MDRawContextPPC64 MinidumpContext;
 #elif TARGET_CPU_PPC
 typedef ppc_thread_state_t breakpad_thread_state_t;
 typedef MDRawContextPPC MinidumpContext;
 #endif
 
 // Use the REGISTER_FROM_THREADSTATE to access a register name from the
 // breakpad_thread_state_t structure.
-#if __DARWIN_UNIX03 || !TARGET_CPU_X86 || TARGET_CPU_X86_64
+#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64
 // In The 10.5 SDK Headers Apple prepended __ to the variable names in the
 // i386_thread_state_t structure.  There's no good way to tell what version of
 // the SDK we're compiling against so we just toggle on the same preprocessor
 // symbol Apple's headers use.
 #define REGISTER_FROM_THREADSTATE(a, b) ((a)->__ ## b)
 #else
 #define REGISTER_FROM_THREADSTATE(a, b) (a->b)
 #endif
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib
@@ -1,15 +1,23 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
 	<key>IBClasses</key>
 	<array>
 		<dict>
+			<key>CLASS</key>
+			<string>LengthLimitingTextField</string>
+			<key>LANGUAGE</key>
+			<string>ObjC</string>
+			<key>SUPERCLASS</key>
+			<string>NSTextField</string>
+		</dict>
+		<dict>
 			<key>ACTIONS</key>
 			<dict>
 				<key>cancel</key>
 				<string>id</string>
 				<key>sendReport</key>
 				<string>id</string>
 				<key>showPrivacyPolicy</key>
 				<string>id</string>
@@ -21,20 +29,24 @@
 			<key>OUTLETS</key>
 			<dict>
 				<key>alertWindow_</key>
 				<string>NSWindow</string>
 				<key>cancelButton_</key>
 				<string>NSButton</string>
 				<key>commentMessage_</key>
 				<string>NSTextField</string>
+				<key>commentsEntryField_</key>
+				<string>LengthLimitingTextField</string>
+				<key>countdownLabel_</key>
+				<string>NSTextField</string>
 				<key>dialogTitle_</key>
 				<string>NSTextField</string>
 				<key>emailEntryField_</key>
-				<string>NSView</string>
+				<string>LengthLimitingTextField</string>
 				<key>emailLabel_</key>
 				<string>NSTextField</string>
 				<key>emailMessage_</key>
 				<string>NSTextField</string>
 				<key>emailSectionBox_</key>
 				<string>NSBox</string>
 				<key>headerBox_</key>
 				<string>NSBox</string>
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
 	<key>IBFramework Version</key>
-	<string>672</string>
+	<string>676</string>
 	<key>IBLastKnownRelativeProjectPath</key>
 	<string>../Breakpad.xcodeproj</string>
 	<key>IBOldestOS</key>
 	<integer>5</integer>
 	<key>IBOpenObjects</key>
 	<array>
-		<integer>2</integer>
+		<integer>132</integer>
 	</array>
 	<key>IBSystem Version</key>
-	<string>9G55</string>
+	<string>9J61</string>
 	<key>targetFramework</key>
 	<string>IBCocoaFramework</string>
 </dict>
 </plist>
index d0cbd3f24e2a9e3bb76f28d9c4aadec5398cdac7..e370206c166426f1ec3ff2a9578fe891ac3a5190
GIT binary patch
literal 14674
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
index efc832acfbb8b635940dedec2d123ae8975e009f..70626567c95f166a7ac0bf7d3b89d695264e4b1b
GIT binary patch
literal 2270
zc%0=`%Wl&^6uoP{Vib{07mi>FLZTETBwDBl{eY?M#F6|WV>jgIfiq{u_L$hIRCR|e
zJM*}YbI!dp`SW`v7oub>jqGJ7rEJAuzL5%ZC3l#m9DT(~1B(sbSTx5K>=nKnd^O$}
zmKq)z#uu`Z7qSS}KEgvw4*RWqMpP{cV=rL+4(~#Kz$#^|8c%+bX({jV%@|L~Pie8!
zVANtam0$2l;nCom!22i>V!l0t;sz_*iTnn>hH(m_DG^VRf9BPgMrb^`2SzFBq2wO3
zl=wB2+5{LQtX<;WV^&k~Cg3;t#kA1gOA&{r?GaurEi^3+J)BL|E9S-)dyYH`XcOAG
zdLDg5i@8Ro3Yl|q@AR_@S~geDUQ3H2CWn&j_MV|*W9NJ7GE{ON$Nx~xSMqYEqJ^)W
zL%WpMv<xd~buRF@%9|6tZdrBBtYPfd-v~RLu(?jL{A?fDE-+-c!-~64lpK17nb$hy
ziA}sN?zTyT=)-v(DsS^>#LOp<JLa~pyPQtTJ-ev$Q|!}(xeQqyxbAjx_R*nd>@<$q
z3k~+;QH^%KmYrS)-zBN%RrH*!CXGj6Mim*5UE}wjiVv(l^}BC+4QeX;Ks3imuiKG@
z!o2dGeGD>po%KEF)eqp+kaZcvTlYN}+79=s<_WeKrv0x1i|`D0ai_h$$(I?<A)h;7
zx(>WDMyjFL3eSO0gl?#KKiE!<{3%%d6SWSaMvPPRc)PRX?x=)4Qu-cT^ZZw2Vt9gD
zc5YaaCDk({OLt4S`j)<D!+)#Cv#q^Xi(Gni^Pc8Am+f4>8``$Ksm`-2MwU-w7iN2m
JU6^k^+h4Dxg<Svu
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h
@@ -36,45 +36,71 @@
 
 #include "client/mac/Framework/Breakpad.h"
 
 #define kClientIdPreferenceKey @"clientid"
 
 extern NSString *const kGoogleServerType;
 extern NSString *const kSocorroServerType;
 extern NSString *const kDefaultServerType;
+
+// We're sublcassing NSTextField in order to override a particular
+// method (see the implementation) that lets us reject changes if they
+// are longer than a particular length.  Bindings would normally solve
+// this problem, but when we implemented a validation method, and
+// returned NO for strings that were too long, the UI was not updated
+// right away, which was a poor user experience.  The UI would be
+// updated as soon as the text field lost first responder status,
+// which isn't soon enough.  It is a known bug that the UI KVO didn't
+// work in the middle of a validation.
+@interface LengthLimitingTextField : NSTextField {
+  @private
+   unsigned int maximumLength_;
+}
+
+- (void) setMaximumLength:(unsigned int)maxLength;
+@end
+
 @interface Reporter : NSObject {
  @public
   IBOutlet NSWindow *alertWindow_;        // The alert window
 
   // Grouping boxes used for resizing.
   IBOutlet NSBox *headerBox_;
   IBOutlet NSBox *preEmailBox_;
   IBOutlet NSBox *emailSectionBox_;
   // Localized elements (or things that need to be moved during localization).
-  IBOutlet NSTextField *dialogTitle_;
-  IBOutlet NSTextField *commentMessage_;
-  IBOutlet NSTextField *emailMessage_;
-  IBOutlet NSTextField *emailLabel_;
-  IBOutlet NSTextField *privacyLinkLabel_;
-  IBOutlet NSButton    *sendButton_;
-  IBOutlet NSButton    *cancelButton_;
-  IBOutlet NSView      *emailEntryField_;
-  IBOutlet NSView      *privacyLinkArrow_;
+  IBOutlet NSTextField                *dialogTitle_;
+  IBOutlet NSTextField                *commentMessage_;
+  IBOutlet NSTextField                *emailMessage_;
+  IBOutlet NSTextField                *emailLabel_;
+  IBOutlet NSTextField                *privacyLinkLabel_;
+  IBOutlet NSButton                   *sendButton_;
+  IBOutlet NSButton                   *cancelButton_;
+  IBOutlet LengthLimitingTextField    *emailEntryField_;
+  IBOutlet LengthLimitingTextField    *commentsEntryField_;
+  IBOutlet NSTextField                *countdownLabel_;
+  IBOutlet NSView                     *privacyLinkArrow_;
 
   // Text field bindings, for user input.
   NSString *commentsValue_;                // Comments from the user
   NSString *emailValue_;                   // Email from the user
-
+  NSString *countdownMessage_;             // Message indicating time
+                                           // left for input.
  @private
   int configFile_;                         // File descriptor for config file
   NSMutableDictionary *parameters_;        // Key value pairs of data (STRONG)
   NSData *minidumpContents_;               // The data in the minidump (STRONG)
   NSData *logFileData_;                    // An NSdata for the tar,
                                            // bz2'd log file.
+  NSTimeInterval remainingDialogTime_;     // Keeps track of how long
+                                           // we have until we cancel
+                                           // the dialog
+  NSTimer *messageTimer_;                  // Timer we use to update
+                                           // the dialog
   NSMutableDictionary *serverDictionary_;  // The dictionary mapping a
                                            // server type name to a
                                            // dictionary of server
                                            // parameter names.
   NSMutableDictionary *socorroDictionary_; // The dictionary for
                                            // Socorro.
   NSMutableDictionary *googleDictionary_;  // The dictionary for
                                            // Google.
@@ -102,9 +128,12 @@ extern NSString *const kDefaultServerTyp
 
 // Accessors to make bindings work
 - (NSString *)commentsValue;
 - (void)setCommentsValue:(NSString *)value;
 
 - (NSString *)emailValue;
 - (void)setEmailValue:(NSString *)value;
 
+- (NSString *)countdownMessage;
+- (void)setCountdownMessage:(NSString *)value;
+
 @end
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m
@@ -37,18 +37,21 @@
 #import "common/mac/HTTPMultipartUpload.h"
 
 #import "crash_report_sender.h"
 #import "common/mac/GTMLogger.h"
 
 
 #define kLastSubmission @"LastSubmission"
 const int kMinidumpFileLengthLimit = 800000;
+const int kUserCommentsMaxLength = 1500;
+const int kEmailMaxLength = 64;
 
-#define kApplePrefsSyncExcludeAllKey @"com.apple.PreferenceSync.ExcludeAllSyncKeys"
+#define kApplePrefsSyncExcludeAllKey \
+  @"com.apple.PreferenceSync.ExcludeAllSyncKeys"
 
 NSString *const kGoogleServerType = @"google";
 NSString *const kSocorroServerType = @"socorro";
 NSString *const kDefaultServerType = @"google";
 
 #pragma mark -
 
 @interface NSView (ResizabilityExtentions)
@@ -104,19 +107,22 @@ NSString *const kDefaultServerType = @"g
 // current text, preserving the existing height and origin.
 // Returns the change in width.
 - (float)breakpad_adjustWidthToFit;
 @end
 
 @implementation NSTextField (ResizabilityExtentions)
 - (float)breakpad_adjustHeightToFit {
   NSRect oldFrame = [self frame];
+  // Starting with the 10.5 SDK, height won't grow, so make it huge to start.
+  NSRect presizeFrame = oldFrame;
+  presizeFrame.size.height = MAXFLOAT;
   // sizeToFit will blow out the width rather than making the field taller, so
   // we do it manually.
-  NSSize newSize = [[self cell] cellSizeForBounds:oldFrame];
+  NSSize newSize = [[self cell] cellSizeForBounds:presizeFrame];
   NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y,
                                NSWidth(oldFrame), newSize.height);
   [self setFrame:newFrame];
 
   return newSize.height - NSHeight(oldFrame);
 }
 
 - (float)breakpad_adjustWidthToFit {
@@ -169,50 +175,53 @@ NSString *const kDefaultServerType = @"g
 
 // Returns YES if it has been long enough since the last report that we should
 // submit a report for this crash.
 - (BOOL)reportIntervalElapsed;
 
 // Returns YES if we should send the report without asking the user first.
 - (BOOL)shouldSubmitSilently;
 
+// Returns YES if the minidump was generated on demand.
+- (BOOL)isOnDemand;
+
 // Returns YES if we should ask the user to provide comments.
 - (BOOL)shouldRequestComments;
 
 // Returns YES if we should ask the user to provide an email address.
 - (BOOL)shouldRequestEmail;
 
 // Shows UI to the user to ask for permission to send and any extra information
 // we've been instructed to request. Returns YES if the user allows the report
 // to be sent.
 - (BOOL)askUserPermissionToSend;
 
 // Returns the short description of the crash, suitable for use as a dialog
 // title (e.g., "The application Foo has quit unexpectedly").
-- (NSString*)shortCrashDialogMessage;
+- (NSString*)shortDialogMessage;
 
 // Return explanatory text about the crash and the reporter, suitable for the
 // body text of a dialog.
-- (NSString*)explanatoryCrashDialogText;
+- (NSString*)explanatoryDialogText;
 
 // Returns the amount of time the UI should be shown before timing out.
 - (NSTimeInterval)messageTimeout;
 
 // Preps the comment-prompting alert window for display:
 // * localizes all the elements
 // * resizes and adjusts layout as necessary for localization
 // * removes the email section if includeEmail is NO
 - (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail;
 
 // Rmevoes the email section of the dialog, adjusting the rest of the window
 // as necessary.
 - (void)removeEmailPrompt;
 
 // Run an alert window with the given timeout. Returns
-// NSAlertButtonDefault if the timeout is exceeded. A timeout of 0
+// NSRunStoppedResponse if the timeout is exceeded. A timeout of 0
 // queues the message immediately in the modal run loop.
 - (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout;
 
 // Returns a unique client id (user-specific), creating a persistent
 // one in the user defaults, if necessary.
 - (NSString*)clientID;
 
 // Returns a dictionary that can be used to map Breakpad parameter names to
@@ -230,16 +239,26 @@ NSString *const kDefaultServerType = @"g
 
 // Accessor method for the URL parameter dictionary
 - (NSMutableDictionary *)urlParameterDictionary;
 
 // This method adds a key/value pair to the dictionary that
 // will be uploaded to the crash server.
 - (void)addServerParameter:(id)value forKey:(NSString *)key;
 
+// This method is used to periodically update the UI with how many
+// seconds are left in the dialog display.
+- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer;
+
+// When we receive this notification, it means that the user has
+// begun editing the email address or comments field, and we disable
+// the timers so that the user has as long as they want to type
+// in their comments/email.
+- (void)controlTextDidBeginEditing:(NSNotification *)aNotification;
+
 @end
 
 @implementation Reporter
 //=============================================================================
 + (uid_t)consoleUID {
   SCDynamicStoreRef store =
     SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Reporter"), NULL, NULL);
   uid_t uid = -2;  // Default to "nobody"
@@ -256,16 +275,17 @@ NSString *const kDefaultServerType = @"g
 
   return uid;
 }
 
 //=============================================================================
 - (id)initWithConfigurationFD:(int)fd {
   if ((self = [super init])) {
     configFile_ = fd;
+    remainingDialogTime_ = 0;
   }
 
   // Because the reporter is embedded in the framework (and many copies
   // of the framework may exist) its not completely certain that the OS
   // will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our
   // Info.plist. To make sure, also set the key directly if needed.
   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
   if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) {
@@ -540,18 +560,18 @@ NSString *const kDefaultServerType = @"g
     if ([self commentsValue]) {
       [parameters_ setObject:[self commentsValue] forKey:@BREAKPAD_COMMENTS];
     }
     if ([self emailValue]) {
       [parameters_ setObject:[self emailValue] forKey:@BREAKPAD_EMAIL];
     }
   } else {
     // Create an alert panel to tell the user something happened
-    NSPanel* alert = NSGetAlertPanel([self shortCrashDialogMessage],
-                                     [self explanatoryCrashDialogText],
+    NSPanel* alert = NSGetAlertPanel([self shortDialogMessage],
+                                     [self explanatoryDialogText],
                                      NSLocalizedString(@"sendReportButton", @""),
                                      NSLocalizedString(@"cancelButton", @""),
                                      nil);
 
     // Pop the alert with an automatic timeout, and wait for the response
     buttonPressed = [self runModalWindow:alert withTimeout:timeout];
 
     // Release the panel memory
@@ -561,21 +581,21 @@ NSString *const kDefaultServerType = @"g
 }
 
 - (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail {
   // Swap in localized values, making size adjustments to impacted elements as
   // we go. Remember that the origin is in the bottom left, so elements above
   // "fall" as text areas are shrunk from their overly-large IB sizes.
 
   // Localize the header. No resizing needed, as it has plenty of room.
-  [dialogTitle_ setStringValue:[self shortCrashDialogMessage]];
+  [dialogTitle_ setStringValue:[self shortDialogMessage]];
 
   // Localize the explanatory text field.
   [commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@",
-                                   [self explanatoryCrashDialogText],
+                                   [self explanatoryDialogText],
                                    NSLocalizedString(@"commentsMsg", @"")]];
   float commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit];
   [headerBox_ breakpad_shiftVertically:commentHeightDelta];
   [alertWindow_ breakpad_adjustHeight:commentHeightDelta];
 
   // Either localize the email explanation field or remove the whole email
   // section depending on whether or not we are asking for email.
   if (includeEmail) {
@@ -610,32 +630,30 @@ NSString *const kDefaultServerType = @"g
   float emailSectionHeight = NSHeight([emailSectionBox_ frame]);
   [preEmailBox_ breakpad_shiftVertically:(-emailSectionHeight)];
   [alertWindow_ breakpad_adjustHeight:(-emailSectionHeight)];
 }
 
 - (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout {
   // Queue a |stopModal| message to be performed in |timeout| seconds.
   if (timeout > 0.001) {
-    [NSApp performSelector:@selector(stopModal)
-                withObject:nil
-                afterDelay:timeout];
+    remainingDialogTime_ = timeout;
+    SEL updateSelector = @selector(updateSecondsLeftInDialogDisplay:);
+    messageTimer_ = [NSTimer scheduledTimerWithTimeInterval:1.0
+                                                     target:self
+                                                   selector:updateSelector
+                                                   userInfo:nil
+                                                    repeats:YES];
   }
 
   // Run the window modally and wait for either a |stopModal| message or a
   // button click.
   [NSApp activateIgnoringOtherApps:YES];
   int returnMethod = [NSApp runModalForWindow:window];
 
-  // Cancel the pending |stopModal| message.
-  if (returnMethod != NSRunStoppedResponse) {
-    [NSObject cancelPreviousPerformRequestsWithTarget:NSApp
-                                             selector:@selector(stopModal)
-                                               object:nil];
-  }
   return returnMethod;
 }
 
 - (IBAction)sendReport:(id)sender {
   // Force the text fields to end editing so text for the currently focused
   // field will be commited.
   [alertWindow_ makeFirstResponder:alertWindow_];
 
@@ -662,25 +680,71 @@ NSString *const kDefaultServerType = @"g
 }
 
 // Text Field Delegate Methods
 //=============================================================================
 - (BOOL)    control:(NSControl*)control
            textView:(NSTextView*)textView
 doCommandBySelector:(SEL)commandSelector {
   BOOL result = NO;
-  // If the user has entered text, don't end editing on "return"
-  if (commandSelector == @selector(insertNewline:)
+  // If the user has entered text on the comment field, don't end
+  // editing on "return".
+  if (control == commentsEntryField_ &&
+      commandSelector == @selector(insertNewline:)
       && [[textView string] length] > 0) {
     [textView insertNewlineIgnoringFieldEditor:self];
     result = YES;
   }
   return result;
 }
 
+- (void)controlTextDidBeginEditing:(NSNotification *)aNotification {
+  [messageTimer_ invalidate];
+  [self setCountdownMessage:@""];
+}
+
+- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer {
+  remainingDialogTime_ -= 1;
+
+  NSString *countdownMessage;
+  NSString *formatString;
+
+  int displayedTimeLeft; // This can be either minutes or seconds.
+  
+  if (remainingDialogTime_ > 59) {
+    // calculate minutes remaining for UI purposes
+    displayedTimeLeft = (remainingDialogTime_ / 60);
+    
+    if (displayedTimeLeft == 1) {
+      formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @"");
+    } else {
+      formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @"");
+    }
+  } else {
+    displayedTimeLeft = remainingDialogTime_;
+    if (remainingDialogTime_ == 1) {
+      formatString = NSLocalizedString(@"countdownMsgSecondSingular", @"");
+    } else {
+      formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @"");
+    }
+  }
+  countdownMessage = [NSString stringWithFormat:formatString,
+                               displayedTimeLeft];
+  if (remainingDialogTime_ <= 30) {
+    [countdownLabel_ setTextColor:[NSColor redColor]];
+  }
+  [self setCountdownMessage:countdownMessage];
+  if (remainingDialogTime_ <= 0) {
+    [messageTimer_ invalidate];
+    [NSApp stopModal];
+  }
+}
+
+
+
 #pragma mark Accessors
 #pragma mark -
 //=============================================================================
 
 - (NSString *)commentsValue {
   return [[commentsValue_ retain] autorelease];
 }
 
@@ -697,16 +761,27 @@ doCommandBySelector:(SEL)commandSelector
 
 - (void)setEmailValue:(NSString *)value {
   if (emailValue_ != value) {
     [emailValue_ release];
     emailValue_ = [value copy];
   }
 }
 
+- (NSString *)countdownMessage {
+  return [[countdownMessage_ retain] autorelease];
+}
+
+- (void)setCountdownMessage:(NSString *)value {
+  if (countdownMessage_ != value) {
+    [countdownMessage_ release];
+    countdownMessage_ = [value copy];
+  }
+}
+
 #pragma mark -
 //=============================================================================
 - (BOOL)reportIntervalElapsed {
   float interval = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL]
     floatValue];
   NSString *program = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
   NSMutableDictionary *programDict =
@@ -725,46 +800,70 @@ doCommandBySelector:(SEL)commandSelector
   GTMLoggerDebug(@"Reporter Interval: %f", interval);
   if (interval > spanSeconds) {
     GTMLoggerDebug(@"Within throttling interval, not sending report");
     return NO;
   }
   return YES;
 }
 
+- (BOOL)isOnDemand {
+  return [[parameters_ objectForKey:@BREAKPAD_ON_DEMAND]
+	   isEqualToString:@"YES"];
+}
+
 - (BOOL)shouldSubmitSilently {
   return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM]
             isEqualToString:@"YES"];
 }
 
 - (BOOL)shouldRequestComments {
   return [[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS]
             isEqualToString:@"YES"];
 }
 
 - (BOOL)shouldRequestEmail {
   return [[parameters_ objectForKey:@BREAKPAD_REQUEST_EMAIL]
             isEqualToString:@"YES"];
 }
 
-- (NSString*)shortCrashDialogMessage {
+- (NSString*)shortDialogMessage {
   NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
   if (![displayName length])
     displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
 
-  return [NSString stringWithFormat:NSLocalizedString(@"headerFmt", @""),
-                                    displayName];
+  if ([self isOnDemand]) {
+    return [NSString
+             stringWithFormat:NSLocalizedString(@"noCrashDialogHeader", @""),
+             displayName];
+  } else {
+    return [NSString 
+             stringWithFormat:NSLocalizedString(@"crashDialogHeader", @""),
+             displayName];
+  }
 }
 
-- (NSString*)explanatoryCrashDialogText {
+- (NSString*)explanatoryDialogText {
+  NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
+  if (![displayName length])
+    displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
+
   NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR];
   if (![vendor length])
     vendor = @"unknown vendor";
 
-  return [NSString stringWithFormat:NSLocalizedString(@"msgFmt", @""), vendor];
+  if ([self isOnDemand]) {
+    return [NSString
+             stringWithFormat:NSLocalizedString(@"noCrashDialogMsg", @""),
+             vendor, displayName];
+  } else {
+    return [NSString
+             stringWithFormat:NSLocalizedString(@"crashDialogMsg", @""),
+             vendor];
+  }
 }
 
 - (NSTimeInterval)messageTimeout {
   // Get the timeout value for the notification.
   NSTimeInterval timeout = [[parameters_ objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]
                               floatValue];
   // Require a timeout of at least a minute (except 0, which means no timeout).
   if (timeout > 0.001 && timeout < 60.0) {
@@ -935,16 +1034,87 @@ doCommandBySelector:(SEL)commandSelector
   [logFileData_ release];
   [googleDictionary_ release];
   [socorroDictionary_ release];
   [serverDictionary_ release];
   [extraServerVars_ release];
   [super dealloc];
 }
 
+- (void)awakeFromNib {
+  [emailEntryField_ setMaximumLength:kEmailMaxLength];
+  [commentsEntryField_ setMaximumLength:kUserCommentsMaxLength];
+}
+
+@end
+
+//=============================================================================
+@implementation LengthLimitingTextField
+
+- (void) setMaximumLength:(unsigned int)maxLength {
+  maximumLength_ = maxLength;
+}
+
+// This is the method we're overriding in NSTextField, which lets us
+// limit the user's input if it makes the string too long.
+- (BOOL)       textView:(NSTextView *)textView
+shouldChangeTextInRange:(NSRange)affectedCharRange
+      replacementString:(NSString *)replacementString {
+
+  // Sometimes the range comes in invalid, so reject if we can't
+  // figure out if the replacement text is too long.
+  if (affectedCharRange.location == NSNotFound) {
+    return NO;
+  }
+  // Figure out what the new string length would be, taking into
+  // account user selections.
+  int newStringLength =
+    [[textView string] length] - affectedCharRange.length +
+    [replacementString length];
+  if (newStringLength > maximumLength_) {
+    return NO;
+  } else {
+    return YES;
+  }
+}
+
+// Cut, copy, and paste have to be caught specifically since there is no menu.
+- (BOOL)performKeyEquivalent:(NSEvent*)event {
+  // Only handle the key equivalent if |self| is the text field with focus.
+  NSText* fieldEditor = [self currentEditor];
+  if (fieldEditor != nil) {
+    // Check for a single "Command" modifier
+    unsigned int modifiers = [event modifierFlags];
+    modifiers &= NSDeviceIndependentModifierFlagsMask;
+    if (modifiers == NSCommandKeyMask) {
+      // Now, check for Select All, Cut, Copy, or Paste key equivalents.
+      NSString* characters = [event characters];
+      // Select All is Command-A.
+      if ([characters isEqualToString:@"a"]) {
+        [fieldEditor selectAll:self];
+        return YES;
+      // Cut is Command-X.
+      } else if ([characters isEqualToString:@"x"]) {
+        [fieldEditor cut:self];
+        return YES;
+      // Copy is Command-C.
+      } else if ([characters isEqualToString:@"c"]) {
+        [fieldEditor copy:self];
+        return YES;
+      // Paste is Command-V.
+      } else if ([characters isEqualToString:@"v"]) {
+        [fieldEditor paste:self];
+        return YES;
+      }
+    }
+  }
+  // Let the super class handle the rest (e.g. Command-Period will cancel).
+  return [super performKeyEquivalent:event];
+}
+
 @end
 
 //=============================================================================
 int main(int argc, const char *argv[]) {
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 #if DEBUG
   // Log to stderr in debug builds.
   [GTMLogger setSharedLogger:[GTMLogger standardLoggerWithStderr]];
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f318a56711d43d1b8925f1c26e709da0872dd607
GIT binary patch
literal 3591
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
N002ovPDHLkV1jxL0`LF;
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm
@@ -204,13 +204,14 @@ const mach_port_t kNoLastExceptionThread
 
   // Cause Breakpad's exception handler to be invoked.
   BreakpadGenerateAndSendReport(b);
 
   STAssertEquals(last_exception_type_, 0,
                  @"Last exception type is not 0 for on demand");
   STAssertEquals(last_exception_code_, 0,
                  @"Last exception code is not 0 for on demand");
-  STAssertEquals(last_exception_thread_, (mach_port_t)0,
-                 @"Last exception thread is not 0 for on demand");
+  STAssertEquals(last_exception_thread_, mach_thread_self(),
+                 @"Last exception thread is not mach_thread_self() "
+                 "for on demand");
 }
 
 @end
--- a/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc
@@ -32,43 +32,53 @@
 // See minidump_file_writer.h for documentation.
 
 #include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
+#include "common/linux/linux_syscall_support.h"
+#include "common/linux/linux_libc_support.h"
 #include "client/minidump_file_writer-inl.h"
 #include "common/string_conversion.h"
 
 namespace google_breakpad {
 
 const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
 
 MinidumpFileWriter::MinidumpFileWriter() : file_(-1), position_(0), size_(0) {
 }
 
 MinidumpFileWriter::~MinidumpFileWriter() {
   Close();
 }
 
 bool MinidumpFileWriter::Open(const char *path) {
   assert(file_ == -1);
+#if __linux__
+  file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
+#else
   file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
+#endif
 
   return file_ != -1;
 }
 
 bool MinidumpFileWriter::Close() {
   bool result = true;
 
   if (file_ != -1) {
     ftruncate(file_, position_);
+#if __linux__
+    result = (sys_close(file_) == 0);
+#else
     result = (close(file_) == 0);
+#endif
     file_ = -1;
   }
 
   return result;
 }
 
 bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
                                               unsigned int length,
@@ -222,19 +232,26 @@ bool MinidumpFileWriter::Copy(MDRVA posi
   assert(size);
   assert(file_ != -1);
 
   // Ensure that the data will fit in the allocated space
   if (size + position > size_)
     return false;
 
   // Seek and write the data
-  if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position))
-    if (write(file_, src, size) == size)
+#if __linux__
+  if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
+    if (sys_write(file_, src, size) == size) {
+#else
+  if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
+    if (write(file_, src, size) == size) {
+#endif
       return true;
+    }
+  }
 
   return false;
 }
 
 bool UntypedMDRVA::Allocate(size_t size) {
   assert(size_ == 0);
   size_ = size;
   position_ = writer_->Allocate(size_);
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln
@@ -5,16 +5,18 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C9
 	ProjectSection(ProjectDependencies) = postProject
 		{A820AF62-6239-4693-8430-4F516C1838F4} = {A820AF62-6239-4693-8430-4F516C1838F4}
 	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_report_sender", "sender\crash_report_sender.vcproj", "{9946A048-043B-4F8F-9E07-9297B204714C}"
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_generation", "crash_generation\crash_generation.vcproj", "{A820AF62-6239-4693-8430-4F516C1838F4}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exception_handler_test", "handler\exception_handler_test\exception_handler_test.vcproj", "{89094A11-CF25-4037-AF43-EACFA751405E}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
 		DebugStaticCRT|Win32 = DebugStaticCRT|Win32
 		Release|Win32 = Release|Win32
 		ReleaseStaticCRT|Win32 = ReleaseStaticCRT|Win32
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
@@ -37,13 +39,21 @@ Global
 		{A820AF62-6239-4693-8430-4F516C1838F4}.Debug|Win32.ActiveCfg = Debug|Win32
 		{A820AF62-6239-4693-8430-4F516C1838F4}.Debug|Win32.Build.0 = Debug|Win32
 		{A820AF62-6239-4693-8430-4F516C1838F4}.DebugStaticCRT|Win32.ActiveCfg = DebugStaticCRT|Win32
 		{A820AF62-6239-4693-8430-4F516C1838F4}.DebugStaticCRT|Win32.Build.0 = DebugStaticCRT|Win32
 		{A820AF62-6239-4693-8430-4F516C1838F4}.Release|Win32.ActiveCfg = Release|Win32
 		{A820AF62-6239-4693-8430-4F516C1838F4}.Release|Win32.Build.0 = Release|Win32
 		{A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.ActiveCfg = ReleaseStaticCRT|Win32
 		{A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.Build.0 = ReleaseStaticCRT|Win32
+		{89094A11-CF25-4037-AF43-EACFA751405E}.Debug|Win32.ActiveCfg = Debug|Win32
+		{89094A11-CF25-4037-AF43-EACFA751405E}.Debug|Win32.Build.0 = Debug|Win32
+		{89094A11-CF25-4037-AF43-EACFA751405E}.DebugStaticCRT|Win32.ActiveCfg = Debug|Win32
+		{89094A11-CF25-4037-AF43-EACFA751405E}.DebugStaticCRT|Win32.Build.0 = Debug|Win32
+		{89094A11-CF25-4037-AF43-EACFA751405E}.Release|Win32.ActiveCfg = Release|Win32
+		{89094A11-CF25-4037-AF43-EACFA751405E}.Release|Win32.Build.0 = Release|Win32
+		{89094A11-CF25-4037-AF43-EACFA751405E}.ReleaseStaticCRT|Win32.ActiveCfg = Release|Win32
+		{89094A11-CF25-4037-AF43-EACFA751405E}.ReleaseStaticCRT|Win32.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
 EndGlobal
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc
@@ -213,22 +213,24 @@ bool CrashGenerationServer::Start() {
                                    TRUE,   // Manual reset.
                                    FALSE,  // Initially signaled.
                                    NULL);  // Name.
   if (!overlapped_.hEvent) {
     return false;
   }
 
   // Register a callback with the thread pool for the client connection.
-  RegisterWaitForSingleObject(&pipe_wait_handle_,
-                              overlapped_.hEvent,
-                              OnPipeConnected,
-                              this,
-                              INFINITE,
-                              kPipeIOThreadFlags);
+  if (!RegisterWaitForSingleObject(&pipe_wait_handle_,
+                                   overlapped_.hEvent,
+                                   OnPipeConnected,
+                                   this,
+                                   INFINITE,
+                                   kPipeIOThreadFlags)) {
+    return false;
+  }
 
   pipe_ = CreateNamedPipe(pipe_name_.c_str(),
                           kPipeAttr,
                           kPipeMode,
                           1,
                           kOutBufferSize,
                           kInBufferSize,
                           0,
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc
@@ -244,22 +244,22 @@ ExceptionHandler::~ExceptionHandler() {
       _set_purecall_handler(previous_pch_);
 
     if (handler_stack_->back() == this) {
       handler_stack_->pop_back();
     } else {
       // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the
       // system's application event log.
       fprintf(stderr, "warning: removing Breakpad handler out of order\n");
-      for (vector<ExceptionHandler*>::iterator iterator =
-               handler_stack_->begin();
-           iterator != handler_stack_->end();
-           ++iterator) {
+      vector<ExceptionHandler*>::iterator iterator = handler_stack_->begin();
+      while (iterator != handler_stack_->end()) {
         if (*iterator == this) {
-          handler_stack_->erase(iterator);
+          iterator = handler_stack_->erase(iterator);
+        } else {
+          ++iterator;
         }
       }
     }
 
     if (handler_stack_->empty()) {
       // When destroying the last ExceptionHandler that installed a handler,
       // clean up the handler stack.
       delete handler_stack_;
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc
@@ -0,0 +1,164 @@
+// Copyright 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "breakpad_googletest_includes.h"
+#include "client/windows/crash_generation/crash_generation_server.h"
+#include "client/windows/handler/exception_handler.h"
+#include <windows.h>
+#include <dbghelp.h>
+#include <strsafe.h>
+#include <objbase.h>
+#include <shellapi.h>
+
+namespace {
+const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
+const char kSuccessIndicator[] = "success";
+const char kFailureIndicator[] = "failure";
+
+// Utility function to test for a path's existence.
+BOOL DoesPathExist(const TCHAR *path_name);
+
+class ExceptionHandlerDeathTest : public ::testing::Test {
+protected:
+  // Member variable for each test that they can use
+  // for temporary storage.
+  TCHAR temp_path_[MAX_PATH];
+  // Actually constructs a temp path name.
+  virtual void SetUp();
+  // A helper method that tests can use to crash.
+  void DoCrash();
+};
+
+void ExceptionHandlerDeathTest::SetUp() {
+  const ::testing::TestInfo* const test_info =
+    ::testing::UnitTest::GetInstance()->current_test_info();
+  TCHAR temp_path[MAX_PATH] = { '\0' };
+  TCHAR test_name_wide[MAX_PATH] = { '\0' };
+  // We want the temporary directory to be what the OS returns
+  // to us, + the test case name.  
+  GetTempPath(MAX_PATH, temp_path);
+  // THe test case name is exposed to use as a c-style string,
+  // But we might be working in UNICODE here on Windows.
+  int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(), 
+    (int)strlen(test_info->name()), test_name_wide, MAX_PATH);
+  if (!dwRet) {
+    assert(false);
+  }
+  StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide);
+  CreateDirectory(temp_path_, NULL);
+}
+
+BOOL DoesPathExist(const TCHAR *path_name) {
+  DWORD flags = GetFileAttributes(path_name);
+  if (flags == INVALID_FILE_ATTRIBUTES) {
+    return FALSE;
+  }
+  return TRUE;
+}
+
+bool MinidumpWrittenCallback(const wchar_t* dump_path, 
+                             const wchar_t* minidump_id,
+                             void* context,
+                             EXCEPTION_POINTERS* exinfo,
+                             MDRawAssertionInfo* assertion,
+                             bool succeeded) {
+  if (succeeded && DoesPathExist(dump_path)) {
+    fprintf(stderr, kSuccessIndicator);
+  } else {
+    fprintf(stderr, kFailureIndicator);
+  }
+  // If we don't flush, the output doesn't get sent before
+  // this process dies.
+  fflush(stderr);
+  return succeeded;
+}
+
+TEST_F(ExceptionHandlerDeathTest, InProcTest) {
+  // For the in-proc test, we just need to instantiate an exception
+  // handler in in-proc mode, and crash.   Since the entire test is
+  // reexecuted in the child process, we don't have to worry about
+  // the semantics of the exception handler being inherited/not
+  // inherited across CreateProcess().
+  ASSERT_TRUE(DoesPathExist(temp_path_));
+  google_breakpad::ExceptionHandler *exc = 
+    new google_breakpad::ExceptionHandler(
+    temp_path_, NULL, &MinidumpWrittenCallback, NULL, 
+    google_breakpad::ExceptionHandler::HANDLER_ALL);
+  int *i = NULL;  
+  ASSERT_DEATH((*i)++, kSuccessIndicator);
+  delete exc;
+}
+
+static bool gDumpCallbackCalled = false;
+
+void clientDumpCallback(void *dump_context,
+                        const google_breakpad::ClientInfo *client_info,
+                        const std::wstring *dump_path){
+
+  gDumpCallbackCalled = true;
+}
+
+void ExceptionHandlerDeathTest::DoCrash() {
+  google_breakpad::ExceptionHandler *exc = 
+    new google_breakpad::ExceptionHandler(
+    temp_path_, NULL, NULL, NULL,
+    google_breakpad::ExceptionHandler::HANDLER_ALL, MiniDumpNormal, kPipeName,
+    NULL);
+  // Although this is executing in the child process of the death test,
+  // if it's not true we'll still get an error rather than the crash 
+  // being expected.
+  ASSERT_TRUE(exc->IsOutOfProcess());
+  int *i = NULL;
+  printf("%d\n", (*i)++);
+}
+
+TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) {
+  // We can take advantage of a detail of google test here to save some
+  // complexity in testing: when you do a death test, it actually forks.
+  // So we can make the main test harness the crash generation server, 
+  // and call ASSERT_DEATH on a NULL dereference, it to expecting test
+  // the out of process scenario, since it's happening in a different
+  // process!  This is different from the above because, above, we pass
+  // a NULL pipe name, and we also don't start a crash generation server.
+
+  ASSERT_TRUE(DoesPathExist(temp_path_));
+  std::wstring dump_path(temp_path_);
+  google_breakpad::CrashGenerationServer server(
+    kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, true,
+    &dump_path);
+  
+  // This HAS to be EXPECT_, because when this test case is executed in the
+  // child process, the server registration will fail due to the named pipe
+  // being the same.
+  EXPECT_TRUE(server.Start());
+  EXPECT_FALSE(gDumpCallbackCalled);
+  ASSERT_DEATH(this->DoCrash(), "");
+  EXPECT_TRUE(gDumpCallbackCalled);
+}
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj
@@ -0,0 +1,266 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="exception_handler_test"
+	ProjectGUID="{89094A11-CF25-4037-AF43-EACFA751405E}"
+	RootNamespace="exception_handler_test"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+				Description=""
+				CommandLine=""
+				Outputs=""
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="..\..\..\..\testing;..\..\..\..\testing\include;..\..\..\..\testing\gtest;..\..\..\..\testing\gtest\include;..\..\..\..\"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				Description="Running tests"
+				CommandLine="$(OutDir)\$(ProjectName).exe"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="..\..\..\..\testing;..\..\..\..\testing\include;..\..\..\..\testing\gtest;..\..\..\..\testing\gtest\include;..\..\..\..\"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
+				RuntimeLibrary="2"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				Description="Running tests"
+				CommandLine="$(OutDir)\$(ProjectName).exe"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath="..\..\crash_generation\client_info.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\..\crash_generation\crash_generation_client.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\..\crash_generation\crash_generation_server.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\exception_handler.cc"
+				>
+			</File>
+			<File
+				RelativePath=".\exception_handler_test.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\..\testing\gtest\src\gtest-all.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\..\testing\gtest\src\gtest_main.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\..\common\windows\guid_string.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\..\crash_generation\minidump_generator.cc"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath="..\..\crash_generation\client_info.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\crash_generation\crash_generation_client.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\crash_generation\crash_generation_server.h"
+				>
+			</File>
+			<File
+				RelativePath="..\exception_handler.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\..\common\windows\guid_string.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\crash_generation\minidump_generator.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+		<File
+			RelativePath=".\ReadMe.txt"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc
@@ -178,17 +178,17 @@ bool ShowDumpResults(const wchar_t* dump
   int result = swprintf_s(text,
                           kMaximumLineLength,
                           TEXT("Dump generation request %s\r\n"),
                           succeeded ? TEXT("succeeded") : TEXT("failed"));
   if (result == -1) {
     delete [] text;
   }
 
-  AppendTextWorker(text);
+  QueueUserWorkItem(AppendTextWorker, text, WT_EXECUTEDEFAULT);
   return succeeded;
 }
 
 static void _cdecl ShowClientConnected(void* context,
                                        const ClientInfo* client_info) {
   TCHAR* line = new TCHAR[kMaximumLineLength];
   line[0] = _T('\0');
   int result = swprintf_s(line,
@@ -462,16 +462,17 @@ int APIENTRY _tWinMain(HINSTANCE instanc
   UNREFERENCED_PARAMETER(previous_instance);
   UNREFERENCED_PARAMETER(command_line);
 
   cs_edit = new CRITICAL_SECTION();
   InitializeCriticalSection(cs_edit);
 
   CustomClientInfo custom_info = {kCustomInfoEntries, kCustomInfoCount};
 
+  CrashServerStart();
   // This is needed for CRT to not show dialog for invalid param
   // failures and instead let the code handle it.
   _CrtSetReportMode(_CRT_ASSERT, 0);
   handler = new ExceptionHandler(L"C:\\dumps\\",
                                  NULL,
                                  google_breakpad::ShowDumpResults,
                                  NULL,
                                  ExceptionHandler::HANDLER_ALL,
--- a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc
@@ -22,136 +22,70 @@
 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include <a.out.h>
-#include <cstdarg>
-#include <cstdlib>
-#include <cstdio>
+#include <assert.h>
 #include <cxxabi.h>
 #include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <link.h>
+#include <string.h>
 #include <sys/mman.h>
-#include <stab.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+
 #include <algorithm>
-
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
 #include <functional>
 #include <list>
+#include <map>
+#include <string>
 #include <vector>
-#include <string.h>
 
 #include "common/linux/dump_symbols.h"
 #include "common/linux/file_id.h"
-#include "common/linux/guid_creator.h"
-#include "processor/scoped_ptr.h"
+#include "common/linux/module.h"
+#include "common/linux/stabs_reader.h"
 
 // This namespace contains helper functions.
 namespace {
 
-// Infomation of a line.
-struct LineInfo {
-  // The index into string table for the name of the source file which
-  // this line belongs to.
-  // Load from stab symbol.
-  uint32_t source_name_index;
-  // Offset from start of the function.
-  // Load from stab symbol.
-  ElfW(Off) rva_to_func;
-  // Offset from base of the loading binary.
-  ElfW(Off) rva_to_base;
-  // Size of the line.
-  // It is the difference of the starting address of the line and starting
-  // address of the next N_SLINE, N_FUN or N_SO.
-  uint32_t size;
-  // Line number.
-  uint32_t line_num;
-  // Id of the source file for this line.
-  int source_id;
-};
-
-typedef std::list<struct LineInfo> LineInfoList;
-
-// Information of a function.
-struct FuncInfo {
-  // Name of the function.
-  const char *name;
-  // Offset from the base of the loading address.
-  ElfW(Off) rva_to_base;
-  // Virtual address of the function.
-  // Load from stab symbol.
-  ElfW(Addr) addr;
-  // Size of the function.
-  // It is the difference of the starting address of the function and starting
-  // address of the next N_FUN or N_SO.
-  uint32_t size;
-  // Total size of stack parameters.
-  uint32_t stack_param_size;
-  // Is there any lines included from other files?
-  bool has_sol;
-  // Line information array.
-  LineInfoList line_info;
-};
-
-typedef std::list<struct FuncInfo> FuncInfoList;
-
-// Information of a source file.
-struct SourceFileInfo {
-  // Name string index into the string table.
-  uint32_t name_index;
-  // Name of the source file.
-  const char *name;
-  // Starting address of the source file.
-  ElfW(Addr) addr;
-  // Id of the source file.
-  int source_id;
-  // Functions information.
-  FuncInfoList func_info;
-};
-
-typedef std::list<struct SourceFileInfo> SourceFileInfoList;
-
-// Information of a symbol table.
-// This is the root of all types of symbol.
-struct SymbolInfo {
-  SourceFileInfoList source_file_info;
-
-  // The next source id for newly found source file.
-  int next_source_id;
-};
+using google_breakpad::Module;
+using std::vector;
 
 // Stab section name.
 static const char *kStabName = ".stab";
 
 // Demangle using abi call.
 // Older GCC may not support it.
-static std::string Demangle(const char *mangled) {
+static std::string Demangle(const std::string &mangled) {
   int status = 0;
-  char *demangled = abi::__cxa_demangle(mangled, NULL, NULL, &status);
+  char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
   if (status == 0 && demangled != NULL) {
     std::string str(demangled);
     free(demangled);
     return str;
   }
   return std::string(mangled);
 }
 
 // Fix offset into virtual address by adding the mapped base into offsets.
 // Make life easier when want to find something by offset.
 static void FixAddress(void *obj_base) {
-  ElfW(Word) base = reinterpret_cast<ElfW(Word)>(obj_base);
+  ElfW(Addr) base = reinterpret_cast<ElfW(Addr)>(obj_base);
   ElfW(Ehdr) *elf_header = static_cast<ElfW(Ehdr) *>(obj_base);
   elf_header->e_phoff += base;
   elf_header->e_shoff += base;
   ElfW(Shdr) *sections = reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
   for (int i = 0; i < elf_header->e_shnum; ++i)
     sections[i].sh_offset += base;
 }
 
@@ -182,508 +116,243 @@ static const ElfW(Shdr) *FindSectionByNa
   assert(nsection > 0);
 
   int name_len = strlen(name);
   if (name_len == 0)
     return NULL;
 
   for (int i = 0; i < nsection; ++i) {
     const char *section_name =
-      (char*)(strtab->sh_offset + sections[i].sh_name);
+      reinterpret_cast<char*>(strtab->sh_offset + sections[i].sh_name);
     if (!strncmp(name, section_name, name_len))
       return sections + i;
   }
   return NULL;
 }
 
-// TODO(liuli): Computer the stack parameter size.
-// Expect parameter variables are immediately following the N_FUN symbol.
-// Will need to parse the type information to get a correct size.
-static int LoadStackParamSize(struct nlist *list,
-                              struct nlist *list_end,
-                              struct FuncInfo *func_info) {
-  struct nlist *cur_list = list;
-  assert(cur_list->n_type == N_FUN);
-  ++cur_list;
-  int step = 1;
-  while (cur_list < list_end && cur_list->n_type == N_PSYM) {
-    ++cur_list;
-    ++step;
-  }
-  func_info->stack_param_size = 0;
-  return step;
-}
+// Our handler class for STABS data.
+class DumpStabsHandler: public google_breakpad::StabsHandler {
+ public:
+  DumpStabsHandler(Module *module) :
+      module_(module),
+      comp_unit_base_address_(0),
+      current_function_(NULL),
+      current_source_file_(NULL),
+      current_source_file_name_(NULL) { }
+
+  bool StartCompilationUnit(const char *name, uint64_t address,
+                            const char *build_directory);
+  bool EndCompilationUnit(uint64_t address);
+  bool StartFunction(const std::string &name, uint64_t address);
+  bool EndFunction(uint64_t address);
+  bool Line(uint64_t address, const char *name, int number);
+
+  // Do any final processing necessary to make module_ contain all the
+  // data provided by the STABS reader.
+  //
+  // Because STABS does not provide reliable size information for
+  // functions and lines, we need to make a pass over the data after
+  // processing all the STABS to compute those sizes.  We take care of
+  // that here.
+  void Finalize();
+
+ private:
+
+  // An arbitrary, but very large, size to use for functions whose
+  // size we can't compute properly.
+  static const uint64_t kFallbackSize = 0x10000000;
+
+  // The module we're contributing debugging info to.
+  Module *module_;
 
-static int LoadLineInfo(struct nlist *list,
-                        struct nlist *list_end,
-                        const struct SourceFileInfo &source_file_info,
-                        struct FuncInfo *func_info) {
-  struct nlist *cur_list = list;
-  func_info->has_sol = false;
-  // Records which source file the following lines belongs. Default
-  // to the file we are handling. This helps us handling inlined source.
-  // When encountering N_SOL, we will change this to the source file
-  // specified by N_SOL.
-  int current_source_name_index = source_file_info.name_index;
-  do {
-    // Skip non line information.
-    while (cur_list < list_end && cur_list->n_type != N_SLINE) {
-      // Only exit when got another function, or source file.
-      if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO)
-        return cur_list - list;
-      // N_SOL means source lines following it will be from
-      // another source file.
-      if (cur_list->n_type == N_SOL) {
-        func_info->has_sol = true;
+  // The functions we've generated so far.  We don't add these to
+  // module_ as we parse them.  Instead, we wait until we've computed
+  // their ending address, and their lines' ending addresses.
+  //
+  // We could just stick them in module_ from the outset, but if
+  // module_ already contains data gathered from other debugging
+  // formats, that would complicate the size computation.
+  vector<Module::Function *> functions_;
+
+  // Boundary addresses.  STABS doesn't necessarily supply sizes for
+  // functions and lines, so we need to compute them ourselves by
+  // finding the next object.
+  vector<Module::Address> boundaries_;
+
+  // The base address of the current compilation unit.  We use this to
+  // recognize functions we should omit from the symbol file.  (If you
+  // know the details of why we omit these, please patch this
+  // comment.)
+  Module::Address comp_unit_base_address_;
 
-        if (cur_list->n_un.n_strx > 0 &&
-            cur_list->n_un.n_strx != current_source_name_index) {
-          // The following lines will be from this source file.
-          current_source_name_index = cur_list->n_un.n_strx;
-        }
-      }
-      ++cur_list;
-    }
-    struct LineInfo line;
-    while (cur_list < list_end && cur_list->n_type == N_SLINE) {
-      line.source_name_index = current_source_name_index;
-      line.rva_to_func = cur_list->n_value;
-      // n_desc is a signed short
-      line.line_num = (unsigned short)cur_list->n_desc;
-      // Don't set it here.
-      // Will be processed in later pass.
-      line.source_id = -1;
-      func_info->line_info.push_back(line);
-      ++cur_list;
-    }
-  } while (list < list_end);
+  // The function we're currently contributing lines to.
+  Module::Function *current_function_;
+
+  // The last Module::File we got a line number in.
+  Module::File *current_source_file_;
 
-  return cur_list - list;
+  // The pointer in the .stabstr section of the name that
+  // current_source_file_ is built from.  This allows us to quickly
+  // recognize when the current line is in the same file as the
+  // previous one (which it usually is).
+  const char *current_source_file_name_;
+};
+    
+bool DumpStabsHandler::StartCompilationUnit(const char *name, uint64_t address,
+                                            const char *build_directory) {
+  assert(! comp_unit_base_address_);
+  current_source_file_name_ = name;
+  current_source_file_ = module_->FindFile(name);
+  comp_unit_base_address_ = address;
+  boundaries_.push_back(static_cast<Module::Address>(address));
+  return true;
 }
 
-static int LoadFuncSymbols(struct nlist *list,
-                           struct nlist *list_end,
-                           const ElfW(Shdr) *stabstr_section,
-                           struct SourceFileInfo *source_file_info) {
-  struct nlist *cur_list = list;
-  assert(cur_list->n_type == N_SO);
-  ++cur_list;
-  source_file_info->func_info.clear();
-  while (cur_list < list_end) {
-    // Go until the function symbol.
-    while (cur_list < list_end && cur_list->n_type != N_FUN) {
-      if (cur_list->n_type == N_SO) {
-        return cur_list - list;
-      }
-      ++cur_list;
-      continue;
-    }
-    if (cur_list->n_type == N_FUN) {
-      struct FuncInfo func_info;
-      func_info.name =
-        reinterpret_cast<char *>(cur_list->n_un.n_strx +
-                                 stabstr_section->sh_offset);
-      func_info.addr = cur_list->n_value;
-      func_info.rva_to_base = 0;
-      func_info.size = 0;
-      func_info.stack_param_size = 0;
-      func_info.has_sol = 0;
-
-      // Stack parameter size.
-      cur_list += LoadStackParamSize(cur_list, list_end, &func_info);
-      // Line info.
-      cur_list += LoadLineInfo(cur_list,
-                               list_end,
-                               *source_file_info,
-                               &func_info);
-
-      // Functions in this module should have address bigger than the module
-      // startring address.
-      // There maybe a lot of duplicated entry for a function in the symbol,
-      // only one of them can met this.
-      if (func_info.addr >= source_file_info->addr) {
-        source_file_info->func_info.push_back(func_info);
-      }
-    }
-  }
-  return cur_list - list;
-}
-
-// Comapre the address.
-// The argument should have a memeber named "addr"
-template<class T1, class T2>
-static bool CompareAddress(T1 *a, T2 *b) {
-  return a->addr < b->addr;
-}
-
-// Sort the array into increasing ordered array based on the virtual address.
-// Return vector of pointers to the elements in the incoming array. So caller
-// should make sure the returned vector lives longer than the incoming vector.
-template<class Container>
-static std::vector<typename Container::value_type *> SortByAddress(
-    Container *container) {
-  typedef typename Container::iterator It;
-  typedef typename Container::value_type T;
-  std::vector<T *> sorted_array_ptr;
-  sorted_array_ptr.reserve(container->size());
-  for (It it = container->begin(); it != container->end(); it++)
-    sorted_array_ptr.push_back(&(*it));
-  std::sort(sorted_array_ptr.begin(),
-            sorted_array_ptr.end(),
-            std::ptr_fun(CompareAddress<T, T>));
-
-  return sorted_array_ptr;
+bool DumpStabsHandler::EndCompilationUnit(uint64_t address) {
+  assert(comp_unit_base_address_);
+  comp_unit_base_address_ = 0;
+  current_source_file_ = NULL;
+  current_source_file_name_ = NULL;
+  if (address)
+    boundaries_.push_back(static_cast<Module::Address>(address));
+  return true;
 }
 
-// Find the address of the next function or source file symbol in the symbol
-// table. The address should be bigger than the current function's address.
-static ElfW(Addr) NextAddress(
-    std::vector<struct FuncInfo *> *sorted_functions,
-    std::vector<struct SourceFileInfo *> *sorted_files,
-    const struct FuncInfo &func_info) {
-  std::vector<struct FuncInfo *>::iterator next_func_iter =
-    std::find_if(sorted_functions->begin(),
-                 sorted_functions->end(),
-                 std::bind1st(
-                     std::ptr_fun(
-                         CompareAddress<struct FuncInfo,
-                                        struct FuncInfo>
-                         ),
-                     &func_info)
-                );
-  if (next_func_iter != sorted_functions->end())
-    return (*next_func_iter)->addr;
-
-  std::vector<struct SourceFileInfo *>::iterator next_file_iter =
-    std::find_if(sorted_files->begin(),
-                 sorted_files->end(),
-                 std::bind1st(
-                     std::ptr_fun(
-                         CompareAddress<struct FuncInfo,
-                                        struct SourceFileInfo>
-                         ),
-                     &func_info)
-                );
-  if (next_file_iter != sorted_files->end()) {
-    return (*next_file_iter)->addr;
-  }
-  return 0;
+bool DumpStabsHandler::StartFunction(const std::string &name,
+                                     uint64_t address) {
+  assert(! current_function_);
+  Module::Function *f = new Module::Function;
+  f->name_ = Demangle(name);
+  f->address_ = address;
+  f->size_ = 0;           // We compute this in DumpStabsHandler::Finalize().
+  f->parameter_size_ = 0; // We don't provide this information.
+  current_function_ = f;
+  boundaries_.push_back(static_cast<Module::Address>(address));
+  return true;
 }
 
-static int FindFileByNameIdx(uint32_t name_index,
-                             SourceFileInfoList &files) {
-  for (SourceFileInfoList::iterator it = files.begin();
-       it != files.end(); it++) {
-    if (it->name_index == name_index)
-      return it->source_id;
-  }
-
-  return -1;
+bool DumpStabsHandler::EndFunction(uint64_t address) {
+  assert(current_function_);
+  // Functions in this compilation unit should have address bigger
+  // than the compilation unit's starting address.  There may be a lot
+  // of duplicated entries for functions in the STABS data; only one
+  // entry can meet this requirement.
+  //
+  // (I don't really understand the above comment; just bringing it
+  // along from the previous code, and leaving the behaivor unchanged.
+  // If you know the whole story, please patch this comment.  --jimb)
+  if (current_function_->address_ >= comp_unit_base_address_)
+    functions_.push_back(current_function_);
+  else
+    delete current_function_;
+  current_function_ = NULL;
+  if (address)
+    boundaries_.push_back(static_cast<Module::Address>(address));
+  return true;
 }
 
-// Add included file information.
-// Also fix the source id for the line info.
-static void AddIncludedFiles(struct SymbolInfo *symbols,
-                             const ElfW(Shdr) *stabstr_section) {
-  for (SourceFileInfoList::iterator source_file_it =
-	 symbols->source_file_info.begin();
-       source_file_it != symbols->source_file_info.end();
-       ++source_file_it) {
-    struct SourceFileInfo &source_file = *source_file_it;
-
-    for (FuncInfoList::iterator func_info_it = source_file.func_info.begin(); 
-	 func_info_it != source_file.func_info.end();
-	 ++func_info_it) {
-      struct FuncInfo &func_info = *func_info_it;
-
-      for (LineInfoList::iterator line_info_it = func_info.line_info.begin(); 
-	   line_info_it != func_info.line_info.end(); ++line_info_it) {
-        struct LineInfo &line_info = *line_info_it;
-
-        assert(line_info.source_name_index > 0);
-        assert(source_file.name_index > 0);
-
-        // Check if the line belongs to the source file by comparing the
-        // name index into string table.
-        if (line_info.source_name_index != source_file.name_index) {
-          // This line is not from the current source file, check if this
-          // source file has been added before.
-          int found_source_id = FindFileByNameIdx(line_info.source_name_index,
-                                                  symbols->source_file_info);
-          if (found_source_id < 0) {
-            // Got a new included file.
-            // Those included files don't have address or line information.
-            SourceFileInfo new_file;
-            new_file.name_index = line_info.source_name_index;
-            new_file.name = reinterpret_cast<char *>(new_file.name_index +
-                                                     stabstr_section->sh_offset);
-            new_file.addr = 0;
-            new_file.source_id = symbols->next_source_id++;
-            line_info.source_id = new_file.source_id;
-            symbols->source_file_info.push_back(new_file);
-          } else {
-            // The file has been added.
-            line_info.source_id = found_source_id;
-          }
-        } else {
-          // The line belongs to the file.
-          line_info.source_id = source_file.source_id;
-        }
-      }  // for each line.
-    }  // for each function.
-  } // for each source file.
-
+bool DumpStabsHandler::Line(uint64_t address, const char *name, int number) {
+  assert(current_function_);
+  assert(current_source_file_);
+  if (name != current_source_file_name_) {
+    current_source_file_ = module_->FindFile(name);
+    current_source_file_name_ = name;
+  }
+  Module::Line line;
+  line.address_ = address;
+  line.size_ = 0;  // We compute this in DumpStabsHandler::Finalize().
+  line.file_ = current_source_file_;
+  line.number_ = number;
+  current_function_->lines_.push_back(line);
+  return true;
 }
 
-// Compute size and rva information based on symbols loaded from stab section.
-static bool ComputeSizeAndRVA(ElfW(Addr) loading_addr,
-                              struct SymbolInfo *symbols) {
-  std::vector<struct SourceFileInfo *> sorted_files =
-    SortByAddress(&(symbols->source_file_info));
-  for (size_t i = 0; i < sorted_files.size(); ++i) {
-    struct SourceFileInfo &source_file = *sorted_files[i];
-    std::vector<struct FuncInfo *> sorted_functions =
-      SortByAddress(&(source_file.func_info));
-    for (size_t j = 0; j < sorted_functions.size(); ++j) {
-      struct FuncInfo &func_info = *sorted_functions[j];
-      assert(func_info.addr >= loading_addr);
-      func_info.rva_to_base = func_info.addr - loading_addr;
-      func_info.size = 0;
-      ElfW(Addr) next_addr = NextAddress(&sorted_functions,
-                                         &sorted_files,
-                                         func_info);
-      // I've noticed functions with an address bigger than any other functions
-      // and source files modules, this is probably the last function in the
-      // module, due to limitions of Linux stab symbol, it is impossible to get
-      // the exact size of this kind of function, thus we give it a default
-      // very big value. This should be safe since this is the last function.
-      // But it is a ugly hack.....
-      // The following code can reproduce the case:
-      // template<class T>
-      // void Foo(T value) {
-      // }
-      //
-      // int main(void) {
-      //   Foo(10);
-      //   Foo(std::string("hello"));
-      //   return 0;
-      // }
-      // TODO(liuli): Find a better solution.
-      static const int kDefaultSize = 0x10000000;
-      static int no_next_addr_count = 0;
-      if (next_addr != 0) {
-        func_info.size = next_addr - func_info.addr;
-      } else {
-        if (no_next_addr_count > 1) {
-          fprintf(stderr, "Got more than one funtion without the \
-                  following symbol. Igore this function.\n");
-          fprintf(stderr, "The dumped symbol may not correct.\n");
-          assert(!"This should not happen!\n");
-          func_info.size = 0;
-          continue;
-        }
+void DumpStabsHandler::Finalize() {
+  // Sort our boundary list, so we can search it quickly.
+  sort(boundaries_.begin(), boundaries_.end());
+  // Sort all functions by address, just for neatness.
+  sort(functions_.begin(), functions_.end(),
+       Module::Function::CompareByAddress);
+  for (vector<Module::Function *>::iterator func_it = functions_.begin();
+       func_it != functions_.end();
+       func_it++) {
+    Module::Function *f = *func_it;
+    // Compute the function f's size.
+    vector<Module::Address>::iterator boundary
+        = std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address_);
+    if (boundary != boundaries_.end())
+      f->size_ = *boundary - f->address_;
+    else
+      // If this is the last function in the module, and the STABS
+      // reader was unable to give us its ending address, then assign
+      // it a bogus, very large value.  This will happen at most once
+      // per module: since we've added all functions' addresses to the
+      // boundary table, only one can be the last.
+      f->size_ = kFallbackSize;
 
-        no_next_addr_count++;
-        func_info.size = kDefaultSize;
-      }
-      // Compute line size.
-      for (LineInfoList::iterator line_info_it = func_info.line_info.begin(); 
-	   line_info_it != func_info.line_info.end(); line_info_it++) {
-        struct LineInfo &line_info = *line_info_it;
-	LineInfoList::iterator next_line_info_it = line_info_it;
-	next_line_info_it++;
-        line_info.size = 0;
-        if (next_line_info_it != func_info.line_info.end()) {
-          line_info.size =
-            next_line_info_it->rva_to_func - line_info.rva_to_func;
-        } else {
-          // The last line in the function.
-          // If we can find a function or source file symbol immediately
-          // following the line, we can get the size of the line by computing
-          // the difference of the next address to the starting address of this
-          // line.
-          // Otherwise, we need to set a default big enough value. This occurs
-          // mostly because the this function is the last one in the module.
-          if (next_addr != 0) {
-            ElfW(Off) next_addr_offset = next_addr - func_info.addr;
-            line_info.size = next_addr_offset - line_info.rva_to_func;
-          } else {
-            line_info.size = kDefaultSize;
-          }
-        }
-        line_info.rva_to_base = line_info.rva_to_func + func_info.rva_to_base;
-      }  // for each line.
-    }  // for each function.
-  } // for each source file.
-  return true;
+    // Compute sizes for each of the function f's lines --- if it has any.
+    if (! f->lines_.empty()) {
+      stable_sort(f->lines_.begin(), f->lines_.end(),
+                  Module::Line::CompareByAddress);
+      vector<Module::Line>::iterator last_line = f->lines_.end() - 1;
+      for (vector<Module::Line>::iterator line_it = f->lines_.begin();
+           line_it != last_line; line_it++)
+        line_it[0].size_ = line_it[1].address_ - line_it[0].address_;
+      // Compute the size of the last line from f's end address.
+      last_line->size_ = (f->address_ + f->size_) - last_line->address_;
+    }
+  }
+  // Now that everything has a size, add our functions to the module, and
+  // dispose of our private list.
+  module_->AddFunctions(functions_.begin(), functions_.end());
+  functions_.clear();
 }
 
 static bool LoadSymbols(const ElfW(Shdr) *stab_section,
                         const ElfW(Shdr) *stabstr_section,
-                        ElfW(Addr) loading_addr,
-                        struct SymbolInfo *symbols) {
+                        Module *module) {
   if (stab_section == NULL || stabstr_section == NULL)
     return false;
 
-  struct nlist *lists =
-    reinterpret_cast<struct nlist *>(stab_section->sh_offset);
-  int nstab = stab_section->sh_size / sizeof(struct nlist);
-  // First pass, load all symbols from the object file.
-  for (int i = 0; i < nstab; ) {
-    int step = 1;
-    struct nlist *cur_list = lists + i;
-    if (cur_list->n_type == N_SO) {
-      // FUNC <address> <length> <param_stack_size> <function>
-      struct SourceFileInfo source_file_info;
-      source_file_info.name_index = cur_list->n_un.n_strx;
-      source_file_info.name = reinterpret_cast<char *>(cur_list->n_un.n_strx +
-                                 stabstr_section->sh_offset);
-      source_file_info.addr = cur_list->n_value;
-      if (strchr(source_file_info.name, '.'))
-        source_file_info.source_id = symbols->next_source_id++;
-      else
-        source_file_info.source_id = -1;
-      step = LoadFuncSymbols(cur_list, lists + nstab,
-                             stabstr_section, &source_file_info);
-      symbols->source_file_info.push_back(source_file_info);
-    }
-    i += step;
-  }
-
-  // Second pass, compute the size of functions and lines.
-  if (ComputeSizeAndRVA(loading_addr, symbols)) {
-    // Third pass, check for included source code, especially for header files.
-    // Until now, we only have compiling unit information, but they can
-    // have code from include files, add them here.
-    AddIncludedFiles(symbols, stabstr_section);
-    return true;
-  }
-  return false;
+  // A callback object to handle data from the STABS reader.
+  DumpStabsHandler handler(module);
+  // Find the addresses of the STABS data, and create a STABS reader object.
+  uint8_t *stabs = reinterpret_cast<uint8_t *>(stab_section->sh_offset);
+  uint8_t *stabstr = reinterpret_cast<uint8_t *>(stabstr_section->sh_offset);
+  google_breakpad::StabsReader reader(stabs, stab_section->sh_size,
+                                      stabstr, stabstr_section->sh_size,
+                                      &handler);
+  // Read the STABS data, and do post-processing.
+  if (! reader.Process())
+    return false;
+  handler.Finalize();
+  return true;
 }
 
-static bool LoadSymbols(ElfW(Ehdr) *elf_header, struct SymbolInfo *symbols) {
+static bool LoadSymbols(ElfW(Ehdr) *elf_header, Module *module) {
   // Translate all offsets in section headers into address.
   FixAddress(elf_header);
   ElfW(Addr) loading_addr = GetLoadingAddress(
       reinterpret_cast<ElfW(Phdr) *>(elf_header->e_phoff),
       elf_header->e_phnum);
+  module->SetLoadAddress(loading_addr);
 
   const ElfW(Shdr) *sections =
     reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
   const ElfW(Shdr) *strtab = sections + elf_header->e_shstrndx;
   const ElfW(Shdr) *stab_section =
     FindSectionByName(kStabName, sections, strtab, elf_header->e_shnum);
   if (stab_section == NULL) {
     fprintf(stderr, "Stab section not found.\n");
     return false;
   }
   const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections;
 
   // Load symbols.
-  return LoadSymbols(stab_section, stabstr_section, loading_addr, symbols);
-}
-
-static bool WriteModuleInfo(FILE *file,
-                            ElfW(Half) arch,
-                            const std::string &obj_file) {
-  const char *arch_name = NULL;
-  if (arch == EM_386)
-    arch_name = "x86";
-  else if (arch == EM_X86_64)
-    arch_name = "x86_64";
-  else
-    return false;
-
-  unsigned char identifier[16];
-  google_breakpad::FileID file_id(obj_file.c_str());
-  if (file_id.ElfFileIdentifier(identifier)) {
-    char identifier_str[40];
-    file_id.ConvertIdentifierToString(identifier,
-                                      identifier_str, sizeof(identifier_str));
-    char id_no_dash[40];
-    int id_no_dash_len = 0;
-    memset(id_no_dash, 0, sizeof(id_no_dash));
-    for (int i = 0; identifier_str[i] != '\0'; ++i)
-      if (identifier_str[i] != '-')
-        id_no_dash[id_no_dash_len++] = identifier_str[i];
-    // Add an extra "0" by the end.
-    id_no_dash[id_no_dash_len++] = '0';
-    std::string filename = obj_file;
-    size_t slash_pos = obj_file.find_last_of("/");
-    if (slash_pos != std::string::npos)
-      filename = obj_file.substr(slash_pos + 1);
-    return 0 <= fprintf(file, "MODULE Linux %s %s %s\n", arch_name,
-                        id_no_dash, filename.c_str());
-  }
-  return false;
-}
-
-static bool WriteSourceFileInfo(FILE *file, const struct SymbolInfo &symbols) {
-  for (SourceFileInfoList::const_iterator it =
-	 symbols.source_file_info.begin();
-       it != symbols.source_file_info.end(); it++) {
-    if (it->source_id != -1) {
-      const char *name = it->name;
-      if (0 > fprintf(file, "FILE %d %s\n", it->source_id, name))
-        return false;
-    }
-  }
-  return true;
-}
-
-static bool WriteOneFunction(FILE *file,
-                             const struct FuncInfo &func_info){
-  // Discard the ending part of the name.
-  std::string func_name(func_info.name);
-  std::string::size_type last_colon = func_name.find_last_of(':');
-  if (last_colon != std::string::npos)
-    func_name = func_name.substr(0, last_colon);
-  func_name = Demangle(func_name.c_str());
-
-  if (func_info.size <= 0)
-    return true;
-
-  if (0 <= fprintf(file, "FUNC %lx %lx %d %s\n",
-                   (unsigned long) func_info.rva_to_base,
-                   (unsigned long) func_info.size,
-                   func_info.stack_param_size,
-                   func_name.c_str())) {
-    for (LineInfoList::const_iterator it = func_info.line_info.begin();
-	 it != func_info.line_info.end(); it++) {
-      const struct LineInfo &line_info = *it;
-      if (0 > fprintf(file, "%lx %lx %d %d\n",
-                      (unsigned long) line_info.rva_to_base,
-                      (unsigned long) line_info.size,
-                      line_info.line_num,
-                      line_info.source_id))
-        return false;
-    }
-    return true;
-  }
-  return false;
-}
-
-static bool WriteFunctionInfo(FILE *file, const struct SymbolInfo &symbols) {
-  for (SourceFileInfoList::const_iterator it =
-	 symbols.source_file_info.begin();
-       it != symbols.source_file_info.end(); it++) {
-    const struct SourceFileInfo &file_info = *it;
-    for (FuncInfoList::const_iterator fiIt = file_info.func_info.begin(); 
-	 fiIt != file_info.func_info.end(); fiIt++) {
-      const struct FuncInfo &func_info = *fiIt;
-      if (!WriteOneFunction(file, func_info))
-        return false;
-    }
-  }
-  return true;
-}
-
-static bool DumpStabSymbols(FILE *file, const struct SymbolInfo &symbols) {
-  return WriteSourceFileInfo(file, symbols) &&
-    WriteFunctionInfo(file, symbols);
+  return LoadSymbols(stab_section, stabstr_section, module);
 }
 
 //
 // FDWrapper
 //
 // Wrapper class to make sure opened file is closed.
 //
 class FDWrapper {
@@ -728,16 +397,58 @@ class MmapWrapper {
      size_ = 0;
    }
 
   private:
    void *base_;
    size_t size_;
 };
 
+// Return the breakpad symbol file identifier for the architecture of
+// ELF_HEADER.
+const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) {
+  ElfW(Half) arch = elf_header->e_machine;
+  if (arch == EM_386)
+    return "x86";
+  else if (arch == EM_X86_64)
+    return "x86_64";
+  else
+    return NULL;
+}
+
+// Format the Elf file identifier in IDENTIFIER as a UUID with the
+// dashes removed.
+std::string FormatIdentifier(unsigned char identifier[16]) {
+  char identifier_str[40];
+  google_breakpad::FileID::ConvertIdentifierToString(
+      identifier,
+      identifier_str,
+      sizeof(identifier_str));
+  std::string id_no_dash;
+  for (int i = 0; identifier_str[i] != '\0'; ++i)
+    if (identifier_str[i] != '-')
+      id_no_dash += identifier_str[i];
+  // Add an extra "0" by the end.  PDB files on Windows have an 'age'
+  // number appended to the end of the file identifier; this isn't
+  // really used or necessary on other platforms, but let's preserve
+  // the pattern.
+  id_no_dash += '0';
+  return id_no_dash;
+}
+
+// Return the non-directory portion of FILENAME: the portion after the
+// last slash, or the whole filename if there are no slashes.
+std::string BaseFileName(const std::string &filename) {
+  // Lots of copies!  basename's behavior is less than ideal.
+  char *c_filename = strdup(filename.c_str());
+  std::string base = basename(c_filename);
+  free(c_filename);
+  return base;
+}
+
 }  // namespace
 
 namespace google_breakpad {
 
 bool DumpSymbols::WriteSymbolFile(const std::string &obj_file,
                                   FILE *sym_file) {
   int obj_fd = open(obj_file.c_str(), O_RDONLY);
   if (obj_fd < 0)
@@ -749,22 +460,32 @@ bool DumpSymbols::WriteSymbolFile(const 
   void *obj_base = mmap(NULL, st.st_size,
                         PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0);
   if (obj_base == MAP_FAILED)
     return false;
   MmapWrapper map_wrapper(obj_base, st.st_size);
   ElfW(Ehdr) *elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_base);
   if (!IsValidElf(elf_header))
     return false;
-  struct SymbolInfo symbols;
-  symbols.next_source_id = 0;
+
+  unsigned char identifier[16];
+  google_breakpad::FileID file_id(obj_file.c_str());
+  if (! file_id.ElfFileIdentifier(identifier))
+    return false;
+
+  const char *architecture = ElfArchitecture(elf_header);
+  if (! architecture)
+    return false;
 
-  if (!LoadSymbols(elf_header, &symbols))
-     return false;
-  // Write to symbol file.
-  if (WriteModuleInfo(sym_file, elf_header->e_machine, obj_file) &&
-      DumpStabSymbols(sym_file, symbols))
-    return true;
+  std::string name = BaseFileName(obj_file);
+  std::string os = "Linux";
+  std::string id = FormatIdentifier(identifier);
 
-  return false;
+  Module module(name, os, architecture, id);
+  if (!LoadSymbols(elf_header, &module))
+    return false;
+  if (!module.Write(sym_file))
+    return false;
+
+  return true;
 }
 
 }  // namespace google_breakpad
--- a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc
@@ -27,114 +27,84 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
 // file_id.cc: Return a unique identifier for a file
 //
 // See file_id.h for documentation
 //
 
-#include <cassert>
-#include <cstdio>
+#include "common/linux/file_id.h"
+
+#include <arpa/inet.h>
 #include <elf.h>
 #include <fcntl.h>
 #include <link.h>
+#include <string.h>
 #include <sys/mman.h>
-#include <string.h>
 #include <unistd.h>
 
-#include "common/linux/file_id.h"
-#include "common/md5.h"
+#include <cassert>
+#include <cstdio>
 
 namespace google_breakpad {
 
-static bool FindElfTextSection(const void *elf_mapped_base,
-                               const void **text_start,
-                               int *text_size) {
-  assert(elf_mapped_base);
-  assert(text_start);
-  assert(text_size);
-
-  const unsigned char *elf_base =
-    static_cast<const unsigned char *>(elf_mapped_base);
-  const ElfW(Ehdr) *elf_header =
-    reinterpret_cast<const ElfW(Ehdr) *>(elf_base);
-  if (memcmp(elf_header, ELFMAG, SELFMAG) != 0)
-    return false;
-  *text_start = NULL;
-  *text_size = 0;
-  const ElfW(Shdr) *sections =
-    reinterpret_cast<const ElfW(Shdr) *>(elf_base + elf_header->e_shoff);
-  const char *text_section_name = ".text";
-  int name_len = strlen(text_section_name);
-  const ElfW(Shdr) *string_section = sections + elf_header->e_shstrndx;
-  const ElfW(Shdr) *text_section = NULL;
-  for (int i = 0; i < elf_header->e_shnum; ++i) {
-    if (sections[i].sh_type == SHT_PROGBITS) {
-      const char *section_name = (char*)(elf_base +
-                                         string_section->sh_offset +
-                                         sections[i].sh_name);
-      if (!strncmp(section_name, text_section_name, name_len)) {
-        text_section = &sections[i];
-        break;
-      }
-    }
-  }
-  if (text_section != NULL && text_section->sh_size > 0) {
-    int text_section_size = text_section->sh_size;
-    *text_start = elf_base + text_section->sh_offset;
-    *text_size = text_section_size;
-  }
-  return true;
-}
-
-FileID::FileID(const char *path) {
+FileID::FileID(const char* path) {
   strncpy(path_, path, sizeof(path_));
 }
 
-bool FileID::ElfFileIdentifier(unsigned char identifier[16]) {
+bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
+  const ssize_t mapped_len = 4096;  // Page size (matches WriteMappings())
   int fd = open(path_, O_RDONLY);
   if (fd < 0)
     return false;
   struct stat st;
-  if (fstat(fd, &st) != 0 && st.st_size <= 0) {
-    close(fd);
-    return false;
-  }
-  void *base = mmap(NULL, st.st_size,
-                    PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
-  if (base == MAP_FAILED) {
+  if (fstat(fd, &st) != 0 || st.st_size <= mapped_len) {
     close(fd);
     return false;
   }
-  bool success = false;
-  const void *text_section = NULL;
-  int text_size = 0;
-  if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) {
-    struct MD5Context md5;
-    MD5Init(&md5);
-    MD5Update(&md5,
-              static_cast<const unsigned char*>(text_section),
-              text_size);
-    MD5Final(identifier, &md5);
-    success = true;
+  void* base = mmap(NULL, mapped_len,
+                    PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+  close(fd);
+  if (base == MAP_FAILED)
+    return false;
+
+  memset(identifier, 0, kMDGUIDSize);
+  uint8_t* ptr = reinterpret_cast<uint8_t*>(base);
+  uint8_t* ptr_end = ptr + mapped_len;
+  while (ptr < ptr_end) {
+    for (unsigned i = 0; i < kMDGUIDSize; i++)
+      identifier[i] ^= ptr[i];
+    ptr += kMDGUIDSize;
   }
 
-  close(fd);
-  munmap(base, st.st_size);
-  return success;
+  munmap(base, mapped_len);
+  return true;
 }
 
 // static
-void FileID::ConvertIdentifierToString(const unsigned char identifier[16],
-                                       char *buffer, int buffer_length) {
+void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
+                                       char* buffer, int buffer_length) {
+  uint8_t identifier_swapped[kMDGUIDSize];
+
+  // Endian-ness swap to match dump processor expectation.
+  memcpy(identifier_swapped, identifier, kMDGUIDSize);
+  uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
+  *data1 = htonl(*data1);
+  uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
+  *data2 = htons(*data2);
+  uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
+  *data3 = htons(*data3);
+
   int buffer_idx = 0;
-  for (int idx = 0; (buffer_idx < buffer_length) && (idx < 16); ++idx) {
-    int hi = (identifier[idx] >> 4) &