1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : // A utility class for reading minidumps.
16 :
17 : #ifndef SYZYGY_MINIDUMP_MINIDUMP_H_
18 : #define SYZYGY_MINIDUMP_MINIDUMP_H_
19 :
20 : #include <windows.h> // NOLINT
21 : #include <dbghelp.h>
22 :
23 : #include <stdint.h>
24 : #include <string>
25 : #include <vector>
26 :
27 : #include "base/macros.h"
28 : #include "base/files/file_path.h"
29 : #include "base/files/scoped_file.h"
30 :
31 : namespace minidump {
32 :
33 : namespace internal {
34 :
35 : // Provides the default header parsing for the TypedMinidumpStream class.
36 : class DefaultHeaderParser {
37 : public:
38 : static size_t Parse(const MINIDUMP_MEMORY_LIST& header);
39 : static size_t Parse(const MINIDUMP_MODULE_LIST& header);
40 : static size_t Parse(const MINIDUMP_THREAD_LIST& header);
41 : static size_t Parse(const MINIDUMP_THREAD_EX_LIST& header);
42 : };
43 :
44 : } // namespace internal
45 :
46 : // fwd.
47 : template <typename HeaderType,
48 : typename ElementType,
49 : size_t (*ParseHeaderFunction)(const HeaderType& hdr) =
50 : internal::DefaultHeaderParser::Parse>
51 : class TypedMinidumpStream;
52 :
53 : template <typename ElementType>
54 : class TypedMinidumpStreamIterator;
55 :
56 : class Minidump {
57 : public:
58 : using TypedMemoryList =
59 : TypedMinidumpStream<MINIDUMP_MEMORY_LIST, MINIDUMP_MEMORY_DESCRIPTOR>;
60 : using TypedModuleList =
61 : TypedMinidumpStream<MINIDUMP_MODULE_LIST, MINIDUMP_MODULE>;
62 : using TypedThreadList =
63 : TypedMinidumpStream<MINIDUMP_THREAD_LIST, MINIDUMP_THREAD>;
64 : using TypedThreadExList =
65 : TypedMinidumpStream<MINIDUMP_THREAD_EX_LIST, MINIDUMP_THREAD_EX>;
66 :
67 : static const size_t kNoStreamId = static_cast<size_t>(-1);
68 :
69 : class Stream;
70 :
71 : Minidump();
72 : ~Minidump();
73 :
74 : // @name Typed stream accessors.
75 : // These functions retrieve typed stream accessors to particular well-known
76 : // streams in the mindump directory.
77 : // @{
78 : TypedMemoryList GetMemoryList() const;
79 : TypedModuleList GetModuleList() const;
80 : TypedThreadList GetThreadList() const;
81 : TypedThreadExList GetThreadExList() const;
82 : // @}
83 :
84 : // Returns a stream for @p location.
85 : // @param location defines the offset and length of the returned stream.
86 : Stream GetStreamFor(const MINIDUMP_LOCATION_DESCRIPTOR& location) const;
87 :
88 : // Returns a stream for the file's @p stream_id.
89 : // @param stream_id the stream id to return, must be a valid stream id.
90 : Stream GetStream(size_t stream_id) const;
91 :
92 : // Find the next stream of type @p stream_type.
93 : // @param prev the previous stream of this type or nullptr.
94 : // @param stream_type the stream type to look for.
95 : // @returns a valid stream if one can be found, otherwise an invalid stream.
96 : Stream FindNextStream(const Stream* prev, size_t stream_type) const;
97 :
98 : // Accessors.
99 E : const std::vector<MINIDUMP_DIRECTORY>& directory() const {
100 E : return directory_;
101 E : }
102 :
103 : protected:
104 : friend class Stream;
105 :
106 : // @name Data accessors.
107 : // Reads file contents.
108 : // @param offset the file offset to read from.
109 : // @param data_size the amount of data to read.
110 : // @param data where to write the data, must be of size @p data_data size or
111 : // larger.
112 : // @returns true on success, false on failure, including a short read.
113 : virtual bool ReadBytes(size_t offset, size_t data_size, void* data) const = 0;
114 :
115 : bool ReadDirectory();
116 :
117 : std::vector<MINIDUMP_DIRECTORY> directory_;
118 :
119 : private:
120 : DISALLOW_COPY_AND_ASSIGN(Minidump);
121 : };
122 :
123 : // Allows parsing a minidump from a file.
124 : class FileMinidump : public Minidump {
125 : public:
126 : // Opens the minidump file at @p path and verifies its header structure.
127 : // @param path the minidump file to open.
128 : // @return true on success, false on failure.
129 : bool Open(const base::FilePath& path);
130 :
131 : protected:
132 : bool ReadBytes(size_t offset, size_t data_size, void* data) const override;
133 :
134 : private:
135 : base::ScopedFILE file_;
136 : };
137 :
138 : // Allows parsing a minidump from an in-memory buffer.
139 : class BufferMinidump : public Minidump {
140 : public:
141 : BufferMinidump();
142 :
143 : // Initializes the minidump to the contents of @p buf[0.. @p buf_len].
144 : // Note that @p buf must outlive this instance, as it does not take ownership
145 : // of the buffer, nor copy it.
146 : // @param buf pointer to the buffer containing the minidump.
147 : // @param buf_len the size of the @p buf buffer.
148 : // @returns true on success, false on failure.
149 : bool Initialize(const uint8_t* buf, size_t buf_len);
150 :
151 : protected:
152 : bool ReadBytes(size_t offset, size_t data_size, void* data) const override;
153 :
154 : private:
155 : // Not owned.
156 : const uint8_t* buf_;
157 : size_t buf_len_;
158 : };
159 :
160 : // A forward-only reading class that bounds reads to streams that make it safe
161 : // and easy to parse minidump streams. Streams are lightweight objects that
162 : // can be freely copied.
163 : // Note that a stream has a current position and a remaining length, and no
164 : // independent start position. It's therefore not possible to "rewind" a
165 : // stream.
166 : class Minidump::Stream {
167 : public:
168 : Stream();
169 : Stream(const Minidump* minidump, size_t offset, size_t length,
170 : size_t stream_id);
171 :
172 E : bool IsValid() const { return minidump_ != nullptr; }
173 :
174 : // @name Functions that read and advance over the read data.
175 : // @{
176 : bool ReadAndAdvanceBytes(size_t data_len, void* data);
177 : bool ReadAndAdvanceBytes(size_t data_len, std::string* data);
178 :
179 : template <class DataType>
180 : bool ReadAndAdvanceElement(DataType* element);
181 : bool ReadAndAdvanceString(std::wstring* data);
182 : // @}
183 :
184 : // @name Functions that will separately read and advance by a number of bytes.
185 : // @{
186 : bool ReadBytes(size_t data_len, void* data);
187 : bool AdvanceBytes(size_t data_len);
188 : // @}
189 :
190 : // Accessors.
191 E : size_t current_offset() const { return current_offset_; }
192 E : size_t remaining_length() const { return remaining_length_; }
193 E : size_t stream_id() const { return stream_id_; }
194 E : const Minidump* minidump() const { return minidump_; }
195 :
196 : private:
197 : const Minidump* minidump_;
198 :
199 : size_t current_offset_;
200 : size_t remaining_length_;
201 : size_t stream_id_;
202 : };
203 :
204 : // A forward only-iterator for Minidump Streams that yields elements of a
205 : // given, fixed type.
206 : template <typename ElementType>
207 : class TypedMinidumpStreamIterator {
208 : public:
209 : // Creates a new iterator on @p stream. This iterator will yield
210 : // @p stream.GetBytesRemaining() / sizeof(ElementType) elements.
211 E : explicit TypedMinidumpStreamIterator(const minidump::Minidump::Stream& stream)
212 E : : stream_(stream) {
213 : // Make sure the stream contains a range that covers whole elements.
214 E : DCHECK(!stream_.IsValid() ||
215 : (stream.remaining_length() % sizeof(ElementType) == 0));
216 E : if (stream.remaining_length() != 0) {
217 : // It's fatal if we can't pre-read the element that should be there.
218 E : CHECK(stream_.ReadBytes(sizeof(element_), &element_));
219 : }
220 E : }
221 : TypedMinidumpStreamIterator(const TypedMinidumpStreamIterator& o)
222 : : stream_(o.stream), element_(o.element_) {}
223 :
224 E : void operator++() {
225 : // It's invalid to advance the end iterator.
226 E : DCHECK_NE(0u, stream_.remaining_length());
227 :
228 : // It's fatal if we can't advance over the current element.
229 E : CHECK(stream_.AdvanceBytes(sizeof(element_)));
230 :
231 E : if (stream_.remaining_length()) {
232 : // Not yet at end, read the current element. Fatal if this fails.
233 E : CHECK(stream_.ReadBytes(sizeof(element_), &element_));
234 : }
235 E : }
236 :
237 E : bool operator!=(const TypedMinidumpStreamIterator& o) const {
238 : // Only iterators on the same minidump can be compared.
239 E : DCHECK_EQ(stream_.minidump(), o.stream_.minidump());
240 :
241 E : return stream_.current_offset() != o.stream_.current_offset();
242 E : }
243 :
244 E : const ElementType& operator*() const {
245 E : DCHECK_NE(0u, stream_.remaining_length());
246 E : return element_;
247 E : }
248 :
249 : private:
250 : // Disallow default construction.
251 : TypedMinidumpStreamIterator() {}
252 :
253 : minidump::Minidump::Stream stream_;
254 : ElementType element_;
255 : };
256 :
257 : // A typed minidump stream allows reading a stream header and iterating over
258 : // the elements of the stream.
259 : template <typename HeaderType,
260 : typename ElementType,
261 : size_t (*ParseHeaderFunction)(const HeaderType& hdr)>
262 : class TypedMinidumpStream {
263 : public:
264 : using Iterator = TypedMinidumpStreamIterator<ElementType>;
265 :
266 : // Initializes this instance to a stream of type @p stream_type in
267 : // @p minidump.
268 : TypedMinidumpStream(const Minidump& minidump, size_t stream_type);
269 : TypedMinidumpStream(const TypedMinidumpStream& other) = default;
270 :
271 E : bool IsValid() const { return element_stream_.IsValid(); }
272 :
273 E : const HeaderType& header() const {
274 E : return *reinterpret_cast<const HeaderType*>(header_storage_);
275 E : }
276 :
277 E : Iterator begin() const { return Iterator(element_stream_); }
278 E : Iterator end() const {
279 E : return Iterator(Minidump::Stream(
280 : element_stream_.minidump(),
281 : element_stream_.current_offset() + element_stream_.remaining_length(),
282 : 0, element_stream_.stream_id()));
283 E : }
284 :
285 : private:
286 : // Initializes this instance to a stream of type @p stream_type in
287 : // @p minidump.
288 : // @returns true on success, false if the stream doesn't exist, is not
289 : // unique, or the stream header can't be read.
290 : bool Initialize(const Minidump& minidump, size_t stream_type);
291 :
292 : // The stream we read elements from, this must be constrained to the
293 : // range elements occupy, e.g. positioned at the start of the first element
294 : // and span a multiple of sizeof(ElementType) bytes.
295 : Minidump::Stream element_stream_;
296 :
297 : // Some of the MINIDUMP_* headers declare a zero element array as placeholder
298 : // for the elements. Since such structures can't be directly instantiated,
299 : // we read them into a byte array instead.
300 : uint8_t header_storage_[sizeof(HeaderType)];
301 : };
302 :
303 : template <typename HeaderType,
304 : typename ElementType,
305 : size_t (*ParseHeaderFunction)(const HeaderType& hdr)>
306 : TypedMinidumpStream<HeaderType, ElementType, ParseHeaderFunction>::
307 E : TypedMinidumpStream(const Minidump& minidump, size_t stream_type) {
308 E : memset(header_storage_, 0, sizeof(header_storage_));
309 E : Initialize(minidump, stream_type);
310 E : }
311 :
312 : template <typename HeaderType,
313 : typename ElementType,
314 : size_t (*ParseHeaderFunction)(const HeaderType& hdr)>
315 : bool TypedMinidumpStream<HeaderType, ElementType, ParseHeaderFunction>::
316 E : Initialize(const Minidump& minidump, size_t stream_type) {
317 : // Find the first stream of the requested type.
318 E : Minidump::Stream stream = minidump.FindNextStream(nullptr, stream_type);
319 E : if (!stream.IsValid())
320 i : return false;
321 :
322 : // Make sure the stream is unique.
323 E : if (minidump.FindNextStream(&stream, stream_type).IsValid())
324 i : return false;
325 :
326 : // Read and advance over the header.
327 E : if (!stream.ReadAndAdvanceBytes(sizeof(header_storage_), header_storage_))
328 i : return false;
329 :
330 E : size_t number_of_elements = ParseHeaderFunction(header());
331 : // Make sure the stream has appropriate byte length.
332 E : if (stream.remaining_length() != number_of_elements * sizeof(ElementType))
333 i : return false;
334 :
335 E : element_stream_ = stream;
336 :
337 E : return true;
338 E : }
339 :
340 : template <typename DataType>
341 E : bool Minidump::Stream::ReadAndAdvanceElement(DataType* element) {
342 E : return ReadAndAdvanceBytes(sizeof(DataType), element);
343 E : }
344 :
345 : } // namespace minidump
346 :
347 : #endif // SYZYGY_MINIDUMP_MINIDUMP_H_
|