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
33static const wchar_t kCustomInfoProcessUptimeName[] = L"ptime";
34static const size_t kMaxCustomInfoEntries = 4096;
35
36namespace google_breakpad {
37
38ClientInfo::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
61bool 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
90void 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
98void 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
110ClientInfo::~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
136bool 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
149bool 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
162void 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
182bool 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
214CustomClientInfo 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