Home | History | Annotate | Download | only in android
      1 // Copyright (c) 2012 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 "chrome/browser/android/crash_dump_manager.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/path_service.h"
     12 #include "base/posix/global_descriptors.h"
     13 #include "base/process/process.h"
     14 #include "base/rand_util.h"
     15 #include "base/stl_util.h"
     16 #include "base/strings/stringprintf.h"
     17 #include "chrome/common/chrome_paths.h"
     18 #include "chrome/common/descriptors_android.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "content/public/browser/child_process_data.h"
     21 #include "content/public/browser/file_descriptor_info.h"
     22 #include "content/public/browser/notification_service.h"
     23 #include "content/public/browser/notification_types.h"
     24 #include "content/public/browser/render_process_host.h"
     25 
     26 using content::BrowserThread;
     27 
     28 // static
     29 CrashDumpManager* CrashDumpManager::instance_ = NULL;
     30 
     31 // static
     32 CrashDumpManager* CrashDumpManager::GetInstance() {
     33   return instance_;
     34 }
     35 
     36 CrashDumpManager::CrashDumpManager() {
     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 (!file_util::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 void CrashDumpManager::ProcessMinidump(const base::FilePath& minidump_path,
     85                                        base::ProcessHandle pid) {
     86   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     87   int64 file_size = 0;
     88   int r = file_util::GetFileSize(minidump_path, &file_size);
     89   DCHECK(r) << "Failed to retrieve size for minidump "
     90             << minidump_path.value();
     91 
     92   if (file_size == 0) {
     93     // Empty minidump, this process did not crash. Just remove the file.
     94     r = base::DeleteFile(minidump_path, false);
     95     DCHECK(r) << "Failed to delete temporary minidump file "
     96               << minidump_path.value();
     97     return;
     98   }
     99 
    100   // We are dealing with a valid minidump. Copy it to the crash report
    101   // directory from where Java code will upload it later on.
    102   base::FilePath crash_dump_dir;
    103   r = PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dump_dir);
    104   if (!r) {
    105     NOTREACHED() << "Failed to retrieve the crash dump directory.";
    106     return;
    107   }
    108 
    109   const uint64 rand = base::RandUint64();
    110   const std::string filename =
    111       base::StringPrintf("chromium-renderer-minidump-%016" PRIx64 ".dmp%d",
    112                          rand, pid);
    113   base::FilePath dest_path = crash_dump_dir.Append(filename);
    114   r = base::Move(minidump_path, dest_path);
    115   if (!r) {
    116     LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value()
    117                << " to " << dest_path.value();
    118     base::DeleteFile(minidump_path, false);
    119     return;
    120   }
    121   LOG(INFO) << "Crash minidump successfully generated: " <<
    122       crash_dump_dir.Append(filename).value();
    123 }
    124 
    125 void CrashDumpManager::BrowserChildProcessHostDisconnected(
    126     const content::ChildProcessData& data) {
    127   OnChildExit(data.id, data.handle);
    128 }
    129 
    130 void CrashDumpManager::BrowserChildProcessCrashed(
    131     const content::ChildProcessData& data) {
    132   OnChildExit(data.id, data.handle);
    133 }
    134 
    135 void CrashDumpManager::Observe(int type,
    136                                const content::NotificationSource& source,
    137                                const content::NotificationDetails& details) {
    138   switch (type) {
    139     case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
    140       // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer
    141       // process is cleanly shutdown. However, we need to fallthrough so that
    142       // we close the minidump_fd we kept open.
    143     case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
    144       content::RenderProcessHost* rph =
    145           content::Source<content::RenderProcessHost>(source).ptr();
    146       OnChildExit(rph->GetID(), rph->GetHandle());
    147       break;
    148     }
    149     default:
    150       NOTREACHED();
    151       return;
    152   }
    153 }
    154 
    155 void CrashDumpManager::OnChildExit(int child_process_id,
    156                                    base::ProcessHandle pid) {
    157   base::FilePath minidump_path;
    158   {
    159     base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_);
    160     ChildProcessIDToMinidumpPath::iterator iter =
    161         child_process_id_to_minidump_path_.find(child_process_id);
    162     if (iter == child_process_id_to_minidump_path_.end()) {
    163       // We might get a NOTIFICATION_RENDERER_PROCESS_TERMINATED and a
    164       // NOTIFICATION_RENDERER_PROCESS_CLOSED.
    165       return;
    166     }
    167     minidump_path = iter->second;
    168     child_process_id_to_minidump_path_.erase(iter);
    169   }
    170   BrowserThread::PostTask(
    171       BrowserThread::FILE, FROM_HERE,
    172       base::Bind(&CrashDumpManager::ProcessMinidump, minidump_path, pid));
    173 }
    174