aboutsummaryrefslogtreecommitdiff
path: root/src/client/mac/handler/minidump_generator.h
blob: f3aa9bd38b20c92eab64298e9e58c6fbb342e8d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// minidump_generator.h:  Create a minidump of the current MacOS process.

#ifndef CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
#define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__

#include <mach/mach.h>
#include <TargetConditionals.h>

#include <string>

#include "client/mac/handler/ucontext_compat.h"
#include "client/minidump_file_writer.h"
#include "common/memory_allocator.h"
#include "common/mac/macho_utilities.h"
#include "google_breakpad/common/minidump_format.h"

#include "dynamic_images.h"
#include "mach_vm_compat.h"

#if !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
  #define HAS_PPC_SUPPORT
#endif
#if defined(__arm__)
#define HAS_ARM_SUPPORT
#elif defined(__aarch64__)
#define HAS_ARM64_SUPPORT
#elif defined(__i386__) || defined(__x86_64__)
  #define HAS_X86_SUPPORT
#endif

namespace google_breakpad {

using std::string;

// Use the REGISTER_FROM_THREADSTATE to access a register name from the
// breakpad_thread_state_t structure.
#if __DARWIN_OPAQUE_ARM_THREAD_STATE64
#define ARRAY_REGISTER_FROM_THREADSTATE(a, b, i) ((a)->__##b[i])
#define GET_REGISTER_FROM_THREADSTATE_fp(a)                                    \
  (reinterpret_cast<uintptr_t>((a)->__opaque_fp))
#define GET_REGISTER_FROM_THREADSTATE_lr(a)                                    \
  (reinterpret_cast<uintptr_t>((a)->__opaque_lr))
#define GET_REGISTER_FROM_THREADSTATE_sp(a)                                    \
  (reinterpret_cast<uintptr_t>((a)->__opaque_sp))
#define GET_REGISTER_FROM_THREADSTATE_pc(a)                                    \
  (reinterpret_cast<uintptr_t>((a)->__opaque_pc))
#define GET_REGISTER_FROM_THREADSTATE_cpsr(a) ((a)->__cpsr)
#define GET_REGISTER_FROM_THREADSTATE_flags(a) ((a)->__opaque_flags)
#define REGISTER_FROM_THREADSTATE(a, b) (GET_REGISTER_FROM_THREADSTATE_##b(a))
#elif __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 || TARGET_CPU_ARM
// In The 10.5 SDK Headers Apple prepended __ to the variable names in the
// i386_thread_state_t structure.  There's no good way to tell what version of
// the SDK we're compiling against so we just toggle on the same preprocessor
// symbol Apple's headers use.
#define REGISTER_FROM_THREADSTATE(a, b) ((a)->__ ## b)
#define ARRAY_REGISTER_FROM_THREADSTATE(a, b, i)                               \
  REGISTER_FROM_THREADSTATE(a, b[i])
#else
#define REGISTER_FROM_THREADSTATE(a, b) (a->b)
#define ARRAY_REGISTER_FROM_THREADSTATE(a, b, i)                               \
  REGISTER_FROM_THREADSTATE(a, b[i])
#endif

// Creates a minidump file of the current process.  If there is exception data,
// use SetExceptionInformation() to add this to the minidump.  The minidump
// file is generated by the Write() function.
// Usage:
// MinidumpGenerator minidump();
// minidump.Write("/tmp/minidump");
//
class MinidumpGenerator {
 public:
  MinidumpGenerator();
  MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread);

  virtual ~MinidumpGenerator();

  // Return <dir>/<unique_name>.dmp
  // Sets |unique_name| (if requested) to the unique name for the minidump
  static string UniqueNameInDirectory(const string &dir, string *unique_name);

  // Write out the minidump into |path|
  // All of the components of |path| must exist and be writable
  // Return true if successful, false otherwise
  bool Write(const char *path);

  // Specify some exception information, if applicable
  void SetExceptionInformation(int type, int code, int subcode,
                               mach_port_t thread_name) {
    exception_type_ = type;
    exception_code_ = code;
    exception_subcode_ = subcode;
    exception_thread_ = thread_name;
  }

  // Specify the task context. If |task_context| is not NULL, it will be used
  // to retrieve the context of the current thread, instead of using
  // |thread_get_state|.
  void SetTaskContext(breakpad_ucontext_t *task_context);

  // Gather system information.  This should be call at least once before using
  // the MinidumpGenerator class.
  static void GatherSystemInformation();

 protected:
  // Overridable Stream writers
  virtual bool WriteExceptionStream(MDRawDirectory *exception_stream);

  // Overridable Helper
  virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);

 private:
  typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);

  // Stream writers
  bool WriteThreadListStream(MDRawDirectory *thread_list_stream);
  bool WriteMemoryListStream(MDRawDirectory *memory_list_stream);
  bool WriteSystemInfoStream(MDRawDirectory *system_info_stream);
  bool WriteModuleListStream(MDRawDirectory *module_list_stream);
  bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream);
  bool WriteBreakpadInfoStream(MDRawDirectory *breakpad_info_stream);

  // Helpers
  uint64_t CurrentPCForStack(breakpad_thread_state_data_t state);
  bool GetThreadState(thread_act_t target_thread, thread_state_t state,
                      mach_msg_type_number_t *count);
  bool WriteStackFromStartAddress(mach_vm_address_t start_addr,
                                  MDMemoryDescriptor *stack_location);
  bool WriteStack(breakpad_thread_state_data_t state,
                  MDMemoryDescriptor *stack_location);
  bool WriteContext(breakpad_thread_state_data_t state,
                    MDLocationDescriptor *register_location);
  bool WriteCVRecord(MDRawModule *module, int cpu_type,
                     const char *module_path, bool in_memory);
  bool WriteModuleStream(unsigned int index, MDRawModule *module);
  size_t CalculateStackSize(mach_vm_address_t start_addr);
  int  FindExecutableModule();

  // Per-CPU implementations of these methods
#ifdef HAS_ARM_SUPPORT
  bool WriteStackARM(breakpad_thread_state_data_t state,
                     MDMemoryDescriptor *stack_location);
  bool WriteContextARM(breakpad_thread_state_data_t state,
                       MDLocationDescriptor *register_location);
  uint64_t CurrentPCForStackARM(breakpad_thread_state_data_t state);
#endif
#ifdef HAS_ARM64_SUPPORT
  bool WriteStackARM64(breakpad_thread_state_data_t state,
                       MDMemoryDescriptor *stack_location);
  bool WriteContextARM64(breakpad_thread_state_data_t state,
                         MDLocationDescriptor *register_location);
  uint64_t CurrentPCForStackARM64(breakpad_thread_state_data_t state);
#endif
#ifdef HAS_PPC_SUPPORT
  bool WriteStackPPC(breakpad_thread_state_data_t state,
                     MDMemoryDescriptor *stack_location);
  bool WriteContextPPC(breakpad_thread_state_data_t state,
                       MDLocationDescriptor *register_location);
  uint64_t CurrentPCForStackPPC(breakpad_thread_state_data_t state);
  bool WriteStackPPC64(breakpad_thread_state_data_t state,
                       MDMemoryDescriptor *stack_location);
  bool WriteContextPPC64(breakpad_thread_state_data_t state,
                       MDLocationDescriptor *register_location);
  uint64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state);
#endif
#ifdef HAS_X86_SUPPORT
  bool WriteStackX86(breakpad_thread_state_data_t state,
                       MDMemoryDescriptor *stack_location);
  bool WriteContextX86(breakpad_thread_state_data_t state,
                       MDLocationDescriptor *register_location);
  uint64_t CurrentPCForStackX86(breakpad_thread_state_data_t state);
  bool WriteStackX86_64(breakpad_thread_state_data_t state,
                        MDMemoryDescriptor *stack_location);
  bool WriteContextX86_64(breakpad_thread_state_data_t state,
                          MDLocationDescriptor *register_location);
  uint64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
#endif

  // disallow copy ctor and operator=
  explicit MinidumpGenerator(const MinidumpGenerator &);
  void operator=(const MinidumpGenerator &);

 protected:
  // Use this writer to put the data to disk
  MinidumpFileWriter writer_;

 private:
  // Exception information
  int exception_type_;
  int exception_code_;
  int exception_subcode_;
  mach_port_t exception_thread_;
  mach_port_t crashing_task_;
  mach_port_t handler_thread_;

  // CPU type of the task being dumped.
  cpu_type_t cpu_type_;

  // System information
  static char build_string_[16];
  static int os_major_version_;
  static int os_minor_version_;
  static int os_build_number_;

  // Context of the task to dump.
  breakpad_ucontext_t *task_context_;

  // Information about dynamically loaded code
  DynamicImages *dynamic_images_;

  // PageAllocator makes it possible to allocate memory
  // directly from the system, even while handling an exception.
  mutable PageAllocator allocator_;

 protected:
  // Blocks of memory written to the dump. These are all currently
  // written while writing the thread list stream, but saved here
  // so a memory list stream can be written afterwards.
  wasteful_vector<MDMemoryDescriptor> memory_blocks_;
};

}  // namespace google_breakpad

#endif  // CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__