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