Home | History | Annotate | Download | only in browser
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/breakpad/browser/crash_dump_manager_android.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/file_util.h"
      9 #include "base/format_macros.h"
     10 #include "base/logging.h"
     11 #include "base/posix/global_descriptors.h"
     12 #include "base/process/process.h"
     13 #include "base/rand_util.h"
     14 #include "base/stl_util.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "content/public/browser/child_process_data.h"
     18 #include "content/public/browser/file_descriptor_info.h"
     19 #include "content/public/browser/notification_service.h"
     20 #include "content/public/browser/notification_types.h"
     21 #include "content/public/browser/render_process_host.h"
     22 
     23 using content::BrowserThread;
     24 
     25 namespace breakpad {
     26 
     27 // static
     28 CrashDumpManager* CrashDumpManager::instance_ = NULL;
     29 
     30 // static
     31 CrashDumpManager* CrashDumpManager::GetInstance() {
     32   return instance_;
     33 }
     34 
     35 CrashDumpManager::CrashDumpManager(const base::FilePath& crash_dump_dir)
     36     : crash_dump_dir_(crash_dump_dir) {
     37   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     38   DCHECK(!instance_);
     39 
     40   instance_ = this;
     41 
     42   notification_registrar_.Add(this,
     43                               content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
     44                               content::NotificationService::AllSources());
     45   notification_registrar_.Add(this,
     46                               content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
     47                               content::NotificationService::AllSources());
     48 
     49   BrowserChildProcessObserver::Add(this);
     50 }
     51 
     52 CrashDumpManager::~CrashDumpManager() {
     53   instance_ = NULL;
     54 
     55   BrowserChildProcessObserver::Remove(this);
     56 }
     57 
     58 int CrashDumpManager::CreateMinidumpFile(int child_process_id) {
     59   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER));
     60   base::FilePath minidump_path;
     61   if (!base::CreateTemporaryFile(&minidump_path))
     62     return base::kInvalidPlatformFileValue;
     63 
     64   base::PlatformFileError error;
     65   // We need read permission as the minidump is generated in several phases
     66   // and needs to be read at some point.
     67   int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ |
     68       base::PLATFORM_FILE_WRITE;
     69   base::PlatformFile minidump_file =
     70       base::CreatePlatformFile(minidump_path, flags, NULL, &error);
     71   if (minidump_file == base::kInvalidPlatformFileValue) {
     72     LOG(ERROR) << "Failed to create temporary file, crash won't be reported.";
     73     return base::kInvalidPlatformFileValue;
     74   }
     75 
     76   {
     77     base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_);
     78     DCHECK(!ContainsKey(child_process_id_to_minidump_path_, child_process_id));
     79     child_process_id_to_minidump_path_[child_process_id] = minidump_path;
     80   }
     81   return minidump_file;
     82 }
     83 
     84 // static
     85 void CrashDumpManager::ProcessMinidump(const base::FilePath& minidump_path,
     86                                        base::ProcessHandle pid) {
     87   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     88   int64 file_size = 0;
     89   int r = base::GetFileSize(minidump_path, &file_size);
     90   DCHECK(r) << "Failed to retrieve size for minidump "
     91             << minidump_path.value();
     92 
     93   if (file_size == 0) {
     94     // Empty minidump, this process did not crash. Just remove the file.
     95     r = base::DeleteFile(minidump_path, false);
     96     DCHECK(r) << "Failed to delete temporary minidump file "
     97               << minidump_path.value();
     98     return;
     99   }
    100 
    101   // We are dealing with a valid minidump. Copy it to the crash report
    102   // directory from where Java code will upload it later on.
    103   if (instance_->crash_dump_dir_.empty()) {
    104     NOTREACHED() << "Failed to retrieve the crash dump directory.";
    105     return;
    106   }
    107   const uint64 rand = base::RandUint64();
    108   const std::string filename =
    109       base::StringPrintf("chromium-renderer-minidump-%016" PRIx64 ".dmp%d",
    110                          rand, pid);
    111   base::FilePath dest_path = instance_->crash_dump_dir_.Append(filename);
    112   r = base::Move(minidump_path, dest_path);
    113   if (!r) {
    114     LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value()
    115                << " to " << dest_path.value();
    116     base::DeleteFile(minidump_path, false);
    117     return;
    118   }
    119   LOG(INFO) << "Crash minidump successfully generated: " <<
    120       instance_->crash_dump_dir_.Append(filename).value();
    121 }
    122 
    123 void CrashDumpManager::BrowserChildProcessHostDisconnected(
    124     const content::ChildProcessData& data) {
    125   OnChildExit(data.id, data.handle);
    126 }
    127 
    128 void CrashDumpManager::BrowserChildProcessCrashed(
    129     const content::ChildProcessData& data) {
    130   OnChildExit(data.id, data.handle);
    131 }
    132 
    133 void CrashDumpManager::Observe(int type,
    134                                const content::NotificationSource& source,
    135                                const content::NotificationDetails& details) {
    136   switch (type) {
    137     case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
    138       // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer
    139       // process is cleanly shutdown. However, we need to fallthrough so that
    140       // we close the minidump_fd we kept open.
    141     case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
    142       content::RenderProcessHost* rph =
    143           content::Source<content::RenderProcessHost>(source).ptr();
    144       OnChildExit(rph->GetID(), rph->GetHandle());
    145       break;
    146     }
    147     default:
    148       NOTREACHED();
    149       return;
    150   }
    151 }
    152 
    153 void CrashDumpManager::OnChildExit(int child_process_id,
    154                                    base::ProcessHandle pid) {
    155   base::FilePath minidump_path;
    156   {
    157     base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_);
    158     ChildProcessIDToMinidumpPath::iterator iter =
    159         child_process_id_to_minidump_path_.find(child_process_id);
    160     if (iter == child_process_id_to_minidump_path_.end()) {
    161       // We might get a NOTIFICATION_RENDERER_PROCESS_TERMINATED and a
    162       // NOTIFICATION_RENDERER_PROCESS_CLOSED.
    163       return;
    164     }
    165     minidump_path = iter->second;
    166     child_process_id_to_minidump_path_.erase(iter);
    167   }
    168   BrowserThread::PostTask(
    169       BrowserThread::FILE, FROM_HERE,
    170       base::Bind(&CrashDumpManager::ProcessMinidump, minidump_path, pid));
    171 }
    172 
    173 }  // namespace breakpad
    174