llvm.org GIT mirror llvm / c3d07af
MinidumpYAML: move serialization code to MinidumpEmitter.cpp Summary: The code for serializing minidumps was living in MinidumpYAML.cpp so that it would be accessible from unit tests. While this had its advantages, it was also unfortunate because it broke symmetry with all other yaml2obj serializers. Fortunately, nowadays all of yaml2obj is a library, so we don't need to do anything special. This patch improves the code consistency by moving the serialization code to MinidumpEmitter.cpp to match the style used in other backends. It also removes the writeAsBinary entry point in favor of the more general convertYAML interface. This patch is just massaging the code a bit. There shouldn't be any functional change here. Reviewers: jhenderson, abrachet Subscribers: llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D66474 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@369517 91177308-0d34-0410-b5e6-96231b3b80d8 Pavel Labath 25 days ago
4 changed file(s) with 200 addition(s) and 218 deletion(s). Raw diff Collapse all Expand all
176176 static Expected create(const object::MinidumpFile &File);
177177 };
178178
179 /// Serialize the minidump file represented by Obj to OS in binary form.
180 void writeAsBinary(Object &Obj, raw_ostream &OS);
181
182 /// Serialize the yaml string as a minidump file to OS in binary form.
183 Error writeAsBinary(StringRef Yaml, raw_ostream &OS);
184
185179 } // namespace MinidumpYAML
186180
187181 namespace yaml {
77
88 #include "llvm/ObjectYAML/MinidumpYAML.h"
99 #include "llvm/ObjectYAML/yaml2obj.h"
10 #include "llvm/Support/ConvertUTF.h"
1011 #include "llvm/Support/raw_ostream.h"
1112
1213 using namespace llvm;
14 using namespace llvm::minidump;
15 using namespace llvm::MinidumpYAML;
16
17 namespace {
18 /// A helper class to manage the placement of various structures into the final
19 /// minidump binary. Space for objects can be allocated via various allocate***
20 /// methods, while the final minidump file is written by calling the writeTo
21 /// method. The plain versions of allocation functions take a reference to the
22 /// data which is to be written (and hence the data must be available until
23 /// writeTo is called), while the "New" versions allocate the data in an
24 /// allocator-managed buffer, which is available until the allocator object is
25 /// destroyed. For both kinds of functions, it is possible to modify the
26 /// data for which the space has been "allocated" until the final writeTo call.
27 /// This is useful for "linking" the allocated structures via their offsets.
28 class BlobAllocator {
29 public:
30 size_t tell() const { return NextOffset; }
31
32 size_t allocateCallback(size_t Size,
33 std::function Callback) {
34 size_t Offset = NextOffset;
35 NextOffset += Size;
36 Callbacks.push_back(std::move(Callback));
37 return Offset;
38 }
39
40 size_t allocateBytes(ArrayRef Data) {
41 return allocateCallback(
42 Data.size(), [Data](raw_ostream &OS) { OS << toStringRef(Data); });
43 }
44
45 size_t allocateBytes(yaml::BinaryRef Data) {
46 return allocateCallback(Data.binary_size(), [Data](raw_ostream &OS) {
47 Data.writeAsBinary(OS);
48 });
49 }
50
51 template size_t allocateArray(ArrayRef Data) {
52 return allocateBytes({reinterpret_cast(Data.data()),
53 sizeof(T) * Data.size()});
54 }
55
56 template
57 std::pair>
58 allocateNewArray(const iterator_range &Range);
59
60 template size_t allocateObject(const T &Data) {
61 return allocateArray(makeArrayRef(Data));
62 }
63
64 template
65 std::pair allocateNewObject(Types &&... Args) {
66 T *Object = new (Temporaries.Allocate()) T(std::forward(Args)...);
67 return {allocateObject(*Object), Object};
68 }
69
70 size_t allocateString(StringRef Str);
71
72 void writeTo(raw_ostream &OS) const;
73
74 private:
75 size_t NextOffset = 0;
76
77 BumpPtrAllocator Temporaries;
78 std::vector> Callbacks;
79 };
80 } // namespace
81
82 template
83 std::pair>
84 BlobAllocator::allocateNewArray(const iterator_range &Range) {
85 size_t Num = std::distance(Range.begin(), Range.end());
86 MutableArrayRef Array(Temporaries.Allocate(Num), Num);
87 std::uninitialized_copy(Range.begin(), Range.end(), Array.begin());
88 return {allocateArray(Array), Array};
89 }
90
91 size_t BlobAllocator::allocateString(StringRef Str) {
92 SmallVector WStr;
93 bool OK = convertUTF8ToUTF16String(Str, WStr);
94 assert(OK && "Invalid UTF8 in Str?");
95 (void)OK;
96
97 // The utf16 string is null-terminated, but the terminator is not counted in
98 // the string size.
99 WStr.push_back(0);
100 size_t Result =
101 allocateNewObject(2 * (WStr.size() - 1)).first;
102 allocateNewArray(make_range(WStr.begin(), WStr.end()));
103 return Result;
104 }
105
106 void BlobAllocator::writeTo(raw_ostream &OS) const {
107 size_t BeginOffset = OS.tell();
108 for (const auto &Callback : Callbacks)
109 Callback(OS);
110 assert(OS.tell() == BeginOffset + NextOffset &&
111 "Callbacks wrote an unexpected number of bytes.");
112 (void)BeginOffset;
113 }
114
115 static LocationDescriptor layout(BlobAllocator &File, yaml::BinaryRef Data) {
116 return {support::ulittle32_t(Data.binary_size()),
117 support::ulittle32_t(File.allocateBytes(Data))};
118 }
119
120 static void layout(BlobAllocator &File, MemoryListStream::entry_type &Range) {
121 Range.Entry.Memory = layout(File, Range.Content);
122 }
123
124 static void layout(BlobAllocator &File, ModuleListStream::entry_type &M) {
125 M.Entry.ModuleNameRVA = File.allocateString(M.Name);
126
127 M.Entry.CvRecord = layout(File, M.CvRecord);
128 M.Entry.MiscRecord = layout(File, M.MiscRecord);
129 }
130
131 static void layout(BlobAllocator &File, ThreadListStream::entry_type &T) {
132 T.Entry.Stack.Memory = layout(File, T.Stack);
133 T.Entry.Context = layout(File, T.Context);
134 }
135
136 template
137 static size_t layout(BlobAllocator &File,
138 MinidumpYAML::detail::ListStream &S) {
139
140 File.allocateNewObject(S.Entries.size());
141 for (auto &E : S.Entries)
142 File.allocateObject(E.Entry);
143
144 size_t DataEnd = File.tell();
145
146 // Lay out the auxiliary data, (which is not a part of the stream).
147 DataEnd = File.tell();
148 for (auto &E : S.Entries)
149 layout(File, E);
150
151 return DataEnd;
152 }
153
154 static Directory layout(BlobAllocator &File, Stream &S) {
155 Directory Result;
156 Result.Type = S.Type;
157 Result.Location.RVA = File.tell();
158 Optional DataEnd;
159 switch (S.Kind) {
160 case Stream::StreamKind::MemoryList:
161 DataEnd = layout(File, cast(S));
162 break;
163 case Stream::StreamKind::ModuleList:
164 DataEnd = layout(File, cast(S));
165 break;
166 case Stream::StreamKind::RawContent: {
167 RawContentStream &Raw = cast(S);
168 File.allocateCallback(Raw.Size, [&Raw](raw_ostream &OS) {
169 Raw.Content.writeAsBinary(OS);
170 assert(Raw.Content.binary_size() <= Raw.Size);
171 OS << std::string(Raw.Size - Raw.Content.binary_size(), '\0');
172 });
173 break;
174 }
175 case Stream::StreamKind::SystemInfo: {
176 SystemInfoStream &SystemInfo = cast(S);
177 File.allocateObject(SystemInfo.Info);
178 // The CSD string is not a part of the stream.
179 DataEnd = File.tell();
180 SystemInfo.Info.CSDVersionRVA = File.allocateString(SystemInfo.CSDVersion);
181 break;
182 }
183 case Stream::StreamKind::TextContent:
184 File.allocateArray(arrayRefFromStringRef(cast(S).Text));
185 break;
186 case Stream::StreamKind::ThreadList:
187 DataEnd = layout(File, cast(S));
188 break;
189 }
190 // If DataEnd is not set, we assume everything we generated is a part of the
191 // stream.
192 Result.Location.DataSize =
193 DataEnd.getValueOr(File.tell()) - Result.Location.RVA;
194 return Result;
195 }
13196
14197 namespace llvm {
15198 namespace yaml {
16199
17 int yaml2minidump(MinidumpYAML::Object &Doc, raw_ostream &Out) {
18 writeAsBinary(Doc, Out);
200 int yaml2minidump(MinidumpYAML::Object &Obj, raw_ostream &Out) {
201 BlobAllocator File;
202 File.allocateObject(Obj.Header);
203
204 std::vector StreamDirectory(Obj.Streams.size());
205 Obj.Header.StreamDirectoryRVA =
206 File.allocateArray(makeArrayRef(StreamDirectory));
207 Obj.Header.NumberOfStreams = StreamDirectory.size();
208
209 for (auto &Stream : enumerate(Obj.Streams))
210 StreamDirectory[Stream.index()] = layout(File, *Stream.value());
211
212 File.writeTo(Out);
19213 return 0;
20214 }
21215
77
88 #include "llvm/ObjectYAML/MinidumpYAML.h"
99 #include "llvm/Support/Allocator.h"
10 #include "llvm/Support/ConvertUTF.h"
1110
1211 using namespace llvm;
1312 using namespace llvm::MinidumpYAML;
1413 using namespace llvm::minidump;
15
16 namespace {
17 /// A helper class to manage the placement of various structures into the final
18 /// minidump binary. Space for objects can be allocated via various allocate***
19 /// methods, while the final minidump file is written by calling the writeTo
20 /// method. The plain versions of allocation functions take a reference to the
21 /// data which is to be written (and hence the data must be available until
22 /// writeTo is called), while the "New" versions allocate the data in an
23 /// allocator-managed buffer, which is available until the allocator object is
24 /// destroyed. For both kinds of functions, it is possible to modify the
25 /// data for which the space has been "allocated" until the final writeTo call.
26 /// This is useful for "linking" the allocated structures via their offsets.
27 class BlobAllocator {
28 public:
29 size_t tell() const { return NextOffset; }
30
31 size_t allocateCallback(size_t Size,
32 std::function Callback) {
33 size_t Offset = NextOffset;
34 NextOffset += Size;
35 Callbacks.push_back(std::move(Callback));
36 return Offset;
37 }
38
39 size_t allocateBytes(ArrayRef Data) {
40 return allocateCallback(
41 Data.size(), [Data](raw_ostream &OS) { OS << toStringRef(Data); });
42 }
43
44 size_t allocateBytes(yaml::BinaryRef Data) {
45 return allocateCallback(Data.binary_size(), [Data](raw_ostream &OS) {
46 Data.writeAsBinary(OS);
47 });
48 }
49
50 template size_t allocateArray(ArrayRef Data) {
51 return allocateBytes({reinterpret_cast(Data.data()),
52 sizeof(T) * Data.size()});
53 }
54
55 template
56 std::pair>
57 allocateNewArray(const iterator_range &Range);
58
59 template size_t allocateObject(const T &Data) {
60 return allocateArray(makeArrayRef(Data));
61 }
62
63 template
64 std::pair allocateNewObject(Types &&... Args) {
65 T *Object = new (Temporaries.Allocate()) T(std::forward(Args)...);
66 return {allocateObject(*Object), Object};
67 }
68
69 size_t allocateString(StringRef Str);
70
71 void writeTo(raw_ostream &OS) const;
72
73 private:
74 size_t NextOffset = 0;
75
76 BumpPtrAllocator Temporaries;
77 std::vector> Callbacks;
78 };
79 } // namespace
80
81 template
82 std::pair>
83 BlobAllocator::allocateNewArray(const iterator_range &Range) {
84 size_t Num = std::distance(Range.begin(), Range.end());
85 MutableArrayRef Array(Temporaries.Allocate(Num), Num);
86 std::uninitialized_copy(Range.begin(), Range.end(), Array.begin());
87 return {allocateArray(Array), Array};
88 }
89
90 size_t BlobAllocator::allocateString(StringRef Str) {
91 SmallVector WStr;
92 bool OK = convertUTF8ToUTF16String(Str, WStr);
93 assert(OK && "Invalid UTF8 in Str?");
94 (void)OK;
95
96 // The utf16 string is null-terminated, but the terminator is not counted in
97 // the string size.
98 WStr.push_back(0);
99 size_t Result =
100 allocateNewObject(2 * (WStr.size() - 1)).first;
101 allocateNewArray(make_range(WStr.begin(), WStr.end()));
102 return Result;
103 }
104
105 void BlobAllocator::writeTo(raw_ostream &OS) const {
106 size_t BeginOffset = OS.tell();
107 for (const auto &Callback : Callbacks)
108 Callback(OS);
109 assert(OS.tell() == BeginOffset + NextOffset &&
110 "Callbacks wrote an unexpected number of bytes.");
111 (void)BeginOffset;
112 }
11314
11415 /// Perform an optional yaml-mapping of an endian-aware type EndianType. The
11516 /// only purpose of this function is to avoid casting the Default value to the
478379 IO.mapRequired("Streams", O.Streams);
479380 }
480381
481 static LocationDescriptor layout(BlobAllocator &File, yaml::BinaryRef Data) {
482 return {support::ulittle32_t(Data.binary_size()),
483 support::ulittle32_t(File.allocateBytes(Data))};
484 }
485
486 static void layout(BlobAllocator &File, MemoryListStream::entry_type &Range) {
487 Range.Entry.Memory = layout(File, Range.Content);
488 }
489
490 static void layout(BlobAllocator &File, ModuleListStream::entry_type &M) {
491 M.Entry.ModuleNameRVA = File.allocateString(M.Name);
492
493 M.Entry.CvRecord = layout(File, M.CvRecord);
494 M.Entry.MiscRecord = layout(File, M.MiscRecord);
495 }
496
497 static void layout(BlobAllocator &File, ThreadListStream::entry_type &T) {
498 T.Entry.Stack.Memory = layout(File, T.Stack);
499 T.Entry.Context = layout(File, T.Context);
500 }
501
502 template
503 static size_t layout(BlobAllocator &File,
504 MinidumpYAML::detail::ListStream &S) {
505
506 File.allocateNewObject(S.Entries.size());
507 for (auto &E : S.Entries)
508 File.allocateObject(E.Entry);
509
510 size_t DataEnd = File.tell();
511
512 // Lay out the auxiliary data, (which is not a part of the stream).
513 DataEnd = File.tell();
514 for (auto &E : S.Entries)
515 layout(File, E);
516
517 return DataEnd;
518 }
519
520 static Directory layout(BlobAllocator &File, Stream &S) {
521 Directory Result;
522 Result.Type = S.Type;
523 Result.Location.RVA = File.tell();
524 Optional DataEnd;
525 switch (S.Kind) {
526 case Stream::StreamKind::MemoryList:
527 DataEnd = layout(File, cast(S));
528 break;
529 case Stream::StreamKind::ModuleList:
530 DataEnd = layout(File, cast(S));
531 break;
532 case Stream::StreamKind::RawContent: {
533 RawContentStream &Raw = cast(S);
534 File.allocateCallback(Raw.Size, [&Raw](raw_ostream &OS) {
535 Raw.Content.writeAsBinary(OS);
536 assert(Raw.Content.binary_size() <= Raw.Size);
537 OS << std::string(Raw.Size - Raw.Content.binary_size(), '\0');
538 });
539 break;
540 }
541 case Stream::StreamKind::SystemInfo: {
542 SystemInfoStream &SystemInfo = cast(S);
543 File.allocateObject(SystemInfo.Info);
544 // The CSD string is not a part of the stream.
545 DataEnd = File.tell();
546 SystemInfo.Info.CSDVersionRVA = File.allocateString(SystemInfo.CSDVersion);
547 break;
548 }
549 case Stream::StreamKind::TextContent:
550 File.allocateArray(arrayRefFromStringRef(cast(S).Text));
551 break;
552 case Stream::StreamKind::ThreadList:
553 DataEnd = layout(File, cast(S));
554 break;
555 }
556 // If DataEnd is not set, we assume everything we generated is a part of the
557 // stream.
558 Result.Location.DataSize =
559 DataEnd.getValueOr(File.tell()) - Result.Location.RVA;
560 return Result;
561 }
562
563 void MinidumpYAML::writeAsBinary(Object &Obj, raw_ostream &OS) {
564 BlobAllocator File;
565 File.allocateObject(Obj.Header);
566
567 std::vector StreamDirectory(Obj.Streams.size());
568 Obj.Header.StreamDirectoryRVA =
569 File.allocateArray(makeArrayRef(StreamDirectory));
570 Obj.Header.NumberOfStreams = StreamDirectory.size();
571
572 for (auto &Stream : enumerate(Obj.Streams))
573 StreamDirectory[Stream.index()] = layout(File, *Stream.value());
574
575 File.writeTo(OS);
576 }
577
578 Error MinidumpYAML::writeAsBinary(StringRef Yaml, raw_ostream &OS) {
579 yaml::Input Input(Yaml);
580 Object Obj;
581 Input >> Obj;
582 if (std::error_code EC = Input.error())
583 return errorCodeToError(EC);
584
585 writeAsBinary(Obj, OS);
586 return Error::success();
587 }
588
589382 Expected>
590383 Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) {
591384 StreamKind Kind = getKind(StreamDesc.Type);
55 //
66 //===----------------------------------------------------------------------===//
77
8 #include "llvm/ObjectYAML/MinidumpYAML.h"
98 #include "llvm/Object/Minidump.h"
10 #include "llvm/ObjectYAML/ObjectYAML.h"
9 #include "llvm/ObjectYAML/yaml2obj.h"
10 #include "llvm/Support/YAMLTraits.h"
1111 #include "llvm/Testing/Support/Error.h"
1212 #include "gtest/gtest.h"
1313
1818 toBinary(SmallVectorImpl &Storage, StringRef Yaml) {
1919 Storage.clear();
2020 raw_svector_ostream OS(Storage);
21 if (Error E = MinidumpYAML::writeAsBinary(Yaml, OS))
21 yaml::Input YIn(Yaml);
22 if (Error E = yaml::convertYAML(YIn, OS))
2223 return std::move(E);
2324
2425 return object::MinidumpFile::create(MemoryBufferRef(OS.str(), "Binary"));