[ObjectYAML] Add basic minidump generation support
Summary:
This patch adds the ability to read a yaml form of a minidump file and
write it out as binary. Apart from the minidump header and the stream
directory, only three basic stream kinds are supported:
- Text: This kind is used for streams which contain textual data. This
is typically the contents of a /proc file on linux (e.g.
/proc/PID/maps). In this case, we just put the raw stream contents
into the yaml.
- SystemInfo: This stream contains various bits of information about the
host system in binary form. We expose the data in a structured form.
- Raw: This kind is used as a fallback when we don't have any special
knowledge about the stream. In this case, we just print the stream
contents in hex.
For this code to be really useful, more stream kinds will need to be
added (particularly for things like lists of memory regions and loaded
modules). However, these can be added incrementally.
Reviewers: jhenderson, zturner, clayborg, aprantl
Subscribers: mgorny, lemo, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59482
llvm-svn: 356753
2019-03-22 15:47:26 +01:00
|
|
|
//===- yaml2minidump.cpp - Convert a YAML file to a minidump file ---------===//
|
|
|
|
//
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "llvm/ObjectYAML/MinidumpYAML.h"
|
[yaml2obj] Move core yaml2obj code into lib and include for use in unit tests
Reviewers: jhenderson, rupprecht, MaskRay, grimar, labath
Reviewed By: rupprecht
Subscribers: gribozavr, mgrang, seiya, mgorny, sbc100, hiraditya, aheejin, jakehehrlich, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D65255
llvm-svn: 368119
2019-08-07 04:44:49 +02:00
|
|
|
#include "llvm/ObjectYAML/yaml2obj.h"
|
2019-08-21 13:30:48 +02:00
|
|
|
#include "llvm/Support/ConvertUTF.h"
|
[ObjectYAML] Add basic minidump generation support
Summary:
This patch adds the ability to read a yaml form of a minidump file and
write it out as binary. Apart from the minidump header and the stream
directory, only three basic stream kinds are supported:
- Text: This kind is used for streams which contain textual data. This
is typically the contents of a /proc file on linux (e.g.
/proc/PID/maps). In this case, we just put the raw stream contents
into the yaml.
- SystemInfo: This stream contains various bits of information about the
host system in binary form. We expose the data in a structured form.
- Raw: This kind is used as a fallback when we don't have any special
knowledge about the stream. In this case, we just print the stream
contents in hex.
For this code to be really useful, more stream kinds will need to be
added (particularly for things like lists of memory regions and loaded
modules). However, these can be added incrementally.
Reviewers: jhenderson, zturner, clayborg, aprantl
Subscribers: mgorny, lemo, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59482
llvm-svn: 356753
2019-03-22 15:47:26 +01:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
|
|
|
|
using namespace llvm;
|
2019-08-21 13:30:48 +02:00
|
|
|
using namespace llvm::minidump;
|
|
|
|
using namespace llvm::MinidumpYAML;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// A helper class to manage the placement of various structures into the final
|
|
|
|
/// minidump binary. Space for objects can be allocated via various allocate***
|
|
|
|
/// methods, while the final minidump file is written by calling the writeTo
|
|
|
|
/// method. The plain versions of allocation functions take a reference to the
|
|
|
|
/// data which is to be written (and hence the data must be available until
|
|
|
|
/// writeTo is called), while the "New" versions allocate the data in an
|
|
|
|
/// allocator-managed buffer, which is available until the allocator object is
|
|
|
|
/// destroyed. For both kinds of functions, it is possible to modify the
|
|
|
|
/// data for which the space has been "allocated" until the final writeTo call.
|
|
|
|
/// This is useful for "linking" the allocated structures via their offsets.
|
|
|
|
class BlobAllocator {
|
|
|
|
public:
|
|
|
|
size_t tell() const { return NextOffset; }
|
|
|
|
|
|
|
|
size_t allocateCallback(size_t Size,
|
|
|
|
std::function<void(raw_ostream &)> Callback) {
|
|
|
|
size_t Offset = NextOffset;
|
|
|
|
NextOffset += Size;
|
|
|
|
Callbacks.push_back(std::move(Callback));
|
|
|
|
return Offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t allocateBytes(ArrayRef<uint8_t> Data) {
|
|
|
|
return allocateCallback(
|
|
|
|
Data.size(), [Data](raw_ostream &OS) { OS << toStringRef(Data); });
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t allocateBytes(yaml::BinaryRef Data) {
|
|
|
|
return allocateCallback(Data.binary_size(), [Data](raw_ostream &OS) {
|
|
|
|
Data.writeAsBinary(OS);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T> size_t allocateArray(ArrayRef<T> Data) {
|
|
|
|
return allocateBytes({reinterpret_cast<const uint8_t *>(Data.data()),
|
|
|
|
sizeof(T) * Data.size()});
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename RangeType>
|
|
|
|
std::pair<size_t, MutableArrayRef<T>>
|
|
|
|
allocateNewArray(const iterator_range<RangeType> &Range);
|
|
|
|
|
|
|
|
template <typename T> size_t allocateObject(const T &Data) {
|
|
|
|
return allocateArray(makeArrayRef(Data));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename... Types>
|
|
|
|
std::pair<size_t, T *> allocateNewObject(Types &&... Args) {
|
|
|
|
T *Object = new (Temporaries.Allocate<T>()) T(std::forward<Types>(Args)...);
|
|
|
|
return {allocateObject(*Object), Object};
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t allocateString(StringRef Str);
|
|
|
|
|
|
|
|
void writeTo(raw_ostream &OS) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
size_t NextOffset = 0;
|
|
|
|
|
|
|
|
BumpPtrAllocator Temporaries;
|
|
|
|
std::vector<std::function<void(raw_ostream &)>> Callbacks;
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
template <typename T, typename RangeType>
|
|
|
|
std::pair<size_t, MutableArrayRef<T>>
|
|
|
|
BlobAllocator::allocateNewArray(const iterator_range<RangeType> &Range) {
|
|
|
|
size_t Num = std::distance(Range.begin(), Range.end());
|
|
|
|
MutableArrayRef<T> Array(Temporaries.Allocate<T>(Num), Num);
|
|
|
|
std::uninitialized_copy(Range.begin(), Range.end(), Array.begin());
|
|
|
|
return {allocateArray(Array), Array};
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t BlobAllocator::allocateString(StringRef Str) {
|
|
|
|
SmallVector<UTF16, 32> WStr;
|
|
|
|
bool OK = convertUTF8ToUTF16String(Str, WStr);
|
|
|
|
assert(OK && "Invalid UTF8 in Str?");
|
|
|
|
(void)OK;
|
|
|
|
|
|
|
|
// The utf16 string is null-terminated, but the terminator is not counted in
|
|
|
|
// the string size.
|
|
|
|
WStr.push_back(0);
|
|
|
|
size_t Result =
|
|
|
|
allocateNewObject<support::ulittle32_t>(2 * (WStr.size() - 1)).first;
|
|
|
|
allocateNewArray<support::ulittle16_t>(make_range(WStr.begin(), WStr.end()));
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlobAllocator::writeTo(raw_ostream &OS) const {
|
|
|
|
size_t BeginOffset = OS.tell();
|
|
|
|
for (const auto &Callback : Callbacks)
|
|
|
|
Callback(OS);
|
|
|
|
assert(OS.tell() == BeginOffset + NextOffset &&
|
|
|
|
"Callbacks wrote an unexpected number of bytes.");
|
|
|
|
(void)BeginOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static LocationDescriptor layout(BlobAllocator &File, yaml::BinaryRef Data) {
|
|
|
|
return {support::ulittle32_t(Data.binary_size()),
|
|
|
|
support::ulittle32_t(File.allocateBytes(Data))};
|
|
|
|
}
|
|
|
|
|
2019-10-18 16:56:19 +02:00
|
|
|
static size_t layout(BlobAllocator &File, MinidumpYAML::ExceptionStream &S) {
|
|
|
|
File.allocateObject(S.MDExceptionStream);
|
|
|
|
|
|
|
|
size_t DataEnd = File.tell();
|
|
|
|
|
|
|
|
// Lay out the thread context data, (which is not a part of the stream).
|
|
|
|
// TODO: This usually (always?) matches the thread context of the
|
|
|
|
// corresponding thread, and may overlap memory regions as well. We could
|
|
|
|
// add a level of indirection to the MinidumpYAML format (like an array of
|
|
|
|
// Blobs that the LocationDescriptors index into) to be able to distinguish
|
|
|
|
// the cases where location descriptions overlap vs happen to reference
|
|
|
|
// identical data.
|
|
|
|
S.MDExceptionStream.ThreadContext = layout(File, S.ThreadContext);
|
|
|
|
|
|
|
|
return DataEnd;
|
|
|
|
}
|
|
|
|
|
2019-08-21 13:30:48 +02:00
|
|
|
static void layout(BlobAllocator &File, MemoryListStream::entry_type &Range) {
|
|
|
|
Range.Entry.Memory = layout(File, Range.Content);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void layout(BlobAllocator &File, ModuleListStream::entry_type &M) {
|
|
|
|
M.Entry.ModuleNameRVA = File.allocateString(M.Name);
|
|
|
|
|
|
|
|
M.Entry.CvRecord = layout(File, M.CvRecord);
|
|
|
|
M.Entry.MiscRecord = layout(File, M.MiscRecord);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void layout(BlobAllocator &File, ThreadListStream::entry_type &T) {
|
|
|
|
T.Entry.Stack.Memory = layout(File, T.Stack);
|
|
|
|
T.Entry.Context = layout(File, T.Context);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename EntryT>
|
|
|
|
static size_t layout(BlobAllocator &File,
|
|
|
|
MinidumpYAML::detail::ListStream<EntryT> &S) {
|
|
|
|
|
|
|
|
File.allocateNewObject<support::ulittle32_t>(S.Entries.size());
|
|
|
|
for (auto &E : S.Entries)
|
|
|
|
File.allocateObject(E.Entry);
|
|
|
|
|
|
|
|
size_t DataEnd = File.tell();
|
|
|
|
|
|
|
|
// Lay out the auxiliary data, (which is not a part of the stream).
|
|
|
|
DataEnd = File.tell();
|
|
|
|
for (auto &E : S.Entries)
|
|
|
|
layout(File, E);
|
|
|
|
|
|
|
|
return DataEnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Directory layout(BlobAllocator &File, Stream &S) {
|
|
|
|
Directory Result;
|
|
|
|
Result.Type = S.Type;
|
|
|
|
Result.Location.RVA = File.tell();
|
|
|
|
Optional<size_t> DataEnd;
|
|
|
|
switch (S.Kind) {
|
2019-10-18 16:56:19 +02:00
|
|
|
case Stream::StreamKind::Exception:
|
|
|
|
DataEnd = layout(File, cast<MinidumpYAML::ExceptionStream>(S));
|
|
|
|
break;
|
2019-10-10 15:05:46 +02:00
|
|
|
case Stream::StreamKind::MemoryInfoList: {
|
|
|
|
MemoryInfoListStream &InfoList = cast<MemoryInfoListStream>(S);
|
|
|
|
File.allocateNewObject<minidump::MemoryInfoListHeader>(
|
|
|
|
sizeof(minidump::MemoryInfoListHeader), sizeof(minidump::MemoryInfo),
|
|
|
|
InfoList.Infos.size());
|
|
|
|
File.allocateArray(makeArrayRef(InfoList.Infos));
|
|
|
|
break;
|
|
|
|
}
|
2019-08-21 13:30:48 +02:00
|
|
|
case Stream::StreamKind::MemoryList:
|
|
|
|
DataEnd = layout(File, cast<MemoryListStream>(S));
|
|
|
|
break;
|
|
|
|
case Stream::StreamKind::ModuleList:
|
|
|
|
DataEnd = layout(File, cast<ModuleListStream>(S));
|
|
|
|
break;
|
|
|
|
case Stream::StreamKind::RawContent: {
|
|
|
|
RawContentStream &Raw = cast<RawContentStream>(S);
|
|
|
|
File.allocateCallback(Raw.Size, [&Raw](raw_ostream &OS) {
|
|
|
|
Raw.Content.writeAsBinary(OS);
|
|
|
|
assert(Raw.Content.binary_size() <= Raw.Size);
|
|
|
|
OS << std::string(Raw.Size - Raw.Content.binary_size(), '\0');
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Stream::StreamKind::SystemInfo: {
|
|
|
|
SystemInfoStream &SystemInfo = cast<SystemInfoStream>(S);
|
|
|
|
File.allocateObject(SystemInfo.Info);
|
|
|
|
// The CSD string is not a part of the stream.
|
|
|
|
DataEnd = File.tell();
|
|
|
|
SystemInfo.Info.CSDVersionRVA = File.allocateString(SystemInfo.CSDVersion);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Stream::StreamKind::TextContent:
|
|
|
|
File.allocateArray(arrayRefFromStringRef(cast<TextContentStream>(S).Text));
|
|
|
|
break;
|
|
|
|
case Stream::StreamKind::ThreadList:
|
|
|
|
DataEnd = layout(File, cast<ThreadListStream>(S));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// If DataEnd is not set, we assume everything we generated is a part of the
|
|
|
|
// stream.
|
|
|
|
Result.Location.DataSize =
|
|
|
|
DataEnd.getValueOr(File.tell()) - Result.Location.RVA;
|
|
|
|
return Result;
|
|
|
|
}
|
[ObjectYAML] Add basic minidump generation support
Summary:
This patch adds the ability to read a yaml form of a minidump file and
write it out as binary. Apart from the minidump header and the stream
directory, only three basic stream kinds are supported:
- Text: This kind is used for streams which contain textual data. This
is typically the contents of a /proc file on linux (e.g.
/proc/PID/maps). In this case, we just put the raw stream contents
into the yaml.
- SystemInfo: This stream contains various bits of information about the
host system in binary form. We expose the data in a structured form.
- Raw: This kind is used as a fallback when we don't have any special
knowledge about the stream. In this case, we just print the stream
contents in hex.
For this code to be really useful, more stream kinds will need to be
added (particularly for things like lists of memory regions and loaded
modules). However, these can be added incrementally.
Reviewers: jhenderson, zturner, clayborg, aprantl
Subscribers: mgorny, lemo, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59482
llvm-svn: 356753
2019-03-22 15:47:26 +01:00
|
|
|
|
[yaml2obj] Move core yaml2obj code into lib and include for use in unit tests
Reviewers: jhenderson, rupprecht, MaskRay, grimar, labath
Reviewed By: rupprecht
Subscribers: gribozavr, mgrang, seiya, mgorny, sbc100, hiraditya, aheejin, jakehehrlich, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D65255
llvm-svn: 368119
2019-08-07 04:44:49 +02:00
|
|
|
namespace llvm {
|
|
|
|
namespace yaml {
|
|
|
|
|
2019-09-13 18:00:16 +02:00
|
|
|
bool yaml2minidump(MinidumpYAML::Object &Obj, raw_ostream &Out,
|
|
|
|
ErrorHandler /*EH*/) {
|
2019-08-21 13:30:48 +02:00
|
|
|
BlobAllocator File;
|
|
|
|
File.allocateObject(Obj.Header);
|
|
|
|
|
|
|
|
std::vector<Directory> StreamDirectory(Obj.Streams.size());
|
|
|
|
Obj.Header.StreamDirectoryRVA =
|
|
|
|
File.allocateArray(makeArrayRef(StreamDirectory));
|
|
|
|
Obj.Header.NumberOfStreams = StreamDirectory.size();
|
|
|
|
|
|
|
|
for (auto &Stream : enumerate(Obj.Streams))
|
|
|
|
StreamDirectory[Stream.index()] = layout(File, *Stream.value());
|
|
|
|
|
|
|
|
File.writeTo(Out);
|
2019-09-13 11:12:38 +02:00
|
|
|
return true;
|
[ObjectYAML] Add basic minidump generation support
Summary:
This patch adds the ability to read a yaml form of a minidump file and
write it out as binary. Apart from the minidump header and the stream
directory, only three basic stream kinds are supported:
- Text: This kind is used for streams which contain textual data. This
is typically the contents of a /proc file on linux (e.g.
/proc/PID/maps). In this case, we just put the raw stream contents
into the yaml.
- SystemInfo: This stream contains various bits of information about the
host system in binary form. We expose the data in a structured form.
- Raw: This kind is used as a fallback when we don't have any special
knowledge about the stream. In this case, we just print the stream
contents in hex.
For this code to be really useful, more stream kinds will need to be
added (particularly for things like lists of memory regions and loaded
modules). However, these can be added incrementally.
Reviewers: jhenderson, zturner, clayborg, aprantl
Subscribers: mgorny, lemo, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59482
llvm-svn: 356753
2019-03-22 15:47:26 +01:00
|
|
|
}
|
[yaml2obj] Move core yaml2obj code into lib and include for use in unit tests
Reviewers: jhenderson, rupprecht, MaskRay, grimar, labath
Reviewed By: rupprecht
Subscribers: gribozavr, mgrang, seiya, mgorny, sbc100, hiraditya, aheejin, jakehehrlich, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D65255
llvm-svn: 368119
2019-08-07 04:44:49 +02:00
|
|
|
|
|
|
|
} // namespace yaml
|
|
|
|
} // namespace llvm
|