1 // Copyright (c) 2008, Google Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above 11 // copyright notice, this list of conditions and the following disclaimer 12 // in the documentation and/or other materials provided with the 13 // distribution. 14 // * Neither the name of Google Inc. nor the names of its 15 // contributors may be used to endorse or promote products derived from 16 // this software without specific prior written permission. 17 // 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 #include "client/windows/crash_generation/client_info.h" 31 #include "client/windows/common/ipc_protocol.h" 32 33 static const wchar_t kCustomInfoProcessUptimeName[] = L"ptime"; 34 static const size_t kMaxCustomInfoEntries = 4096; 35 36 namespace google_breakpad { 37 38 ClientInfo::ClientInfo(CrashGenerationServer* crash_server, 39 DWORD pid, 40 MINIDUMP_TYPE dump_type, 41 DWORD* thread_id, 42 EXCEPTION_POINTERS** ex_info, 43 MDRawAssertionInfo* assert_info, 44 const CustomClientInfo& custom_client_info) 45 : crash_server_(crash_server), 46 pid_(pid), 47 dump_type_(dump_type), 48 ex_info_(ex_info), 49 assert_info_(assert_info), 50 custom_client_info_(custom_client_info), 51 thread_id_(thread_id), 52 process_handle_(NULL), 53 dump_requested_handle_(NULL), 54 dump_generated_handle_(NULL), 55 dump_request_wait_handle_(NULL), 56 process_exit_wait_handle_(NULL), 57 crash_id_(NULL) { 58 GetSystemTimeAsFileTime(&start_time_); 59 } 60 61 bool ClientInfo::Initialize() { 62 process_handle_ = OpenProcess(GENERIC_ALL, FALSE, pid_); 63 if (!process_handle_) { 64 return false; 65 } 66 67 // The crash_id will be the low order word of the process creation time. 68 FILETIME creation_time, exit_time, kernel_time, user_time; 69 if (GetProcessTimes(process_handle_, &creation_time, &exit_time, 70 &kernel_time, &user_time)) { 71 start_time_ = creation_time; 72 } 73 crash_id_ = start_time_.dwLowDateTime; 74 75 dump_requested_handle_ = CreateEvent(NULL, // Security attributes. 76 TRUE, // Manual reset. 77 FALSE, // Initial state. 78 NULL); // Name. 79 if (!dump_requested_handle_) { 80 return false; 81 } 82 83 dump_generated_handle_ = CreateEvent(NULL, // Security attributes. 84 TRUE, // Manual reset. 85 FALSE, // Initial state. 86 NULL); // Name. 87 return dump_generated_handle_ != NULL; 88 } 89 90 void ClientInfo::UnregisterDumpRequestWaitAndBlockUntilNoPending() { 91 if (dump_request_wait_handle_) { 92 // Wait for callbacks that might already be running to finish. 93 UnregisterWaitEx(dump_request_wait_handle_, INVALID_HANDLE_VALUE); 94 dump_request_wait_handle_ = NULL; 95 } 96 } 97 98 void ClientInfo::UnregisterProcessExitWait(bool block_until_no_pending) { 99 if (process_exit_wait_handle_) { 100 if (block_until_no_pending) { 101 // Wait for the callback that might already be running to finish. 102 UnregisterWaitEx(process_exit_wait_handle_, INVALID_HANDLE_VALUE); 103 } else { 104 UnregisterWait(process_exit_wait_handle_); 105 } 106 process_exit_wait_handle_ = NULL; 107 } 108 } 109 110 ClientInfo::~ClientInfo() { 111 // Waiting for the callback to finish here is safe because ClientInfo's are 112 // never destroyed from the dump request handling callback. 113 UnregisterDumpRequestWaitAndBlockUntilNoPending(); 114 115 // This is a little tricky because ClientInfo's may be destroyed by the same 116 // callback (OnClientEnd) and waiting for it to finish will cause a deadlock. 117 // Regardless of this complication, wait for any running callbacks to finish 118 // so that the common case is properly handled. In order to avoid deadlocks, 119 // the OnClientEnd callback must call UnregisterProcessExitWait(false) 120 // before deleting the ClientInfo. 121 UnregisterProcessExitWait(true); 122 123 if (process_handle_) { 124 CloseHandle(process_handle_); 125 } 126 127 if (dump_requested_handle_) { 128 CloseHandle(dump_requested_handle_); 129 } 130 131 if (dump_generated_handle_) { 132 CloseHandle(dump_generated_handle_); 133 } 134 } 135 136 bool ClientInfo::GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const { 137 SIZE_T bytes_count = 0; 138 if (!ReadProcessMemory(process_handle_, 139 ex_info_, 140 ex_info, 141 sizeof(*ex_info), 142 &bytes_count)) { 143 return false; 144 } 145 146 return bytes_count == sizeof(*ex_info); 147 } 148 149 bool ClientInfo::GetClientThreadId(DWORD* thread_id) const { 150 SIZE_T bytes_count = 0; 151 if (!ReadProcessMemory(process_handle_, 152 thread_id_, 153 thread_id, 154 sizeof(*thread_id), 155 &bytes_count)) { 156 return false; 157 } 158 159 return bytes_count == sizeof(*thread_id); 160 } 161 162 void ClientInfo::SetProcessUptime() { 163 FILETIME now = {0}; 164 GetSystemTimeAsFileTime(&now); 165 166 ULARGE_INTEGER time_start; 167 time_start.HighPart = start_time_.dwHighDateTime; 168 time_start.LowPart = start_time_.dwLowDateTime; 169 170 ULARGE_INTEGER time_now; 171 time_now.HighPart = now.dwHighDateTime; 172 time_now.LowPart = now.dwLowDateTime; 173 174 // Calculate the delay and convert it from 100-nanoseconds to milliseconds. 175 __int64 delay = (time_now.QuadPart - time_start.QuadPart) / 10 / 1000; 176 177 // Convert it to a string. 178 wchar_t* value = custom_info_entries_.get()[custom_client_info_.count].value; 179 _i64tow_s(delay, value, CustomInfoEntry::kValueMaxLength, 10); 180 } 181 182 bool ClientInfo::PopulateCustomInfo() { 183 if (custom_client_info_.count > kMaxCustomInfoEntries) 184 return false; 185 186 SIZE_T bytes_count = 0; 187 SIZE_T read_count = sizeof(CustomInfoEntry) * custom_client_info_.count; 188 189 // If the scoped array for custom info already has an array, it will be 190 // the same size as what we need. This is because the number of custom info 191 // entries is always the same. So allocate memory only if scoped array has 192 // a NULL pointer. 193 if (!custom_info_entries_.get()) { 194 // Allocate an extra entry for reporting uptime for the client process. 195 custom_info_entries_.reset( 196 new CustomInfoEntry[custom_client_info_.count + 1]); 197 // Use the last element in the array for uptime. 198 custom_info_entries_.get()[custom_client_info_.count].set_name( 199 kCustomInfoProcessUptimeName); 200 } 201 202 if (!ReadProcessMemory(process_handle_, 203 custom_client_info_.entries, 204 custom_info_entries_.get(), 205 read_count, 206 &bytes_count)) { 207 return false; 208 } 209 210 SetProcessUptime(); 211 return (bytes_count == read_count); 212 } 213 214 CustomClientInfo ClientInfo::GetCustomInfo() const { 215 CustomClientInfo custom_info; 216 custom_info.entries = custom_info_entries_.get(); 217 // Add 1 to the count from the client process to account for extra entry for 218 // process uptime. 219 custom_info.count = custom_client_info_.count + 1; 220 return custom_info; 221 } 222 223 } // namespace google_breakpad 224