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