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 "base/bind.h" |
| 8 #include "base/file_util.h" |
| 9 #include "base/format_macros.h" |
| 10 #include "base/global_descriptors_posix.h" |
| 11 #include "base/logging.h" |
| 12 #include "base/path_service.h" |
| 13 #include "base/process.h" |
| 14 #include "base/rand_util.h" |
| 15 #include "base/stl_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/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 CrashDumpManager::CrashDumpManager() { |
| 29 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 30 notification_registrar_.Add(this, |
| 31 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
| 32 content::NotificationService::AllSources()); |
| 33 notification_registrar_.Add(this, |
| 34 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
| 35 content::NotificationService::AllSources()); |
| 36 notification_registrar_.Add(this, |
| 37 content::NOTIFICATION_CHILD_PROCESS_HOST_DISCONNECTED, |
| 38 content::NotificationService::AllSources()); |
| 39 notification_registrar_.Add(this, |
| 40 content::NOTIFICATION_CHILD_PROCESS_CRASHED, |
| 41 content::NotificationService::AllSources()); |
| 42 } |
| 43 |
| 44 CrashDumpManager::~CrashDumpManager() { |
| 45 } |
| 46 |
| 47 int CrashDumpManager::CreateMinidumpFile(int child_process_id) { |
| 48 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)); |
| 49 FilePath minidump_path; |
| 50 if (!file_util::CreateTemporaryFile(&minidump_path)) |
| 51 return base::kInvalidPlatformFileValue; |
| 52 |
| 53 base::PlatformFileError error; |
| 54 // We need read permission as the minidump is generated in several phases |
| 55 // and needs to be read at some point. |
| 56 int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ | |
| 57 base::PLATFORM_FILE_WRITE; |
| 58 base::PlatformFile minidump_file = |
| 59 base::CreatePlatformFile(minidump_path, flags, NULL, &error); |
| 60 if (minidump_file == base::kInvalidPlatformFileValue) { |
| 61 LOG(ERROR) << "Failed to create temporary file, crash won't be reported."; |
| 62 return base::kInvalidPlatformFileValue; |
| 63 } |
| 64 |
| 65 MinidumpInfo minidump_info; |
| 66 minidump_info.file = minidump_file; |
| 67 minidump_info.path = minidump_path; |
| 68 { |
| 69 base::AutoLock auto_lock(child_process_id_to_minidump_info_lock_); |
| 70 DCHECK(!ContainsKey(child_process_id_to_minidump_info_, child_process_id)); |
| 71 child_process_id_to_minidump_info_[child_process_id] = minidump_info; |
| 72 } |
| 73 return minidump_file; |
| 74 } |
| 75 |
| 76 void CrashDumpManager::ProcessMinidump(const MinidumpInfo& minidump) { |
| 77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 78 // Close the file descriptor, it is still open. |
| 79 bool r = base::ClosePlatformFile(minidump.file); |
| 80 DCHECK(r) << "Failed to close minidump file descriptor."; |
| 81 |
| 82 int64 file_size = 0; |
| 83 r = file_util::GetFileSize(minidump.path, &file_size); |
| 84 DCHECK(r) << "Failed to retrieve size for minidump " |
| 85 << minidump.path.value(); |
| 86 |
| 87 if (file_size == 0) { |
| 88 // Empty minidump, this process did not crash. Just remove the file. |
| 89 r = file_util::Delete(minidump.path, false); |
| 90 DCHECK(r) << "Failed to delete temporary minidump file " |
| 91 << minidump.path.value(); |
| 92 return; |
| 93 } |
| 94 |
| 95 // We are dealing with a valid minidump. Copy it to the crash report |
| 96 // directory from where Java code will upload it later on. |
| 97 FilePath crash_dump_dir; |
| 98 r = PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dump_dir); |
| 99 if (!r) { |
| 100 NOTREACHED() << "Failed to retrieve the crash dump directory."; |
| 101 return; |
| 102 } |
| 103 |
| 104 const uint64 rand = base::RandUint64(); |
| 105 const std::string filename = |
| 106 base::StringPrintf("chromium-renderer-minidump-%016" PRIx64 ".dmp%d", |
| 107 rand, minidump.pid); |
| 108 FilePath dest_path = crash_dump_dir.Append(filename); |
| 109 r = file_util::Move(minidump.path, dest_path); |
| 110 if (!r) { |
| 111 LOG(ERROR) << "Failed to move crash dump from " << minidump.path.value() |
| 112 << " to " << dest_path.value(); |
| 113 file_util::Delete(minidump.path, false); |
| 114 return; |
| 115 } |
| 116 LOG(INFO) << "Crash minidump successfully generated: " << |
| 117 crash_dump_dir.Append(filename).value(); |
| 118 } |
| 119 |
| 120 void CrashDumpManager::Observe(int type, |
| 121 const content::NotificationSource& source, |
| 122 const content::NotificationDetails& details) { |
| 123 int child_process_id; |
| 124 switch (type) { |
| 125 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: |
| 126 // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer |
| 127 // process is cleanly shutdown. However, we need to fallthrough so that |
| 128 // we close the minidump_fd we kept open. |
| 129 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { |
| 130 content::RenderProcessHost* rph = |
| 131 content::Source<content::RenderProcessHost>(source).ptr(); |
| 132 child_process_id = rph->GetID(); |
| 133 break; |
| 134 } |
| 135 case content::NOTIFICATION_CHILD_PROCESS_CRASHED: |
| 136 case content::NOTIFICATION_CHILD_PROCESS_HOST_DISCONNECTED: { |
| 137 content::ChildProcessData* child_process_data = |
| 138 content::Details<content::ChildProcessData>(details).ptr(); |
| 139 child_process_id = child_process_data->id; |
| 140 break; |
| 141 } |
| 142 default: |
| 143 NOTREACHED(); |
| 144 return; |
| 145 } |
| 146 MinidumpInfo minidump_info; |
| 147 { |
| 148 base::AutoLock auto_lock(child_process_id_to_minidump_info_lock_); |
| 149 ChildProcessIDToMinidumpInfo::iterator iter = |
| 150 child_process_id_to_minidump_info_.find(child_process_id); |
| 151 if (iter == child_process_id_to_minidump_info_.end()) { |
| 152 // We might get a NOTIFICATION_RENDERER_PROCESS_TERMINATED and a |
| 153 // NOTIFICATION_RENDERER_PROCESS_CLOSED. |
| 154 return; |
| 155 } |
| 156 minidump_info = iter->second; |
| 157 child_process_id_to_minidump_info_.erase(iter); |
| 158 } |
| 159 ProcessMinidump(minidump_info); |
| 160 } |
OLD | NEW |