1 // Copyright 2014 The Crashpad Authors. 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 #include "minidump/minidump_memory_writer.h"
16
17 #include <utility>
18
19 #include "base/format_macros.h"
20 #include "base/stl_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "gtest/gtest.h"
23 #include "minidump/minidump_extensions.h"
24 #include "minidump/minidump_file_writer.h"
25 #include "minidump/test/minidump_file_writer_test_util.h"
26 #include "minidump/test/minidump_memory_writer_test_util.h"
27 #include "minidump/test/minidump_writable_test_util.h"
28 #include "snapshot/test/test_memory_snapshot.h"
29 #include "util/file/string_file.h"
30
31 namespace crashpad {
32 namespace test {
33 namespace {
34
35 constexpr MinidumpStreamType kBogusStreamType =
36 static_cast<MinidumpStreamType>(1234);
37
38 // expected_streams is the expected number of streams in the file. The memory
39 // list must be the last stream. If there is another stream, it must come first,
40 // have stream type kBogusStreamType, and have zero-length data.
GetMemoryListStream(const std::string & file_contents,const MINIDUMP_MEMORY_LIST ** memory_list,const uint32_t expected_streams)41 void GetMemoryListStream(const std::string& file_contents,
42 const MINIDUMP_MEMORY_LIST** memory_list,
43 const uint32_t expected_streams) {
44 constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
45 const size_t kMemoryListStreamOffset =
46 kDirectoryOffset + expected_streams * sizeof(MINIDUMP_DIRECTORY);
47 const size_t kMemoryDescriptorsOffset =
48 kMemoryListStreamOffset + sizeof(MINIDUMP_MEMORY_LIST);
49
50 ASSERT_GE(file_contents.size(), kMemoryDescriptorsOffset);
51
52 const MINIDUMP_DIRECTORY* directory;
53 const MINIDUMP_HEADER* header =
54 MinidumpHeaderAtStart(file_contents, &directory);
55 ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, expected_streams, 0));
56 ASSERT_TRUE(directory);
57
58 size_t directory_index = 0;
59 if (expected_streams > 1) {
60 ASSERT_EQ(directory[directory_index].StreamType, kBogusStreamType);
61 ASSERT_EQ(directory[directory_index].Location.DataSize, 0u);
62 ASSERT_EQ(directory[directory_index].Location.Rva, kMemoryListStreamOffset);
63 ++directory_index;
64 }
65
66 ASSERT_EQ(directory[directory_index].StreamType,
67 kMinidumpStreamTypeMemoryList);
68 EXPECT_EQ(directory[directory_index].Location.Rva, kMemoryListStreamOffset);
69
70 *memory_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
71 file_contents, directory[directory_index].Location);
72 ASSERT_TRUE(memory_list);
73 }
74
TEST(MinidumpMemoryWriter,EmptyMemoryList)75 TEST(MinidumpMemoryWriter, EmptyMemoryList) {
76 MinidumpFileWriter minidump_file_writer;
77 auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
78
79 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));
80
81 StringFile string_file;
82 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
83
84 ASSERT_EQ(string_file.string().size(),
85 sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
86 sizeof(MINIDUMP_MEMORY_LIST));
87
88 const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
89 ASSERT_NO_FATAL_FAILURE(
90 GetMemoryListStream(string_file.string(), &memory_list, 1));
91
92 EXPECT_EQ(memory_list->NumberOfMemoryRanges, 0u);
93 }
94
TEST(MinidumpMemoryWriter,OneMemoryRegion)95 TEST(MinidumpMemoryWriter, OneMemoryRegion) {
96 MinidumpFileWriter minidump_file_writer;
97 auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
98
99 constexpr uint64_t kBaseAddress = 0xfedcba9876543210;
100 constexpr size_t kSize = 0x1000;
101 constexpr uint8_t kValue = 'm';
102
103 auto memory_writer =
104 std::make_unique<TestMinidumpMemoryWriter>(kBaseAddress, kSize, kValue);
105 memory_list_writer->AddMemory(std::move(memory_writer));
106
107 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));
108
109 StringFile string_file;
110 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
111
112 const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
113 ASSERT_NO_FATAL_FAILURE(
114 GetMemoryListStream(string_file.string(), &memory_list, 1));
115
116 MINIDUMP_MEMORY_DESCRIPTOR expected;
117 expected.StartOfMemoryRange = kBaseAddress;
118 expected.Memory.DataSize = kSize;
119 expected.Memory.Rva =
120 sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
121 sizeof(MINIDUMP_MEMORY_LIST) +
122 memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
123 ExpectMinidumpMemoryDescriptorAndContents(&expected,
124 &memory_list->MemoryRanges[0],
125 string_file.string(),
126 kValue,
127 true);
128 }
129
TEST(MinidumpMemoryWriter,TwoMemoryRegions)130 TEST(MinidumpMemoryWriter, TwoMemoryRegions) {
131 MinidumpFileWriter minidump_file_writer;
132 auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
133
134 constexpr uint64_t kBaseAddress0 = 0xc0ffee;
135 constexpr size_t kSize0 = 0x0100;
136 constexpr uint8_t kValue0 = '6';
137 constexpr uint64_t kBaseAddress1 = 0xfac00fac;
138 constexpr size_t kSize1 = 0x0200;
139 constexpr uint8_t kValue1 = '!';
140
141 auto memory_writer_0 = std::make_unique<TestMinidumpMemoryWriter>(
142 kBaseAddress0, kSize0, kValue0);
143 memory_list_writer->AddMemory(std::move(memory_writer_0));
144 auto memory_writer_1 = std::make_unique<TestMinidumpMemoryWriter>(
145 kBaseAddress1, kSize1, kValue1);
146 memory_list_writer->AddMemory(std::move(memory_writer_1));
147
148 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));
149
150 StringFile string_file;
151 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
152
153 const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
154 ASSERT_NO_FATAL_FAILURE(
155 GetMemoryListStream(string_file.string(), &memory_list, 1));
156
157 EXPECT_EQ(memory_list->NumberOfMemoryRanges, 2u);
158
159 MINIDUMP_MEMORY_DESCRIPTOR expected;
160
161 {
162 SCOPED_TRACE("region 0");
163
164 expected.StartOfMemoryRange = kBaseAddress0;
165 expected.Memory.DataSize = kSize0;
166 expected.Memory.Rva =
167 sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
168 sizeof(MINIDUMP_MEMORY_LIST) +
169 memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
170 ExpectMinidumpMemoryDescriptorAndContents(&expected,
171 &memory_list->MemoryRanges[0],
172 string_file.string(),
173 kValue0,
174 false);
175 }
176
177 {
178 SCOPED_TRACE("region 1");
179
180 expected.StartOfMemoryRange = kBaseAddress1;
181 expected.Memory.DataSize = kSize1;
182 expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva +
183 memory_list->MemoryRanges[0].Memory.DataSize;
184 ExpectMinidumpMemoryDescriptorAndContents(&expected,
185 &memory_list->MemoryRanges[1],
186 string_file.string(),
187 kValue1,
188 true);
189 }
190 }
191
TEST(MinidumpMemoryWriter,RegionReadFails)192 TEST(MinidumpMemoryWriter, RegionReadFails) {
193 MinidumpFileWriter minidump_file_writer;
194 auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
195
196 constexpr uint64_t kBaseAddress = 0xfedcba9876543210;
197 constexpr size_t kSize = 0x1000;
198 constexpr uint8_t kValue = 'm';
199
200 auto memory_writer =
201 std::make_unique<TestMinidumpMemoryWriter>(kBaseAddress, kSize, kValue);
202
203 // Make the read of that memory fail.
204 memory_writer->SetShouldFailRead(true);
205
206 memory_list_writer->AddMemory(std::move(memory_writer));
207
208 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));
209
210 StringFile string_file;
211 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
212
213 const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
214 ASSERT_NO_FATAL_FAILURE(
215 GetMemoryListStream(string_file.string(), &memory_list, 1));
216
217 MINIDUMP_MEMORY_DESCRIPTOR expected;
218 expected.StartOfMemoryRange = kBaseAddress;
219 expected.Memory.DataSize = kSize;
220 expected.Memory.Rva =
221 sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
222 sizeof(MINIDUMP_MEMORY_LIST) +
223 memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
224 ExpectMinidumpMemoryDescriptorAndContents(
225 &expected,
226 &memory_list->MemoryRanges[0],
227 string_file.string(),
228 0xfe, // Not kValue ('m'), but the value that the implementation inserts
229 // if memory is unreadable.
230 true);
231 }
232
233 class TestMemoryStream final : public internal::MinidumpStreamWriter {
234 public:
TestMemoryStream(uint64_t base_address,size_t size,uint8_t value)235 TestMemoryStream(uint64_t base_address, size_t size, uint8_t value)
236 : MinidumpStreamWriter(), memory_(base_address, size, value) {}
237
~TestMemoryStream()238 ~TestMemoryStream() override {}
239
memory()240 TestMinidumpMemoryWriter* memory() {
241 return &memory_;
242 }
243
244 // MinidumpStreamWriter:
StreamType() const245 MinidumpStreamType StreamType() const override {
246 return kBogusStreamType;
247 }
248
249 protected:
250 // MinidumpWritable:
SizeOfObject()251 size_t SizeOfObject() override {
252 EXPECT_GE(state(), kStateFrozen);
253 return 0;
254 }
255
Children()256 std::vector<MinidumpWritable*> Children() override {
257 EXPECT_GE(state(), kStateFrozen);
258 std::vector<MinidumpWritable*> children(1, memory());
259 return children;
260 }
261
WriteObject(FileWriterInterface * file_writer)262 bool WriteObject(FileWriterInterface* file_writer) override {
263 EXPECT_EQ(state(), kStateWritable);
264 return true;
265 }
266
267 private:
268 TestMinidumpMemoryWriter memory_;
269
270 DISALLOW_COPY_AND_ASSIGN(TestMemoryStream);
271 };
272
TEST(MinidumpMemoryWriter,ExtraMemory)273 TEST(MinidumpMemoryWriter, ExtraMemory) {
274 // This tests MinidumpMemoryListWriter::AddExtraMemory(). That method adds
275 // a MinidumpMemoryWriter to the MinidumpMemoryListWriter without making the
276 // memory writer a child of the memory list writer.
277 MinidumpFileWriter minidump_file_writer;
278
279 constexpr uint64_t kBaseAddress0 = 0x1000;
280 constexpr size_t kSize0 = 0x0400;
281 constexpr uint8_t kValue0 = '1';
282 auto test_memory_stream =
283 std::make_unique<TestMemoryStream>(kBaseAddress0, kSize0, kValue0);
284
285 auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
286 memory_list_writer->AddNonOwnedMemory(test_memory_stream->memory());
287
288 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(test_memory_stream)));
289
290 constexpr uint64_t kBaseAddress1 = 0x2000;
291 constexpr size_t kSize1 = 0x0400;
292 constexpr uint8_t kValue1 = 'm';
293
294 auto memory_writer = std::make_unique<TestMinidumpMemoryWriter>(
295 kBaseAddress1, kSize1, kValue1);
296 memory_list_writer->AddMemory(std::move(memory_writer));
297
298 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));
299
300 StringFile string_file;
301 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
302
303 const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
304 ASSERT_NO_FATAL_FAILURE(
305 GetMemoryListStream(string_file.string(), &memory_list, 2));
306
307 EXPECT_EQ(memory_list->NumberOfMemoryRanges, 2u);
308
309 MINIDUMP_MEMORY_DESCRIPTOR expected;
310
311 {
312 SCOPED_TRACE("region 0");
313
314 expected.StartOfMemoryRange = kBaseAddress0;
315 expected.Memory.DataSize = kSize0;
316 expected.Memory.Rva =
317 sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) +
318 sizeof(MINIDUMP_MEMORY_LIST) +
319 memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
320 ExpectMinidumpMemoryDescriptorAndContents(&expected,
321 &memory_list->MemoryRanges[0],
322 string_file.string(),
323 kValue0,
324 false);
325 }
326
327 {
328 SCOPED_TRACE("region 1");
329
330 expected.StartOfMemoryRange = kBaseAddress1;
331 expected.Memory.DataSize = kSize1;
332 expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva +
333 memory_list->MemoryRanges[0].Memory.DataSize;
334 ExpectMinidumpMemoryDescriptorAndContents(&expected,
335 &memory_list->MemoryRanges[1],
336 string_file.string(),
337 kValue1,
338 true);
339 }
340 }
341
TEST(MinidumpMemoryWriter,AddFromSnapshot)342 TEST(MinidumpMemoryWriter, AddFromSnapshot) {
343 MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[3] = {};
344 uint8_t values[base::size(expect_memory_descriptors)] = {};
345
346 expect_memory_descriptors[0].StartOfMemoryRange = 0;
347 expect_memory_descriptors[0].Memory.DataSize = 0x1000;
348 values[0] = 0x01;
349
350 expect_memory_descriptors[1].StartOfMemoryRange = 0x2000;
351 expect_memory_descriptors[1].Memory.DataSize = 0x2000;
352 values[1] = 0xf4;
353
354 expect_memory_descriptors[2].StartOfMemoryRange = 0x7654321000000000;
355 expect_memory_descriptors[2].Memory.DataSize = 0x800;
356 values[2] = 0xa9;
357
358 std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner;
359 std::vector<const MemorySnapshot*> memory_snapshots;
360 for (size_t index = 0; index < base::size(expect_memory_descriptors);
361 ++index) {
362 memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>());
363 TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get();
364 memory_snapshot->SetAddress(
365 expect_memory_descriptors[index].StartOfMemoryRange);
366 memory_snapshot->SetSize(expect_memory_descriptors[index].Memory.DataSize);
367 memory_snapshot->SetValue(values[index]);
368 memory_snapshots.push_back(memory_snapshot);
369 }
370
371 auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
372 memory_list_writer->AddFromSnapshot(memory_snapshots);
373
374 MinidumpFileWriter minidump_file_writer;
375 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));
376
377 StringFile string_file;
378 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
379
380 const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
381 ASSERT_NO_FATAL_FAILURE(
382 GetMemoryListStream(string_file.string(), &memory_list, 1));
383
384 ASSERT_EQ(memory_list->NumberOfMemoryRanges, 3u);
385
386 for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) {
387 SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
388 ExpectMinidumpMemoryDescriptorAndContents(
389 &expect_memory_descriptors[index],
390 &memory_list->MemoryRanges[index],
391 string_file.string(),
392 values[index],
393 index == memory_list->NumberOfMemoryRanges - 1);
394 }
395 }
396
TEST(MinidumpMemoryWriter,CoalesceExplicitMultiple)397 TEST(MinidumpMemoryWriter, CoalesceExplicitMultiple) {
398 MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[4] = {};
399 uint8_t values[base::size(expect_memory_descriptors)] = {};
400
401 expect_memory_descriptors[0].StartOfMemoryRange = 0;
402 expect_memory_descriptors[0].Memory.DataSize = 1000;
403 values[0] = 0x01;
404
405 expect_memory_descriptors[1].StartOfMemoryRange = 10000;
406 expect_memory_descriptors[1].Memory.DataSize = 2000;
407 values[1] = 0xf4;
408
409 expect_memory_descriptors[2].StartOfMemoryRange = 0x1111111111111111;
410 expect_memory_descriptors[2].Memory.DataSize = 1024;
411 values[2] = 0x99;
412
413 expect_memory_descriptors[3].StartOfMemoryRange = 0xfedcba9876543210;
414 expect_memory_descriptors[3].Memory.DataSize = 1024;
415 values[3] = 0x88;
416
417 struct {
418 uint64_t base;
419 size_t size;
420 uint8_t value;
421 } snapshots_to_add[] = {
422 // Various overlapping.
423 {0, 500, 0x01},
424 {0, 500, 0x01},
425 {250, 500, 0x01},
426 {600, 400, 0x01},
427
428 // Empty removed.
429 {0, 0, 0xbb},
430 {300, 0, 0xcc},
431 {1000, 0, 0xdd},
432 {12000, 0, 0xee},
433
434 // Abutting.
435 {10000, 500, 0xf4},
436 {10500, 500, 0xf4},
437 {11000, 1000, 0xf4},
438
439 // Large base addresses.
440 { 0xfedcba9876543210, 1024, 0x88 },
441 { 0x1111111111111111, 1024, 0x99 },
442 };
443
444 std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner;
445 std::vector<const MemorySnapshot*> memory_snapshots;
446 for (const auto& to_add : snapshots_to_add) {
447 memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>());
448 TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get();
449 memory_snapshot->SetAddress(to_add.base);
450 memory_snapshot->SetSize(to_add.size);
451 memory_snapshot->SetValue(to_add.value);
452 memory_snapshots.push_back(memory_snapshot);
453 }
454
455 auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
456 memory_list_writer->AddFromSnapshot(memory_snapshots);
457
458 MinidumpFileWriter minidump_file_writer;
459 minidump_file_writer.AddStream(std::move(memory_list_writer));
460
461 StringFile string_file;
462 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
463
464 const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
465 ASSERT_NO_FATAL_FAILURE(
466 GetMemoryListStream(string_file.string(), &memory_list, 1));
467
468 ASSERT_EQ(4u, memory_list->NumberOfMemoryRanges);
469
470 for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) {
471 SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
472 ExpectMinidumpMemoryDescriptorAndContents(
473 &expect_memory_descriptors[index],
474 &memory_list->MemoryRanges[index],
475 string_file.string(),
476 values[index],
477 index == memory_list->NumberOfMemoryRanges - 1);
478 }
479 }
480
481 struct TestRange {
TestRangecrashpad::test::__anona2e0b2440111::TestRange482 TestRange(uint64_t base, size_t size) : base(base), size(size) {}
483
484 uint64_t base;
485 size_t size;
486 };
487
488 // Parses a string spec to build a list of ranges suitable for CoalesceTest().
ParseCoalesceSpec(const char * spec)489 std::vector<TestRange> ParseCoalesceSpec(const char* spec) {
490 std::vector<TestRange> result;
491 enum { kNone, kSpace, kDot } state = kNone;
492 const char* range_started_at = nullptr;
493 for (const char* p = spec;; ++p) {
494 EXPECT_TRUE(*p == ' ' || *p == '.' || *p == 0);
495 if (*p == ' ' || *p == 0) {
496 if (state == kDot) {
497 result.push_back(
498 TestRange(range_started_at - spec, p - range_started_at));
499 }
500 state = kSpace;
501 range_started_at = nullptr;
502 } else if (*p == '.') {
503 if (state != kDot) {
504 range_started_at = p;
505 state = kDot;
506 }
507 }
508
509 if (*p == 0)
510 break;
511 }
512
513 return result;
514 }
515
TEST(MinidumpMemoryWriter,CoalesceSpecHelperParse)516 TEST(MinidumpMemoryWriter, CoalesceSpecHelperParse) {
517 const auto empty = ParseCoalesceSpec("");
518 ASSERT_EQ(empty.size(), 0u);
519
520 const auto a = ParseCoalesceSpec("...");
521 ASSERT_EQ(a.size(), 1u);
522 EXPECT_EQ(a[0].base, 0u);
523 EXPECT_EQ(a[0].size, 3u);
524
525 const auto b = ParseCoalesceSpec(" ...");
526 ASSERT_EQ(b.size(), 1u);
527 EXPECT_EQ(b[0].base, 2u);
528 EXPECT_EQ(b[0].size, 3u);
529
530 const auto c = ParseCoalesceSpec(" ... ");
531 ASSERT_EQ(c.size(), 1u);
532 EXPECT_EQ(c[0].base, 2u);
533 EXPECT_EQ(c[0].size, 3u);
534
535 const auto d = ParseCoalesceSpec(" ... ....");
536 ASSERT_EQ(d.size(), 2u);
537 EXPECT_EQ(d[0].base, 2u);
538 EXPECT_EQ(d[0].size, 3u);
539 EXPECT_EQ(d[1].base, 7u);
540 EXPECT_EQ(d[1].size, 4u);
541
542 const auto e = ParseCoalesceSpec(" ... ...... ... ");
543 ASSERT_EQ(e.size(), 3u);
544 EXPECT_EQ(e[0].base, 2u);
545 EXPECT_EQ(e[0].size, 3u);
546 EXPECT_EQ(e[1].base, 7u);
547 EXPECT_EQ(e[1].size, 6u);
548 EXPECT_EQ(e[2].base, 14u);
549 EXPECT_EQ(e[2].size, 3u);
550 }
551
552 constexpr uint8_t kMemoryValue = 0xcd;
553
554 // Builds a coalesce test out of specs of ' ' and '.'. Tests that when the two
555 // ranges are added and coalesced, the result is equal to expected.
CoalesceTest(const char * r1_spec,const char * r2_spec,const char * expected_spec)556 void CoalesceTest(const char* r1_spec,
557 const char* r2_spec,
558 const char* expected_spec) {
559 auto r1 = ParseCoalesceSpec(r1_spec);
560 auto r2 = ParseCoalesceSpec(r2_spec);
561 auto expected = ParseCoalesceSpec(expected_spec);
562
563 std::vector<MINIDUMP_MEMORY_DESCRIPTOR> expect_memory_descriptors;
564 for (const auto& range : expected) {
565 MINIDUMP_MEMORY_DESCRIPTOR mmd = {};
566 mmd.StartOfMemoryRange = range.base;
567 mmd.Memory.DataSize = static_cast<uint32_t>(range.size);
568 expect_memory_descriptors.push_back(mmd);
569 }
570
571 std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner;
572 std::vector<const MemorySnapshot*> memory_snapshots;
573
574 const auto add_test_memory_snapshots = [&memory_snapshots_owner,
575 &memory_snapshots](
576 std::vector<TestRange> ranges) {
577 for (const auto& r : ranges) {
578 memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>());
579 TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get();
580 memory_snapshot->SetAddress(r.base);
581 memory_snapshot->SetSize(r.size);
582 memory_snapshot->SetValue(kMemoryValue);
583 memory_snapshots.push_back(memory_snapshot);
584 }
585 };
586 add_test_memory_snapshots(r1);
587 add_test_memory_snapshots(r2);
588
589 auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
590 memory_list_writer->AddFromSnapshot(memory_snapshots);
591
592 MinidumpFileWriter minidump_file_writer;
593 minidump_file_writer.AddStream(std::move(memory_list_writer));
594
595 StringFile string_file;
596 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
597
598 const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
599 ASSERT_NO_FATAL_FAILURE(
600 GetMemoryListStream(string_file.string(), &memory_list, 1));
601
602 ASSERT_EQ(expected.size(), memory_list->NumberOfMemoryRanges);
603
604 for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) {
605 SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
606 ExpectMinidumpMemoryDescriptorAndContents(
607 &expect_memory_descriptors[index],
608 &memory_list->MemoryRanges[index],
609 string_file.string(),
610 kMemoryValue,
611 index == memory_list->NumberOfMemoryRanges - 1);
612 }
613 }
614
TEST(MinidumpMemoryWriter,CoalescePairsVariousCases)615 TEST(MinidumpMemoryWriter, CoalescePairsVariousCases) {
616 // clang-format off
617
618 CoalesceTest(" .........",
619 " .......",
620 /* result */ " ..............");
621
622 CoalesceTest(" .......",
623 " .........",
624 /* result */ " ..............");
625
626 CoalesceTest(" ...",
627 " .........",
628 /* result */ " .........");
629
630 CoalesceTest(" .........",
631 " ......",
632 /* result */ " .........");
633
634 CoalesceTest(" ...",
635 " ........",
636 /* result */ " ........");
637
638 CoalesceTest(" ........",
639 " ...",
640 /* result */ " ........");
641
642 CoalesceTest(" ...",
643 " ........",
644 /* result */ " ........");
645
646 CoalesceTest(" ........",
647 " ...",
648 /* result */ " ........");
649
650 CoalesceTest(" ... ",
651 " ...",
652 /* result */ " ... ...");
653
654 CoalesceTest(" ...",
655 " ... ",
656 /* result */ " ... ...");
657
658 CoalesceTest("...",
659 ".....",
660 /* result */ ".....");
661
662 CoalesceTest("...",
663 " ..",
664 /* result */ ".....");
665
666 CoalesceTest(" .....",
667 " ..",
668 /* result */ " .......");
669
670 CoalesceTest(" ......... ......",
671 " .......",
672 /* result */ " ..................");
673
674 CoalesceTest(" .......",
675 " ......... ......",
676 /* result */ " ..................");
677
678 CoalesceTest(" .....",
679 " ......... ......",
680 /* result */ " ......... ......");
681
682 CoalesceTest(" ......... ....... .... .",
683 " ......... ...... ....",
684 /* result */ " .......................... .......");
685
686 // clang-format on
687 }
688
689 } // namespace
690 } // namespace test
691 } // namespace crashpad
692