bug 603592 - add a breakpad api to add info about known modules. r=mwu a=blocking-fennec
authorTed Mielczarek <ted.mielczarek@gmail.com>
Tue, 19 Oct 2010 14:52:27 -0400
changeset 56283 b799dd554e1259cb519ce2ee7048bf5e23587a01
parent 56282 f2253edf5fc4879285a092f94f7b1a70a5c92b2f
child 56284 781d0b2374705e2863cbf147fa16f7fe75251a35
push id16462
push userblassey@mozilla.com
push dateThu, 21 Oct 2010 00:44:42 +0000
treeherdermozilla-central@8fc42e3e185f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwu, blocking-fennec
bugs603592
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 603592 - add a breakpad api to add info about known modules. r=mwu a=blocking-fennec
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_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
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
@@ -83,21 +83,23 @@
 #endif
 #include <sys/wait.h>
 #if !defined(__ANDROID__)
 #include <ucontext.h>
 #endif
 #include <unistd.h>
 
 #include <algorithm>
+#include <utility>
 #include <vector>
 
 #include "common/linux/linux_libc_support.h"
 #include "common/linux/linux_syscall_support.h"
 #include "common/memory.h"
+#include "client/linux/minidump_writer/linux_dumper.h"
 #include "client/linux/minidump_writer/minidump_writer.h"
 #include "common/linux/guid_creator.h"
 #include "common/linux/eintr_wrapper.h"
 
 #ifndef PR_SET_PTRACER
 #define PR_SET_PTRACER 0x59616d61
 #endif
 
@@ -432,18 +434,21 @@ void ExceptionHandler::WaitForContinueSi
     sys_write(2, "\n", 1);
   }
 }
 
 // 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);
+  return google_breakpad::WriteMinidump(next_minidump_path_c_,
+                                        crashing_process,
+                                        context,
+                                        context_size,
+                                        mapping_info_);
 }
 
 // static
 bool ExceptionHandler::WriteMinidump(const std::string &dump_path,
                                      MinidumpCallback callback,
                                      void* callback_context) {
   return WriteMinidump(dump_path, false, callback, callback_context);
 }
@@ -510,9 +515,26 @@ bool ExceptionHandler::WriteMinidumpForC
                                       child,
                                       child_blamed_thread))
       return false;
 
   return callback ? callback(eh.dump_path_c_, eh.next_minidump_id_c_,
                              callback_context, true) : true;
 }
 
+void ExceptionHandler::AddMappingInfo(const std::string& name,
+                                      const u_int8_t identifier[sizeof(MDGUID)],
+                                      uintptr_t start_address,
+                                      size_t mapping_size,
+                                      size_t file_offset) {
+   MappingInfo info;
+   info.start_addr = start_address;
+   info.size = mapping_size;
+   info.offset = file_offset;
+   strncpy(info.name, name.c_str(), std::min(name.size() + 1, sizeof(info)));
+ 
+   std::pair<MappingInfo, u_int8_t[sizeof(MDGUID)]> mapping;
+   mapping.first = info;
+   memcpy(mapping.second, identifier, sizeof(MDGUID));
+   mapping_info_.push_back(mapping);
+}
+
 }  // 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
@@ -25,25 +25,28 @@
 // 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_
 
+#include <string>
 #include <vector>
-#include <string>
 
 #include <pthread.h>
 #include <signal.h>
+#include <stdint.h>
 #include <stdio.h>
 
 #include "client/linux/android_ucontext.h"
 #include "client/linux/crash_generation/crash_generation_client.h"
+#include "client/linux/minidump_writer/minidump_writer.h"
+#include "google_breakpad/common/minidump_format.h"
 #include "processor/scoped_ptr.h"
 
 struct sigaction;
 
 namespace google_breakpad {
 
 class ExceptionHandler;
 
@@ -202,16 +205,25 @@ class ExceptionHandler {
 #endif
   };
 
   // Returns whether out-of-process dump generation is used or not.
   bool IsOutOfProcess() const {
       return crash_generation_client_.get() != NULL;
   }
 
+  // Add information about a memory mapping. This can be used if
+  // a custom library loader is used that maps things in a way
+  // that the linux dumper can't handle by reading the maps file.
+  void AddMappingInfo(const std::string& name,
+                      const u_int8_t identifier[sizeof(MDGUID)],
+                      uintptr_t start_address,
+                      size_t mapping_size,
+                      size_t file_offset);
+
  private:
   void Init(const std::string &dump_path,
             const int server_fd);
   bool InstallHandlers();
   void UninstallHandlers();
   void PreresolveSymbols();
   bool GenerateDump(CrashContext *context);
   void SendContinueSignalToChild();
@@ -257,13 +269,17 @@ class ExceptionHandler {
   std::vector<std::pair<int, struct sigaction *> > old_handlers_;
 
   // We need to explicitly enable ptrace of parent processes on some
   // kernels, but we need to know the PID of the cloned process before we
   // can do this. We create a pipe which we can use to block the
   // cloned process after creating it, until we have explicitly enabled 
   // ptrace. This is used to store the file descriptors for the pipe
   int fdes[2];
+
+  // Callers can add extra info about mappings for cases where the
+  // dumper code cannot extract enough information from /proc/<pid>/maps.
+  MappingList mapping_info_;
 };
 
 }  // namespace google_breakpad
 
 #endif  // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc
@@ -562,16 +562,97 @@ TEST(ExceptionHandlerTest, InstructionPo
   ASSERT_TRUE(exception);
   ASSERT_TRUE(memory_list);
   ASSERT_EQ((unsigned int)1, memory_list->region_count());
 
   unlink(minidump_filename.c_str());
   free(filename);
 }
 
+static bool SimpleCallback(const char* dump_path,
+                           const char* minidump_id,
+                           void* context,
+                           bool succeeded) {
+  if (!succeeded)
+    return succeeded;
+
+  string* minidump_file = reinterpret_cast<string*>(context);
+  minidump_file->append(dump_path);
+  minidump_file->append("/");
+  minidump_file->append(minidump_id);
+  minidump_file->append(".dmp");
+  return true;
+}
+
+// Test that anonymous memory maps can be annotated with names and IDs.
+TEST(ExceptionHandlerTest, ModuleInfo) {
+  // These are defined here so the parent can use them to check the
+  // data from the minidump afterwards.
+  const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
+  const char* kMemoryName = "a fake module";
+  const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
+    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+    0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
+  };
+  char module_identifier_buffer[37];
+  FileID::ConvertIdentifierToString(kModuleGUID,
+                                    module_identifier_buffer,
+                                    sizeof(module_identifier_buffer));
+  string module_identifier(module_identifier_buffer);
+  // Strip out dashes
+  size_t pos;
+  while ((pos = module_identifier.find('-')) != string::npos) {
+    module_identifier.erase(pos, 1);
+  }
+  // And append a zero, because module IDs include an "age" field
+  // which is always zero on Linux.
+  module_identifier += "0";
+
+  // Get some memory.
+  char* memory =
+    reinterpret_cast<char*>(mmap(NULL,
+                                 kMemorySize,
+                                 PROT_READ | PROT_WRITE,
+                                 MAP_PRIVATE | MAP_ANON,
+                                 -1,
+                                 0));
+  const u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory);
+  ASSERT_TRUE(memory);
+
+  string minidump_filename;
+  ExceptionHandler handler(TEMPDIR, NULL, SimpleCallback,
+                           (void*)&minidump_filename, true);
+  // Add info about the anonymous memory mapping.
+  handler.AddMappingInfo(kMemoryName,
+                         kModuleGUID,
+                         kMemoryAddress,
+                         kMemorySize,
+                         0);
+  handler.WriteMinidump();
+
+  // Read the minidump. Load the module list, and ensure that
+  // the mmap'ed |memory| is listed with the given module name
+  // and debug ID.
+  Minidump minidump(minidump_filename);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpModuleList* module_list = minidump.GetModuleList();
+  ASSERT_TRUE(module_list);
+  const MinidumpModule* module =
+    module_list->GetModuleForAddress(kMemoryAddress);
+  ASSERT_TRUE(module);
+
+  EXPECT_EQ(kMemoryAddress, module->base_address());
+  EXPECT_EQ(kMemorySize, module->size());
+  EXPECT_EQ(kMemoryName, module->code_file());
+  EXPECT_EQ(module_identifier, module->debug_identifier());
+
+  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];
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc
@@ -101,22 +101,22 @@ bool AttachThread(pid_t pid) {
   return true;
 }
 
 bool DetachThread(pid_t pid) {
   return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
 }
 
 inline bool IsMappedFileOpenUnsafe(
-    const google_breakpad::MappingInfo* mapping) {
+    const google_breakpad::MappingInfo& mapping) {
   // It is unsafe to attempt to open a mapped file that lives under /dev,
   // because the semantics of the open may be driver-specific so we'd risk
   // hanging the crash dumper. And a file in /dev/ almost certainly has no
   // ELF file identifier anyways.
-  return my_strncmp(mapping->name,
+  return my_strncmp(mapping.name,
                     kMappedFileUnsafePrefix,
                     sizeof(kMappedFileUnsafePrefix) - 1) == 0;
 }
 
 bool GetThreadRegisters(ThreadInfo* info) {
   pid_t tid = info->tid;
 
   if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
@@ -232,26 +232,24 @@ LinuxDumper::BuildProcPath(char* path, p
   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);
 }
 
 bool
-LinuxDumper::ElfFileIdentifierForMapping(unsigned int mapping_id,
+LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
                                          uint8_t identifier[sizeof(MDGUID)])
 {
-  assert(mapping_id < mappings_.size());
   my_memset(identifier, 0, sizeof(MDGUID));
-  const MappingInfo* mapping = mappings_[mapping_id];
   if (IsMappedFileOpenUnsafe(mapping)) {
     return false;
   }
-  int fd = sys_open(mapping->name, O_RDONLY, 0);
+  int fd = sys_open(mapping.name, O_RDONLY, 0);
   if (fd < 0)
     return false;
   struct kernel_stat st;
   if (sys_fstat(fd, &st) != 0) {
     sys_close(fd);
     return false;
   }
 #if defined(__x86_64)
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h
@@ -158,17 +158,17 @@ class LinuxDumper {
                               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;
 
   // Generate a File ID from the .text section of a mapped entry
-  bool ElfFileIdentifierForMapping(unsigned int mapping_id,
+  bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
                                    uint8_t identifier[sizeof(MDGUID)]);
 
   // 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;
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc
@@ -217,17 +217,17 @@ TEST(LinuxDumperTest, FileIDsMatch) {
       found_exe = true;
       break;
     }
   }
   ASSERT_TRUE(found_exe);
 
   uint8_t identifier1[sizeof(MDGUID)];
   uint8_t identifier2[sizeof(MDGUID)];
-  EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(i, identifier1));
+  EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], identifier1));
   FileID fileid(exe_name);
   EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
   char identifier_string1[37];
   char identifier_string2[37];
   FileID::ConvertIdentifierToString(identifier1, identifier_string1,
                                     37);
   FileID::ConvertIdentifierToString(identifier2, identifier_string2,
                                     37);
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc
@@ -411,30 +411,32 @@ namespace google_breakpad {
 // from the signal handler.  In case (2), we have to extract it using
 // ptrace, and we can't assume that crashing_tid_ still exists (or
 // ever did).
 class MinidumpWriter {
  public:
   // case (1) above
   MinidumpWriter(const char* filename,
                  pid_t crashing_pid,
-                 const ExceptionHandler::CrashContext* context)
+                 const ExceptionHandler::CrashContext* context,
+                 const MappingList& mappings)
       : filename_(filename),
         siginfo_(&context->siginfo),
         ucontext_(&context->context),
 #if !defined(__ARM_EABI__)
         float_state_(&context->float_state),
 #else
         //TODO: fix this after fixing ExceptionHandler
         float_state_(NULL),
 #endif
         crashing_tid_(context->tid),
         crashing_tid_pc_(0),
         dumper_(crashing_pid),
-        memory_blocks_(dumper_.allocator()) {
+        memory_blocks_(dumper_.allocator()),
+        mapping_info_(mappings) {
   }
 
   // case (2) above
   MinidumpWriter(const char* filename,
                  pid_t pid,
                  pid_t blame_thread)
       : filename_(filename),
         siginfo_(NULL),         // we fill this in if we find blame_thread
@@ -824,90 +826,133 @@ class MinidumpWriter {
         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;
   }
 
+  // If there is caller-provided information about this mapping
+  // in the mapping_info_ list, return true. Otherwise, return false.
+  bool HaveMappingInfo(const MappingInfo& mapping) {
+    for (MappingList::const_iterator iter = mapping_info_.begin();
+         iter != mapping_info_.end();
+         ++iter) {
+      if (iter->first.start_addr == mapping.start_addr &&
+          iter->first.size == mapping.size) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   // 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;
+    unsigned num_output_mappings = mapping_info_.size();
 
     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) {
+    // First write all the mappings from the dumper
+    unsigned int j = 0;
+    for (unsigned i = 0; i < num_mappings; ++i) {
       const MappingInfo& mapping = *dumper_.mappings()[i];
-      if (!ShouldIncludeMapping(mapping))
+      if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(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))
+      if (!FillRawModule(mapping, mod, NULL))
         return false;
-
-      const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
-      memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
-      cv_ptr += sizeof(cv_signature);
-      uint8_t* signature = cv_ptr;
-      cv_ptr += sizeof(MDGUID);
-      dumper_.ElfFileIdentifierForMapping(i, signature);
-      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);
     }
+    // Next write all the mappings provided by the caller
+    for (MappingList::const_iterator iter = mapping_info_.begin();
+         iter != mapping_info_.end();
+         ++iter) {
+      MDRawModule mod;
+      if (!FillRawModule(iter->first, mod, iter->second))
+          return false;
+        list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
+    }
 
     return true;
   }
 
+  // Fill the MDRawModule mod with information about the provided
+  // mapping. If identifier is non-NULL, use it instead of calculating
+  // a file ID from the mapping.
+  bool FillRawModule(const MappingInfo& mapping,
+                     MDRawModule& mod,
+                     const u_int8_t* identifier) {
+    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++;
+
+    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);
+    uint8_t* signature = cv_ptr;
+    cv_ptr += sizeof(MDGUID);
+    if (identifier) {
+      // GUID was provided by caller.
+      memcpy(signature, identifier, sizeof(MDGUID));
+    } else {
+      dumper_.ElfFileIdentifierForMapping(mapping, signature);
+    }
+    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;
+    return true;
+  }
+
   bool WriteMemoryListStream(MDRawDirectory* dirent) {
     TypedMDRVA<uint32_t> list(&minidump_writer_);
     if (!list.AllocateObjectAndArray(memory_blocks_.size(),
                                      sizeof(MDMemoryDescriptor)))
       return false;
 
     dirent->stream_type = MD_MEMORY_LIST_STREAM;
     dirent->location = list.location();
@@ -1304,25 +1349,34 @@ popline:
                               // use siginfo_)
   LinuxDumper dumper_;
   MinidumpFileWriter minidump_writer_;
   MDLocationDescriptor crashing_thread_context_;
   // Blocks of memory written to the dump. These are all currently
   // written while writing the thread list stream, but saved here
   // so a memory list stream can be written afterwards.
   wasteful_vector<MDMemoryDescriptor> memory_blocks_;
+  // Additional information about some mappings provided by the caller.
+  const MappingList& mapping_info_;
 };
 
 bool WriteMinidump(const char* filename, pid_t crashing_process,
                    const void* blob, size_t blob_size) {
+  MappingList m;
+  return WriteMinidump(filename, crashing_process, blob, blob_size, m);
+}
+
+bool WriteMinidump(const char* filename, pid_t crashing_process,
+                   const void* blob, size_t blob_size,
+                   const MappingList& mappings) {
   if (blob_size != sizeof(ExceptionHandler::CrashContext))
     return false;
   const ExceptionHandler::CrashContext* context =
       reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
-  MinidumpWriter writer(filename, crashing_process, context);
+  MinidumpWriter writer(filename, crashing_process, context, mappings);
   if (!writer.Init())
     return false;
   return writer.Dump();
 }
 
 bool WriteMinidump(const char* filename, pid_t process,
                    pid_t process_blamed_thread) {
   MinidumpWriter writer(filename, process, process_blamed_thread);
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h
@@ -25,21 +25,30 @@
 // 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 <list>
+#include <utility>
+
 #include <stdint.h>
 #include <unistd.h>
 
+#include "google_breakpad/common/minidump_format.h"
+
 namespace google_breakpad {
 
+// A list of <MappingInfo, GUID>
+typedef std::pair<struct MappingInfo, u_int8_t[sizeof(MDGUID)]> MappingEntry;
+typedef std::list<MappingEntry> MappingList;
+
 // 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
@@ -51,11 +60,16 @@ bool WriteMinidump(const char* filename,
 // Alternate form of WriteMinidump() that works with processes that
 // are not expected to have crashed.  If |process_blamed_thread| is
 // meaningful, it will be the one from which a crash signature is
 // extracted.  It is not expected that this function will be called
 // from a compromised context, but it is safe to do so.
 bool WriteMinidump(const char* filename, pid_t process,
                    pid_t process_blamed_thread);
 
+// This overload also allows passing a list of known mappings.
+bool WriteMinidump(const char* filename, pid_t crashing_process,
+                   const void* blob, size_t blob_size,
+                   const MappingList& mappings);
+
 }  // namespace google_breakpad
 
 #endif  // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc
@@ -63,8 +63,96 @@ TEST(MinidumpWriterTest, Setup) {
   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]);
 }
+
+TEST(MinidumpWriterTest, MappingInfo) {
+  int fds[2];
+  ASSERT_NE(-1, pipe(fds));
+
+  // These are defined here so the parent can use them to check the
+  // data from the minidump afterwards.
+  const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
+  const char* kMemoryName = "a fake module";
+  const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
+    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+    0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
+  };
+  char module_identifier_buffer[37];
+  FileID::ConvertIdentifierToString(kModuleGUID,
+                                    module_identifier_buffer,
+                                    sizeof(module_identifier_buffer));
+  string module_identifier(module_identifier_buffer);
+  // Strip out dashes
+  size_t pos;
+  while ((pos = module_identifier.find('-')) != string::npos) {
+    module_identifier.erase(pos, 1);
+  }
+  // And append a zero, because module IDs include an "age" field
+  // which is always zero on Linux.
+  module_identifier += "0";
+  
+  // Get some memory.
+  char* memory =
+    reinterpret_cast<char*>(mmap(NULL,
+                                 kMemorySize,
+                                 PROT_READ | PROT_WRITE,
+                                 MAP_PRIVATE | MAP_ANON,
+                                 -1,
+                                 0));
+  const u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory);
+  ASSERT_TRUE(memory);
+
+  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[] = TEMPDIR "/minidump-writer-unittest-XXXXXX";
+  mktemp(templ);
+
+  // Add information about the mapped memory.
+  MappingInfo info;
+  info.start_addr = kMemoryAddress;
+  info.size = kMemorySize;
+  info.offset = 0;
+  strcpy(info.name, kMemoryName);
+  
+  MappingList mappings;
+  std::pair<MappingInfo, u_int8_t[sizeof(MDGUID)]> mapping;
+  mapping.first = info;
+  memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
+  mappings.push_back(mapping);
+  ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context), mappings));
+
+  // Read the minidump. Load the module list, and ensure that
+  // the mmap'ed |memory| is listed with the given module name
+  // and debug ID.
+  Minidump minidump(templ);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpModuleList* module_list = minidump.GetModuleList();
+  ASSERT_TRUE(module_list);
+  const MinidumpModule* module =
+    module_list->GetModuleForAddress(kMemoryAddress);
+  ASSERT_TRUE(module);
+
+  EXPECT_EQ(kMemoryAddress, module->base_address());
+  EXPECT_EQ(kMemorySize, module->size());
+  EXPECT_EQ(kMemoryName, module->code_file());
+  EXPECT_EQ(module_identifier, module->debug_identifier());
+
+  unlink(templ);
+  close(fds[1]);
+}