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