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