aboutsummaryrefslogtreecommitdiff
path: root/src/processor/synth_minidump.h
blob: 8dac8784e58178d47a9ea2a19af5a52bbd9ba6b9 (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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
// -*- mode: C++ -*-

// Copyright (c) 2010, 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.

// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>

// synth_minidump.h: Interface to SynthMinidump: fake minidump generator.
//
// We treat a minidump file as the concatenation of a bunch of
// test_assembler::Sections. The file header, stream directory,
// streams, memory regions, strings, and so on --- each is a Section
// that eventually gets appended to the minidump. Dump, Memory,
// Context, Thread, and so on all inherit from test_assembler::Section.
// For example:
//
//    using google_breakpad::test_assembler::kLittleEndian;
//    using google_breakpad::SynthMinidump::Context;
//    using google_breakpad::SynthMinidump::Dump;
//    using google_breakpad::SynthMinidump::Memory;
//    using google_breakpad::SynthMinidump::Thread;
//    
//    Dump minidump(MD_NORMAL, kLittleEndian);
//    
//    Memory stack1(minidump, 0x569eb0a9);
//    ... build contents of stack1 with test_assembler::Section functions ...
//    
//    MDRawContextX86 x86_context1;
//    x86_context1.context_flags = MD_CONTEXT_X86;
//    x86_context1.eip = 0x7c90eb94;
//    x86_context1.esp = 0x569eb0a9;
//    x86_context1.ebp = x86_context1.esp + something appropriate;
//    Context context1(minidump, x86_context1);
//    
//    Thread thread1(minidump, 0xe4a4821d, stack1, context1);
//    
//    minidump.Add(&stack1);
//    minidump.Add(&context1);
//    minidump.Add(&thread1);
//    minidump.Finish();
//    
//    string contents;
//    EXPECT_TRUE(minidump.GetContents(&contents));
//    // contents now holds the bytes of a minidump file
//
// Because the test_assembler classes let us write Label references to
// sections before the Labels' values are known, this gives us
// flexibility in how we put the dump together: minidump pieces can
// hold the file offsets of other minidump pieces before the
// referents' positions have been decided. As long as everything has
// been placed by the time we call dump.GetContents to obtain the
// bytes, all the Labels' values will be known, and everything will
// get patched up appropriately.
//   
// The dump.Add(thing) functions append THINGS's contents to the
// minidump, but they also do two other things:
//
// - dump.Add(thing) invokes thing->Finish, which tells *thing the
//   offset within the file at which it was placed, and allows *thing
//   to do any final content generation.
//
// - If THING is something which should receive an entry in some sort
//   of list or directory, then dump.Add(THING) automatically creates
//   the appropriate directory or list entry. Streams must appear in
//   the stream directory; memory ranges should be listed in the
//   memory list; threads should be placed in the thread list; and so
//   on.
//
// By convention, Section subclass constructors that take references
// to other Sections do not take care of 'Add'ing their arguments to
// the dump. For example, although the Thread constructor takes
// references to a Memory and a Context, it does not add them to the
// dump on the caller's behalf. Rather, the caller is responsible for
// 'Add'ing every section they create. This allows Sections to be
// cited from more than one place; for example, Memory ranges are
// cited both from Thread objects (as their stack contents) and by the
// memory list stream.
//
// If you forget to Add some Section, the Dump::GetContents call will
// fail, as the test_assembler::Labels used to cite the Section's
// contents from elsewhere will still be undefined.
#ifndef PROCESSOR_SYNTH_MINIDUMP_H_
#define PROCESSOR_SYNTH_MINIDUMP_H_

#include <assert.h>

#include <iostream>
#include <string>

#include "common/test_assembler.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"

namespace google_breakpad {

namespace SynthMinidump {

using test_assembler::Endianness;
using test_assembler::kBigEndian;
using test_assembler::kLittleEndian;
using test_assembler::kUnsetEndian;
using test_assembler::Label;

class Dump;
class Memory;
class String;

// A test_assembler::Section which will be appended to a minidump.
class Section: public test_assembler::Section {
 public:
  explicit Section(const Dump &dump);

  // Append an MDLocationDescriptor referring to this section to SECTION.
  // If 'this' is NULL, append a descriptor with a zero length and MDRVA.
  //
  // (I couldn't find the language in the C++ standard that says that
  // invoking member functions of a NULL pointer to a class type is
  // bad, if such language exists. Having this function handle NULL
  // 'this' is convenient, but if it causes trouble, it's not hard to
  // do differently.)
  void CiteLocationIn(test_assembler::Section *section) const;

  // Note that this section's contents are complete, and that it has
  // been placed in the minidump file at OFFSET. The 'Add' member
  // functions call the Finish member function of the object being
  // added for you; if you are 'Add'ing this section, you needn't Finish it.
  virtual void Finish(const Label &offset) { 
    file_offset_ = offset; size_ = Size();
  }

 protected:
  // This section's size and offset within the minidump file.
  Label file_offset_, size_;
};

// A stream within a minidump file. 'Add'ing a stream to a minidump
// creates an entry for it in the minidump's stream directory.
class Stream: public Section {
 public:
  // Create a stream of type TYPE.  You can append whatever contents
  // you like to this stream using the test_assembler::Section methods.
  Stream(const Dump &dump, uint32_t type) : Section(dump), type_(type) { }

  // Append an MDRawDirectory referring to this stream to SECTION.
  void CiteStreamIn(test_assembler::Section *section) const;

 private:
  // The type of this stream.
  uint32_t type_;
};

class SystemInfo: public Stream {
 public:
  // Create an MD_SYSTEM_INFO_STREAM stream belonging to DUMP holding
  // an MDRawSystem info structure initialized with the values from
  // SYSTEM_INFO, except that the csd_version field is replaced with
  // the file offset of the string CSD_VERSION, which can be 'Add'ed
  // to the dump at the desired location.
  // 
  // Remember that you are still responsible for 'Add'ing CSD_VERSION
  // to the dump yourself.
  SystemInfo(const Dump &dump,
             const MDRawSystemInfo &system_info,
             const String &csd_version);

  // Stock MDRawSystemInfo information and associated strings, for
  // writing tests.
  static const MDRawSystemInfo windows_x86;
  static const string windows_x86_csd_version;
};

// An MDString: a string preceded by a 32-bit length.
class String: public Section {
 public:
  String(const Dump &dump, const string &value);

  // Append an MDRVA referring to this string to SECTION.
  void CiteStringIn(test_assembler::Section *section) const;
};

// A range of memory contents. 'Add'ing a memory range to a minidump
// creates n entry for it in the minidump's memory list. By
// convention, the 'start', 'Here', and 'Mark' member functions refer
// to memory addresses.
class Memory: public Section {
 public:
  Memory(const Dump &dump, uint64_t address)
      : Section(dump), address_(address) { start() = address; }

  // Append an MDMemoryDescriptor referring to this memory range to SECTION.
  void CiteMemoryIn(test_assembler::Section *section) const;

 private:
  // The process address from which these memory contents were taken.
  // Shouldn't this be a Label?
  uint64_t address_;
};

class Context: public Section {
 public:
  // Create a context belonging to DUMP whose contents are a copy of CONTEXT.
  Context(const Dump &dump, const MDRawContextX86 &context);
  Context(const Dump &dump, const MDRawContextARM &context);
  Context(const Dump &dump, const MDRawContextMIPS &context);
  // Add an empty context to the dump.
  Context(const Dump &dump) : Section(dump) {}
  // Add constructors for other architectures here. Remember to byteswap.
};

class Thread: public Section {
 public:
  // Create a thread belonging to DUMP with the given values, citing
  // STACK and CONTEXT (which you must Add to the dump separately).
  Thread(const Dump &dump,
         uint32_t thread_id,
         const Memory &stack,
         const Context &context,
         uint32_t suspend_count = 0,
         uint32_t priority_class = 0,
         uint32_t priority = 0,
         uint64_t teb = 0);
};

class Module: public Section {
 public:
  // Create a module with the given values. Note that CV_RECORD and
  // MISC_RECORD can be NULL, in which case the corresponding location
  // descriptior in the minidump will have a length of zero.
  Module(const Dump &dump,
         uint64_t base_of_image,
         uint32_t size_of_image,
         const String &name,
         uint32_t time_date_stamp = 1262805309,
         uint32_t checksum = 0,
         const MDVSFixedFileInfo &version_info = Module::stock_version_info,
         const Section *cv_record = NULL,
         const Section *misc_record = NULL);

 private:
  // A standard MDVSFixedFileInfo structure to use as a default for
  // minidumps.  There's no reason to make users write out all this crap
  // over and over.
  static const MDVSFixedFileInfo stock_version_info;
};

class Exception : public Stream {
public:
  Exception(const Dump &dump,
            const Context &context,
            uint32_t thread_id = 0,
            uint32_t exception_code = 0,
            uint32_t exception_flags = 0,
            uint64_t exception_address = 0);
};

// A list of entries starting with a 32-bit count, like a memory list
// or a thread list.
template<typename Element>
class List: public Stream {
 public:
  List(const Dump &dump, uint32_t type) : Stream(dump, type), count_(0) {
    D32(count_label_);
  }

  // Add ELEMENT to this list.
  void Add(Element *element) {
    element->Finish(file_offset_ + Size());
    Append(*element);
    count_++;
  }

  // Return true if this List is empty, false otherwise.
  bool Empty() { return count_ == 0; }

  // Finish up the contents of this section, mark it as having been
  // placed at OFFSET.
  virtual void Finish(const Label &offset) {
    Stream::Finish(offset);
    count_label_ = count_;
  }

 private:
  size_t count_;
  Label count_label_;
};

class Dump: public test_assembler::Section {
 public:

  // Create a test_assembler::Section containing a minidump file whose
  // header uses the given values. ENDIANNESS determines the
  // endianness of the signature; we set this section's default
  // endianness by this.
  Dump(uint64_t flags,
       Endianness endianness = kLittleEndian,
       uint32_t version = MD_HEADER_VERSION,
       uint32_t date_time_stamp = 1262805309);

  // The following functions call OBJECT->Finish(), and append the
  // contents of OBJECT to this minidump. They also record OBJECT in
  // whatever directory or list is appropriate for its type. The
  // stream directory, memory list, thread list, and module list are
  // accumulated this way.
  Dump &Add(SynthMinidump::Section *object); // simply append data
  Dump &Add(Stream *object); // append, record in stream directory
  Dump &Add(Memory *object); // append, record in memory list
  Dump &Add(Thread *object); // append, record in thread list
  Dump &Add(Module *object); // append, record in module list

  // Complete the construction of the minidump, given the Add calls
  // we've seen up to this point. After this call, this Dump's
  // contents are complete, all labels should be defined if everything
  // Cited has been Added, and you may call GetContents on it.
  void Finish();

 private:
  // A label representing the start of the minidump file.
  Label file_start_;

  // The stream directory.  We construct this incrementally from
  // Add(Stream *) calls.
  SynthMinidump::Section stream_directory_; // The directory's contents.
  size_t stream_count_;                 // The number of streams so far.
  Label stream_count_label_;            // Cited in file header.
  Label stream_directory_rva_;          // The directory's file offset.

  // This minidump's thread list. We construct this incrementally from
  // Add(Thread *) calls.
  List<Thread> thread_list_;

  // This minidump's module list. We construct this incrementally from
  // Add(Module *) calls.
  List<Module> module_list_;

  // This minidump's memory list. We construct this incrementally from
  // Add(Memory *) calls. This is actually a list of MDMemoryDescriptors,
  // not memory ranges --- thus the odd type.
  List<SynthMinidump::Section> memory_list_;
};

} // namespace SynthMinidump

} // namespace google_breakpad

#endif  // PROCESSOR_SYNTH_MINIDUMP_H_