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 base::File 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::File(); 63 64 // We need read permission as the minidump is generated in several phases 65 // and needs to be read at some point. 66 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ | 67 base::File::FLAG_WRITE; 68 base::File minidump_file(minidump_path, flags); 69 if (!minidump_file.IsValid()) { 70 LOG(ERROR) << "Failed to create temporary file, crash won't be reported."; 71 return base::File(); 72 } 73 74 { 75 base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_); 76 DCHECK(!ContainsKey(child_process_id_to_minidump_path_, child_process_id)); 77 child_process_id_to_minidump_path_[child_process_id] = minidump_path; 78 } 79 return minidump_file.Pass(); 80 } 81 82 // static 83 void CrashDumpManager::ProcessMinidump(const base::FilePath& minidump_path, 84 base::ProcessHandle pid) { 85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 86 int64 file_size = 0; 87 int r = base::GetFileSize(minidump_path, &file_size); 88 DCHECK(r) << "Failed to retrieve size for minidump " 89 << minidump_path.value(); 90 91 if (file_size == 0) { 92 // Empty minidump, this process did not crash. Just remove the file. 93 r = base::DeleteFile(minidump_path, false); 94 DCHECK(r) << "Failed to delete temporary minidump file " 95 << minidump_path.value(); 96 return; 97 } 98 99 // We are dealing with a valid minidump. Copy it to the crash report 100 // directory from where Java code will upload it later on. 101 if (instance_->crash_dump_dir_.empty()) { 102 NOTREACHED() << "Failed to retrieve the crash dump directory."; 103 return; 104 } 105 const uint64 rand = base::RandUint64(); 106 const std::string filename = 107 base::StringPrintf("chromium-renderer-minidump-%016" PRIx64 ".dmp%d", 108 rand, pid); 109 base::FilePath dest_path = instance_->crash_dump_dir_.Append(filename); 110 r = base::Move(minidump_path, dest_path); 111 if (!r) { 112 LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value() 113 << " to " << dest_path.value(); 114 base::DeleteFile(minidump_path, false); 115 return; 116 } 117 VLOG(1) << "Crash minidump successfully generated: " << 118 instance_->crash_dump_dir_.Append(filename).value(); 119 } 120 121 void CrashDumpManager::BrowserChildProcessHostDisconnected( 122 const content::ChildProcessData& data) { 123 OnChildExit(data.id, data.handle); 124 } 125 126 void CrashDumpManager::BrowserChildProcessCrashed( 127 const content::ChildProcessData& data) { 128 OnChildExit(data.id, data.handle); 129 } 130 131 void CrashDumpManager::Observe(int type, 132 const content::NotificationSource& source, 133 const content::NotificationDetails& details) { 134 switch (type) { 135 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: 136 // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer 137 // process is cleanly shutdown. However, we need to fallthrough so that 138 // we close the minidump_fd we kept open. 139 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { 140 content::RenderProcessHost* rph = 141 content::Source<content::RenderProcessHost>(source).ptr(); 142 OnChildExit(rph->GetID(), rph->GetHandle()); 143 break; 144 } 145 default: 146 NOTREACHED(); 147 return; 148 } 149 } 150 151 void CrashDumpManager::OnChildExit(int child_process_id, 152 base::ProcessHandle pid) { 153 base::FilePath minidump_path; 154 { 155 base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_); 156 ChildProcessIDToMinidumpPath::iterator iter = 157 child_process_id_to_minidump_path_.find(child_process_id); 158 if (iter == child_process_id_to_minidump_path_.end()) { 159 // We might get a NOTIFICATION_RENDERER_PROCESS_TERMINATED and a 160 // NOTIFICATION_RENDERER_PROCESS_CLOSED. 161 return; 162 } 163 minidump_path = iter->second; 164 child_process_id_to_minidump_path_.erase(iter); 165 } 166 BrowserThread::PostTask( 167 BrowserThread::FILE, FROM_HERE, 168 base::Bind(&CrashDumpManager::ProcessMinidump, minidump_path, pid)); 169 } 170 171 } // namespace breakpad 172