Home | History | Annotate | Download | only in crash_generation
      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