PageRenderTime 94ms CodeModel.GetById 42ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/client/windows/crash_generation/minidump_generator.cc

http://github.com/tomahawk-player/tomahawk
C++ | 309 lines | 219 code | 43 blank | 47 comment | 40 complexity | 7cb4cf9b8631197da001b5d2cc71aa4d MD5 | raw file
  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/minidump_generator.h"
 31#include <cassert>
 32#include "client/windows/common/auto_critical_section.h"
 33#include "common/windows/guid_string.h"
 34
 35using std::wstring;
 36
 37namespace google_breakpad {
 38
 39MinidumpGenerator::MinidumpGenerator(const wstring& dump_path)
 40    : dbghelp_module_(NULL),
 41      rpcrt4_module_(NULL),
 42      dump_path_(dump_path),
 43      write_dump_(NULL),
 44      create_uuid_(NULL) {
 45  InitializeCriticalSection(&module_load_sync_);
 46  InitializeCriticalSection(&get_proc_address_sync_);
 47}
 48
 49MinidumpGenerator::~MinidumpGenerator() {
 50  if (dbghelp_module_) {
 51    FreeLibrary(dbghelp_module_);
 52  }
 53
 54  if (rpcrt4_module_) {
 55    FreeLibrary(rpcrt4_module_);
 56  }
 57
 58  DeleteCriticalSection(&get_proc_address_sync_);
 59  DeleteCriticalSection(&module_load_sync_);
 60}
 61
 62bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
 63                                      DWORD process_id,
 64                                      DWORD thread_id,
 65                                      DWORD requesting_thread_id,
 66                                      EXCEPTION_POINTERS* exception_pointers,
 67                                      MDRawAssertionInfo* assert_info,
 68                                      MINIDUMP_TYPE dump_type,
 69                                      bool is_client_pointers,
 70                                      wstring* dump_path) {
 71  // Just call the full WriteMinidump with NULL as the full_dump_path.
 72  return this->WriteMinidump(process_handle, process_id, thread_id,
 73                             requesting_thread_id, exception_pointers,
 74                             assert_info, dump_type, is_client_pointers,
 75                             dump_path, NULL);
 76}
 77
 78bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
 79                                      DWORD process_id,
 80                                      DWORD thread_id,
 81                                      DWORD requesting_thread_id,
 82                                      EXCEPTION_POINTERS* exception_pointers,
 83                                      MDRawAssertionInfo* assert_info,
 84                                      MINIDUMP_TYPE dump_type,
 85                                      bool is_client_pointers,
 86                                      wstring* dump_path,
 87                                      wstring* full_dump_path) {
 88  MiniDumpWriteDumpType write_dump = GetWriteDump();
 89  if (!write_dump) {
 90    return false;
 91  }
 92
 93  wstring dump_file_path;
 94  if (!GenerateDumpFilePath(&dump_file_path)) {
 95    return false;
 96  }
 97
 98  // If the client requests a full memory dump, we will write a normal mini
 99  // dump and a full memory dump. Both dump files use the same uuid as file
100  // name prefix.
101  bool full_memory_dump = (dump_type & MiniDumpWithFullMemory) != 0;
102  wstring full_dump_file_path;
103  if (full_memory_dump) {
104    full_dump_file_path.assign(dump_file_path);
105    full_dump_file_path.resize(full_dump_file_path.size() - 4);  // strip .dmp
106    full_dump_file_path.append(TEXT("-full.dmp"));
107  }
108
109  HANDLE dump_file = CreateFile(dump_file_path.c_str(),
110                                GENERIC_WRITE,
111                                0,
112                                NULL,
113                                CREATE_NEW,
114                                FILE_ATTRIBUTE_NORMAL,
115                                NULL);
116
117  if (dump_file == INVALID_HANDLE_VALUE) {
118    return false;
119  }
120
121  HANDLE full_dump_file = INVALID_HANDLE_VALUE;
122  if (full_memory_dump) {
123    full_dump_file = CreateFile(full_dump_file_path.c_str(),
124                                GENERIC_WRITE,
125                                0,
126                                NULL,
127                                CREATE_NEW,
128                                FILE_ATTRIBUTE_NORMAL,
129                                NULL);
130
131    if (full_dump_file == INVALID_HANDLE_VALUE) {
132      CloseHandle(dump_file);
133      return false;
134    }
135  }
136
137  MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL;
138  MINIDUMP_EXCEPTION_INFORMATION dump_exception_info;
139
140  // Setup the exception information object only if it's a dump
141  // due to an exception.
142  if (exception_pointers) {
143    dump_exception_pointers = &dump_exception_info;
144    dump_exception_info.ThreadId = thread_id;
145    dump_exception_info.ExceptionPointers = exception_pointers;
146    dump_exception_info.ClientPointers = is_client_pointers;
147  }
148
149  // Add an MDRawBreakpadInfo stream to the minidump, to provide additional
150  // information about the exception handler to the Breakpad processor.
151  // The information will help the processor determine which threads are
152  // relevant. The Breakpad processor does not require this information but
153  // can function better with Breakpad-generated dumps when it is present.
154  // The native debugger is not harmed by the presence of this information.
155  MDRawBreakpadInfo breakpad_info = {0};
156  if (!is_client_pointers) {
157    // Set the dump thread id and requesting thread id only in case of
158    // in-process dump generation.
159    breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
160                             MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
161    breakpad_info.dump_thread_id = thread_id;
162    breakpad_info.requesting_thread_id = requesting_thread_id;
163  }
164
165  // Leave room in user_stream_array for a possible assertion info stream.
166  MINIDUMP_USER_STREAM user_stream_array[2];
167  user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
168  user_stream_array[0].BufferSize = sizeof(breakpad_info);
169  user_stream_array[0].Buffer = &breakpad_info;
170
171  MINIDUMP_USER_STREAM_INFORMATION user_streams;
172  user_streams.UserStreamCount = 1;
173  user_streams.UserStreamArray = user_stream_array;
174
175  MDRawAssertionInfo* actual_assert_info = assert_info;
176  MDRawAssertionInfo client_assert_info = {0};
177
178  if (assert_info) {
179    // If the assertion info object lives in the client process,
180    // read the memory of the client process.
181    if (is_client_pointers) {
182      SIZE_T bytes_read = 0;
183      if (!ReadProcessMemory(process_handle,
184                             assert_info,
185                             &client_assert_info,
186                             sizeof(client_assert_info),
187                             &bytes_read)) {
188        CloseHandle(dump_file);
189        if (full_dump_file != INVALID_HANDLE_VALUE)
190          CloseHandle(full_dump_file);
191        return false;
192      }
193
194      if (bytes_read != sizeof(client_assert_info)) {
195        CloseHandle(dump_file);
196        if (full_dump_file != INVALID_HANDLE_VALUE)
197          CloseHandle(full_dump_file);
198        return false;
199      }
200
201      actual_assert_info  = &client_assert_info;
202    }
203
204    user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM;
205    user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo);
206    user_stream_array[1].Buffer = actual_assert_info;
207    ++user_streams.UserStreamCount;
208  }
209
210  bool result_minidump = write_dump(
211      process_handle,
212      process_id,
213      dump_file,
214      static_cast<MINIDUMP_TYPE>((dump_type & (~MiniDumpWithFullMemory))
215                                  | MiniDumpNormal),
216      exception_pointers ? &dump_exception_info : NULL,
217      &user_streams,
218      NULL) != FALSE;
219
220  bool result_full_memory = true;
221  if (full_memory_dump) {
222    result_full_memory = write_dump(
223        process_handle,
224        process_id,
225        full_dump_file,
226        static_cast<MINIDUMP_TYPE>(dump_type & (~MiniDumpNormal)),
227        exception_pointers ? &dump_exception_info : NULL,
228        &user_streams,
229        NULL) != FALSE;
230  }
231
232  bool result = result_minidump && result_full_memory;
233
234  CloseHandle(dump_file);
235  if (full_dump_file != INVALID_HANDLE_VALUE)
236    CloseHandle(full_dump_file);
237
238  // Store the path of the dump file in the out parameter if dump generation
239  // succeeded.
240  if (result && dump_path) {
241    *dump_path = dump_file_path;
242  }
243  if (result && full_memory_dump && full_dump_path) {
244    *full_dump_path = full_dump_file_path;
245  }
246
247  return result;
248}
249
250HMODULE MinidumpGenerator::GetDbghelpModule() {
251  AutoCriticalSection lock(&module_load_sync_);
252  if (!dbghelp_module_) {
253    dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll"));
254  }
255
256  return dbghelp_module_;
257}
258
259MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() {
260  AutoCriticalSection lock(&get_proc_address_sync_);
261  if (!write_dump_) {
262    HMODULE module = GetDbghelpModule();
263    if (module) {
264      FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump");
265      write_dump_ = reinterpret_cast<MiniDumpWriteDumpType>(proc);
266    }
267  }
268
269  return write_dump_;
270}
271
272HMODULE MinidumpGenerator::GetRpcrt4Module() {
273  AutoCriticalSection lock(&module_load_sync_);
274  if (!rpcrt4_module_) {
275    rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll"));
276  }
277
278  return rpcrt4_module_;
279}
280
281MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() {
282  AutoCriticalSection lock(&module_load_sync_);
283  if (!create_uuid_) {
284    HMODULE module = GetRpcrt4Module();
285    if (module) {
286      FARPROC proc = GetProcAddress(module, "UuidCreate");
287      create_uuid_ = reinterpret_cast<UuidCreateType>(proc);
288    }
289  }
290
291  return create_uuid_;
292}
293
294bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) {
295  UUID id = {0};
296
297  UuidCreateType create_uuid = GetCreateUuid();
298  if (!create_uuid) {
299    return false;
300  }
301
302  create_uuid(&id);
303  wstring id_str = GUIDString::GUIDToWString(&id);
304
305  *file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp");
306  return true;
307}
308
309}  // namespace google_breakpad