OLD | NEW |
(Empty) | |
| 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 <inttypes.h> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/file_util.h" |
| 11 #include "base/global_descriptors_posix.h" |
| 12 #include "base/logging.h" |
| 13 #include "base/path_service.h" |
| 14 #include "base/process.h" |
| 15 #include "base/rand_util.h" |
| 16 #include "base/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/file_descriptor_info.h" |
| 21 #include "content/public/browser/notification_service.h" |
| 22 #include "content/public/browser/notification_types.h" |
| 23 #include "content/public/browser/render_process_host.h" |
| 24 |
| 25 using content::BrowserThread; |
| 26 |
| 27 CrashDumpManager::CrashDumpManager() { |
| 28 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 29 notification_registrar_.Add(this, |
| 30 content::NOTIFICATION_RENDERER_PROCESS_CREATED, |
| 31 content::NotificationService::AllSources()); |
| 32 notification_registrar_.Add(this, |
| 33 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
| 34 content::NotificationService::AllSources()); |
| 35 } |
| 36 |
| 37 CrashDumpManager::~CrashDumpManager() { |
| 38 } |
| 39 |
| 40 int CrashDumpManager::CreateMinidumpFile() { |
| 41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)); |
| 42 FilePath minidump_path; |
| 43 if (!file_util::CreateTemporaryFile(&minidump_path)) |
| 44 return base::kInvalidPlatformFileValue; |
| 45 |
| 46 base::PlatformFileError error; |
| 47 // We need read permission as the minidump is generated in several phases |
| 48 // and needs to be read at some point. |
| 49 int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ | |
| 50 base::PLATFORM_FILE_WRITE; |
| 51 base::PlatformFile f = |
| 52 base::CreatePlatformFile(minidump_path, flags, NULL, &error); |
| 53 if (f == base::kInvalidPlatformFileValue) { |
| 54 LOG(ERROR) << "Failed to create temporary file, crash won't be reported."; |
| 55 return base::kInvalidPlatformFileValue; |
| 56 } |
| 57 |
| 58 // Associate that file descriptor with its path, we'll need the path when the |
| 59 // process terminates. |
| 60 { |
| 61 // WARNING: the UI process may be blocked on this lock. We should release as |
| 62 // soon as possible and cannot do any IO from here. |
| 63 base::AutoLock auto_lock(file_to_path_lock_); |
| 64 file_to_path_[f] = minidump_path; |
| 65 } |
| 66 |
| 67 return f; |
| 68 } |
| 69 |
| 70 void CrashDumpManager::ProcessMinidump(const MinidumpInfo& minidump) { |
| 71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 72 // Close the file descriptor, it is still open. |
| 73 bool r = base::ClosePlatformFile(minidump.file); |
| 74 DCHECK(r) << "Failed to close minidump file descriptor."; |
| 75 |
| 76 int64 file_size = 0; |
| 77 r = file_util::GetFileSize(minidump.path, &file_size); |
| 78 DCHECK(r) << "Failed to retrieve size for minidump " << |
| 79 minidump.path.value(); |
| 80 |
| 81 if (file_size == 0) { |
| 82 // Empty minidump, this process did not crash. Just remove the file. |
| 83 r = file_util::Delete(minidump.path, false); |
| 84 DCHECK(r) << "Failed to delete temporary minidump file " << |
| 85 minidump.path.value(); |
| 86 return; |
| 87 } |
| 88 |
| 89 // We are dealing with a valid minidump. Copy it to the crash report |
| 90 // directory from where Java code will upload it later on. |
| 91 FilePath crash_dump_dir; |
| 92 r = PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dump_dir); |
| 93 if (!r) { |
| 94 NOTREACHED() << "Failed to retrieve the crash dump directory."; |
| 95 return; |
| 96 } |
| 97 |
| 98 const uint64 rand = base::RandUint64(); |
| 99 const std::string filename = |
| 100 base::StringPrintf("chromium-renderer-minidump-%016" PRIx64 ".dmp%d", |
| 101 rand, minidump.pid); |
| 102 FilePath dest_path = crash_dump_dir.Append(filename); |
| 103 r = file_util::Move(minidump.path, dest_path); |
| 104 if (!r) { |
| 105 LOG(ERROR) << "Failed to move crash dump from " << minidump.path.value() << |
| 106 " to " << dest_path.value(); |
| 107 file_util::Delete(minidump.path, false); |
| 108 return; |
| 109 } |
| 110 LOG(INFO) << "Crash minidump successfully generated: " << |
| 111 crash_dump_dir.Append(filename).value(); |
| 112 } |
| 113 |
| 114 void CrashDumpManager::Observe(int type, |
| 115 const content::NotificationSource& source, |
| 116 const content::NotificationDetails& details) { |
| 117 switch (type) { |
| 118 case content::NOTIFICATION_RENDERER_PROCESS_CREATED: { |
| 119 content::RenderProcessHost* rph = |
| 120 content::Source<content::RenderProcessHost>(source).ptr(); |
| 121 base::PlatformFile minidump_fd = base::kInvalidPlatformFileValue; |
| 122 const content::FDInfoList* mapped_files = |
| 123 content::Details<content::FDInfoList>(details).ptr(); |
| 124 for (content::FDInfoList::const_iterator iter = mapped_files->begin(); |
| 125 iter != mapped_files->end(); ++iter) { |
| 126 if (iter->id == kAndroidMinidumpDescriptor) { |
| 127 minidump_fd = iter->fd.fd; |
| 128 break; |
| 129 } |
| 130 } |
| 131 |
| 132 FilePath minidump_path; |
| 133 { |
| 134 base::AutoLock auto_lock(file_to_path_lock_); |
| 135 FileToPath::iterator iter = file_to_path_.find(minidump_fd); |
| 136 if (iter == file_to_path_.end()) { |
| 137 LOG(ERROR) << "Failed to find the path of minidump FD."; |
| 138 return; |
| 139 } |
| 140 minidump_path = iter->second; |
| 141 file_to_path_.erase(iter); |
| 142 } |
| 143 MinidumpInfo minidump_info; |
| 144 minidump_info.file = minidump_fd; |
| 145 minidump_info.path = minidump_path; |
| 146 base::Process child_process(rph->GetHandle()); |
| 147 minidump_info.pid = child_process.pid(); |
| 148 rph_to_minidump_info_[rph] = minidump_info; |
| 149 break; |
| 150 } |
| 151 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { |
| 152 content::RenderProcessHost* rph = |
| 153 content::Source<content::RenderProcessHost>(source).ptr(); |
| 154 RPHToMinidumpInfo::iterator iter = rph_to_minidump_info_.find(rph); |
| 155 if (iter == rph_to_minidump_info_.end()) { |
| 156 LOG(ERROR) << "Minidump not found for closed process."; |
| 157 return; |
| 158 } |
| 159 BrowserThread::PostTask( |
| 160 BrowserThread::IO, FROM_HERE, |
| 161 base::Bind(&ProcessMinidump, iter->second)); |
| 162 rph_to_minidump_info_.erase(iter); |
| 163 break; |
| 164 } |
| 165 default: |
| 166 NOTREACHED(); |
| 167 break; |
| 168 } |
| 169 } |
OLD | NEW |