RawContent,
SystemInfo,
TextContent,
+ ThreadList,
};
Stream(StreamKind Kind, minidump::StreamType Type) : Kind(Kind), Type(Type) {}
const object::MinidumpFile &File);
};
-/// A stream representing the list of modules loaded in the process. On disk, it
-/// is represented as a sequence of minidump::Module structures. These contain
-/// pointers to other data structures, like the module's name and CodeView
-/// record. In memory, we represent these as the ParsedModule struct, which
-/// groups minidump::Module with all of its dependant structures in a single
-/// entity.
-struct ModuleListStream : public Stream {
- struct ParsedModule {
- minidump::Module Module;
- std::string Name;
- yaml::BinaryRef CvRecord;
- yaml::BinaryRef MiscRecord;
- };
- std::vector<ParsedModule> Modules;
+namespace detail {
+/// A stream representing a list of abstract entries in a minidump stream. Its
+/// instantiations can be used to represent the ModuleList stream and other
+/// streams with a similar structure.
+template <typename EntryT> struct ListStream : public Stream {
+ using entry_type = EntryT;
- ModuleListStream(std::vector<ParsedModule> Modules = {})
- : Stream(StreamKind::ModuleList, minidump::StreamType::ModuleList),
- Modules(std::move(Modules)) {}
+ std::vector<entry_type> Entries;
- static bool classof(const Stream *S) {
- return S->Kind == StreamKind::ModuleList;
- }
+ explicit ListStream(std::vector<entry_type> Entries = {})
+ : Stream(EntryT::Kind, EntryT::Type), Entries(std::move(Entries)) {}
+
+ static bool classof(const Stream *S) { return S->Kind == EntryT::Kind; }
};
+/// A structure containing all data belonging to a single minidump module.
+struct ParsedModule {
+ static constexpr Stream::StreamKind Kind = Stream::StreamKind::ModuleList;
+ static constexpr minidump::StreamType Type = minidump::StreamType::ModuleList;
+
+ minidump::Module Entry;
+ std::string Name;
+ yaml::BinaryRef CvRecord;
+ yaml::BinaryRef MiscRecord;
+};
+
+/// A structure containing all data belonging to a single minidump thread.
+struct ParsedThread {
+ static constexpr Stream::StreamKind Kind = Stream::StreamKind::ThreadList;
+ static constexpr minidump::StreamType Type = minidump::StreamType::ThreadList;
+
+ minidump::Thread Entry;
+ yaml::BinaryRef Stack;
+ yaml::BinaryRef Context;
+};
+} // namespace detail
+
+using ModuleListStream = detail::ListStream<detail::ParsedModule>;
+using ThreadListStream = detail::ListStream<detail::ParsedThread>;
+
/// A minidump stream represented as a sequence of hex bytes. This is used as a
/// fallback when no other stream kind is suitable.
struct RawContentStream : public Stream {
static StringRef validate(IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S);
};
+template <> struct MappingContextTraits<minidump::MemoryDescriptor, BinaryRef> {
+ static void mapping(IO &IO, minidump::MemoryDescriptor &Memory,
+ BinaryRef &Content);
+};
+
} // namespace yaml
} // namespace llvm
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::CPUInfo::OtherInfo)
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::CPUInfo::X86Info)
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::VSFixedFileInfo)
+
+LLVM_YAML_DECLARE_MAPPING_TRAITS(
+ llvm::MinidumpYAML::ModuleListStream::entry_type)
LLVM_YAML_DECLARE_MAPPING_TRAITS(
- llvm::MinidumpYAML::ModuleListStream::ParsedModule)
+ llvm::MinidumpYAML::ThreadListStream::entry_type)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<llvm::MinidumpYAML::Stream>)
-LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ModuleListStream::ParsedModule)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ModuleListStream::entry_type)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ThreadListStream::entry_type)
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::MinidumpYAML::Object)
case StreamType::LinuxProcStat:
case StreamType::LinuxProcUptime:
return StreamKind::TextContent;
+ case StreamType::ThreadList:
+ return StreamKind::ThreadList;
default:
return StreamKind::RawContent;
}
return llvm::make_unique<SystemInfoStream>();
case StreamKind::TextContent:
return llvm::make_unique<TextContentStream>(Type);
+ case StreamKind::ThreadList:
+ return llvm::make_unique<ThreadListStream>();
}
llvm_unreachable("Unhandled stream kind!");
}
mapOptionalHex(IO, "File Date Low", Info.FileDateLow, 0);
}
-void yaml::MappingTraits<ModuleListStream::ParsedModule>::mapping(
- IO &IO, ModuleListStream::ParsedModule &M) {
- mapRequiredHex(IO, "Base of Image", M.Module.BaseOfImage);
- mapRequiredHex(IO, "Size of Image", M.Module.SizeOfImage);
- mapOptionalHex(IO, "Checksum", M.Module.Checksum, 0);
- IO.mapOptional("Time Date Stamp", M.Module.TimeDateStamp,
+void yaml::MappingTraits<ModuleListStream::entry_type>::mapping(
+ IO &IO, ModuleListStream::entry_type &M) {
+ mapRequiredHex(IO, "Base of Image", M.Entry.BaseOfImage);
+ mapRequiredHex(IO, "Size of Image", M.Entry.SizeOfImage);
+ mapOptionalHex(IO, "Checksum", M.Entry.Checksum, 0);
+ IO.mapOptional("Time Date Stamp", M.Entry.TimeDateStamp,
support::ulittle32_t(0));
IO.mapRequired("Module Name", M.Name);
- IO.mapOptional("Version Info", M.Module.VersionInfo, VSFixedFileInfo());
+ IO.mapOptional("Version Info", M.Entry.VersionInfo, VSFixedFileInfo());
IO.mapRequired("CodeView Record", M.CvRecord);
IO.mapOptional("Misc Record", M.MiscRecord, yaml::BinaryRef());
- mapOptionalHex(IO, "Reserved0", M.Module.Reserved0, 0);
- mapOptionalHex(IO, "Reserved1", M.Module.Reserved1, 0);
+ mapOptionalHex(IO, "Reserved0", M.Entry.Reserved0, 0);
+ mapOptionalHex(IO, "Reserved1", M.Entry.Reserved1, 0);
}
static void streamMapping(yaml::IO &IO, RawContentStream &Stream) {
}
static void streamMapping(yaml::IO &IO, ModuleListStream &Stream) {
- IO.mapRequired("Modules", Stream.Modules);
+ IO.mapRequired("Modules", Stream.Entries);
}
static void streamMapping(yaml::IO &IO, SystemInfoStream &Stream) {
IO.mapOptional("Text", Stream.Text);
}
+void yaml::MappingContextTraits<MemoryDescriptor, yaml::BinaryRef>::mapping(
+ IO &IO, MemoryDescriptor &Memory, BinaryRef &Content) {
+ mapRequiredHex(IO, "Start of Memory Range", Memory.StartOfMemoryRange);
+ IO.mapRequired("Content", Content);
+}
+
+void yaml::MappingTraits<ThreadListStream::entry_type>::mapping(
+ IO &IO, ThreadListStream::entry_type &T) {
+ mapRequiredHex(IO, "Thread Id", T.Entry.ThreadId);
+ mapOptionalHex(IO, "Suspend Count", T.Entry.SuspendCount, 0);
+ mapOptionalHex(IO, "Priority Class", T.Entry.PriorityClass, 0);
+ mapOptionalHex(IO, "Priority", T.Entry.Priority, 0);
+ mapOptionalHex(IO, "Environment Block", T.Entry.EnvironmentBlock, 0);
+ IO.mapRequired("Context", T.Context);
+ IO.mapRequired("Stack", T.Entry.Stack, T.Stack);
+}
+
+static void streamMapping(yaml::IO &IO, ThreadListStream &Stream) {
+ IO.mapRequired("Threads", Stream.Entries);
+}
+
void yaml::MappingTraits<std::unique_ptr<Stream>>::mapping(
yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S) {
StreamType Type;
case MinidumpYAML::Stream::StreamKind::TextContent:
streamMapping(IO, llvm::cast<TextContentStream>(*S));
break;
+ case MinidumpYAML::Stream::StreamKind::ThreadList:
+ streamMapping(IO, llvm::cast<ThreadListStream>(*S));
+ break;
}
}
case MinidumpYAML::Stream::StreamKind::ModuleList:
case MinidumpYAML::Stream::StreamKind::SystemInfo:
case MinidumpYAML::Stream::StreamKind::TextContent:
+ case MinidumpYAML::Stream::StreamKind::ThreadList:
return "";
}
llvm_unreachable("Fully covered switch above!");
IO.mapRequired("Streams", O.Streams);
}
+static LocationDescriptor layout(BlobAllocator &File, yaml::BinaryRef Data) {
+ return {support::ulittle32_t(Data.binary_size()),
+ support::ulittle32_t(File.allocateBytes(Data))};
+}
+
+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) {
- case Stream::StreamKind::ModuleList: {
- ModuleListStream &List = cast<ModuleListStream>(S);
-
- File.allocateNewObject<support::ulittle32_t>(List.Modules.size());
- for (ModuleListStream::ParsedModule &M : List.Modules)
- File.allocateObject(M.Module);
-
- // Module names and CodeView/Misc records are not a part of the stream.
- DataEnd = File.tell();
- for (ModuleListStream::ParsedModule &M : List.Modules) {
- M.Module.ModuleNameRVA = File.allocateString(M.Name);
-
- M.Module.CvRecord.RVA = File.allocateBytes(M.CvRecord);
- M.Module.CvRecord.DataSize = M.CvRecord.binary_size();
-
- M.Module.MiscRecord.RVA = File.allocateBytes(M.MiscRecord);
- M.Module.MiscRecord.DataSize = M.MiscRecord.binary_size();
- }
+ 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) {
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.
auto ExpectedList = File.getModuleList();
if (!ExpectedList)
return ExpectedList.takeError();
- std::vector<ModuleListStream::ParsedModule> Modules;
+ std::vector<ModuleListStream::entry_type> Modules;
for (const Module &M : *ExpectedList) {
auto ExpectedName = File.getString(M.ModuleNameRVA);
if (!ExpectedName)
case StreamKind::TextContent:
return llvm::make_unique<TextContentStream>(
StreamDesc.Type, toStringRef(File.getRawStream(StreamDesc)));
+ case StreamKind::ThreadList: {
+ auto ExpectedList = File.getThreadList();
+ if (!ExpectedList)
+ return ExpectedList.takeError();
+ std::vector<ThreadListStream::entry_type> Threads;
+ for (const Thread &T : *ExpectedList) {
+ auto ExpectedStack = File.getRawData(T.Stack.Memory);
+ if (!ExpectedStack)
+ return ExpectedStack.takeError();
+ auto ExpectedContext = File.getRawData(T.Context);
+ if (!ExpectedContext)
+ return ExpectedContext.takeError();
+ Threads.push_back({T, *ExpectedStack, *ExpectedContext});
+ }
+ return llvm::make_unique<ThreadListStream>(std::move(Threads));
+ }
}
llvm_unreachable("Unhandled stream kind!");
}
# RUN: yaml2obj %s | obj2yaml - | FileCheck %s
--- !minidump
-Streams:
+Streams:
- Type: SystemInfo
Processor Arch: ARM64
Platform ID: Linux
CSD Version: Linux 3.13.0-91-generic
- CPU:
+ CPU:
CPUID: 0x05060708
- Type: LinuxAuxv
Content: DEADBEEFBAADF00D
400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process
- Type: ModuleList
- Modules:
+ Modules:
- Base of Image: 0x0001020304050607
Size of Image: 0x08090A0B
Checksum: 0x0C0D0E0F
Time Date Stamp: 47
Module Name: a.out
- Version Info:
- Signature: 0x10111213
- Struct Version: 0x14151617
- File Version High: 0x18191A1B
- File Version Low: 0x1C1D1E1F
+ Version Info:
+ Signature: 0x10111213
+ Struct Version: 0x14151617
+ File Version High: 0x18191A1B
+ File Version Low: 0x1C1D1E1F
Product Version High: 0x20212223
- Product Version Low: 0x24252627
- File Flags Mask: 0x28292A2B
- File Flags: 0x2C2D2E2F
- File OS: 0x30313233
- File Type: 0x34353637
- File Subtype: 0x38393A3B
- File Date High: 0x3C3D3E3F
- File Date Low: 0x40414243
+ Product Version Low: 0x24252627
+ File Flags Mask: 0x28292A2B
+ File Flags: 0x2C2D2E2F
+ File OS: 0x30313233
+ File Type: 0x34353637
+ File Subtype: 0x38393A3B
+ File Date High: 0x3C3D3E3F
+ File Date Low: 0x40414243
CodeView Record: '44454647'
Misc Record: 48494A4B
- Base of Image: 0x4C4D4E4F50515253
Size of Image: 0x54555657
Module Name: libb.so
CodeView Record: 58595A5B
+ - Type: ThreadList
+ Threads:
+ - Thread Id: 0x5C5D5E5F
+ Priority Class: 0x60616263
+ Environment Block: 0x6465666768696A6B
+ Context: 7C7D7E7F80818283
+ Stack:
+ Start of Memory Range: 0x6C6D6E6F70717273
+ Content: 7475767778797A7B
...
# CHECK: --- !minidump
-# CHECK-NEXT: Streams:
+# CHECK-NEXT: Streams:
# CHECK-NEXT: - Type: SystemInfo
# CHECK-NEXT: Processor Arch: ARM64
# CHECK-NEXT: Platform ID: Linux
# CHECK-NEXT: CSD Version: Linux 3.13.0-91-generic
-# CHECK-NEXT: CPU:
+# CHECK-NEXT: CPU:
# CHECK-NEXT: CPUID: 0x05060708
# CHECK-NEXT: - Type: LinuxAuxv
# CHECK-NEXT: Content: DEADBEEFBAADF00D
# CHECK-NEXT: 400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process
# CHECK-EMPTY:
# CHECK-NEXT: - Type: ModuleList
-# CHECK-NEXT: Modules:
+# CHECK-NEXT: Modules:
# CHECK-NEXT: - Base of Image: 0x0001020304050607
# CHECK-NEXT: Size of Image: 0x08090A0B
# CHECK-NEXT: Checksum: 0x0C0D0E0F
# CHECK-NEXT: Time Date Stamp: 47
# CHECK-NEXT: Module Name: a.out
-# CHECK-NEXT: Version Info:
-# CHECK-NEXT: Signature: 0x10111213
-# CHECK-NEXT: Struct Version: 0x14151617
-# CHECK-NEXT: File Version High: 0x18191A1B
-# CHECK-NEXT: File Version Low: 0x1C1D1E1F
+# CHECK-NEXT: Version Info:
+# CHECK-NEXT: Signature: 0x10111213
+# CHECK-NEXT: Struct Version: 0x14151617
+# CHECK-NEXT: File Version High: 0x18191A1B
+# CHECK-NEXT: File Version Low: 0x1C1D1E1F
# CHECK-NEXT: Product Version High: 0x20212223
-# CHECK-NEXT: Product Version Low: 0x24252627
-# CHECK-NEXT: File Flags Mask: 0x28292A2B
-# CHECK-NEXT: File Flags: 0x2C2D2E2F
-# CHECK-NEXT: File OS: 0x30313233
-# CHECK-NEXT: File Type: 0x34353637
-# CHECK-NEXT: File Subtype: 0x38393A3B
-# CHECK-NEXT: File Date High: 0x3C3D3E3F
-# CHECK-NEXT: File Date Low: 0x40414243
+# CHECK-NEXT: Product Version Low: 0x24252627
+# CHECK-NEXT: File Flags Mask: 0x28292A2B
+# CHECK-NEXT: File Flags: 0x2C2D2E2F
+# CHECK-NEXT: File OS: 0x30313233
+# CHECK-NEXT: File Type: 0x34353637
+# CHECK-NEXT: File Subtype: 0x38393A3B
+# CHECK-NEXT: File Date High: 0x3C3D3E3F
+# CHECK-NEXT: File Date Low: 0x40414243
# CHECK-NEXT: CodeView Record: '44454647'
# CHECK-NEXT: Misc Record: 48494A4B
# CHECK-NEXT: - Base of Image: 0x4C4D4E4F50515253
# CHECK-NEXT: Size of Image: 0x54555657
# CHECK-NEXT: Module Name: libb.so
# CHECK-NEXT: CodeView Record: 58595A5B
+# CHECK-NEXT: - Type: ThreadList
+# CHECK-NEXT: Threads:
+# CHECK-NEXT: - Thread Id: 0x5C5D5E5F
+# CHECK-NEXT: Priority Class: 0x60616263
+# CHECK-NEXT: Environment Block: 0x6465666768696A6B
+# CHECK-NEXT: Context: 7C7D7E7F80818283
+# CHECK-NEXT: Stack:
+# CHECK-NEXT: Start of Memory Range: 0x6C6D6E6F70717273
+# CHECK-NEXT: Content: 7475767778797A7B
# CHECK-NEXT: ...