Skip to content

Commit dcdb3c6

Browse files
committed
MinidumpYAML: add support for the ThreadList stream
Summary: The implementation is a pretty straightforward extension of the pattern used for (de)serializing the ModuleList stream. Since there are other streams which use the same format (MemoryList and MemoryList64, at least). I tried to generalize the code a bit so that adding future streams of this type can be done with less code. Reviewers: amccarth, jhenderson, clayborg Subscribers: markmentovai, lldb-commits, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D61423 llvm-svn: 360350
1 parent 40177ac commit dcdb3c6

File tree

3 files changed

+194
-84
lines changed

3 files changed

+194
-84
lines changed

llvm/include/llvm/ObjectYAML/MinidumpYAML.h

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct Stream {
3030
RawContent,
3131
SystemInfo,
3232
TextContent,
33+
ThreadList,
3334
};
3435

3536
Stream(StreamKind Kind, minidump::StreamType Type) : Kind(Kind), Type(Type) {}
@@ -50,30 +51,46 @@ struct Stream {
5051
const object::MinidumpFile &File);
5152
};
5253

53-
/// A stream representing the list of modules loaded in the process. On disk, it
54-
/// is represented as a sequence of minidump::Module structures. These contain
55-
/// pointers to other data structures, like the module's name and CodeView
56-
/// record. In memory, we represent these as the ParsedModule struct, which
57-
/// groups minidump::Module with all of its dependant structures in a single
58-
/// entity.
59-
struct ModuleListStream : public Stream {
60-
struct ParsedModule {
61-
minidump::Module Module;
62-
std::string Name;
63-
yaml::BinaryRef CvRecord;
64-
yaml::BinaryRef MiscRecord;
65-
};
66-
std::vector<ParsedModule> Modules;
54+
namespace detail {
55+
/// A stream representing a list of abstract entries in a minidump stream. Its
56+
/// instantiations can be used to represent the ModuleList stream and other
57+
/// streams with a similar structure.
58+
template <typename EntryT> struct ListStream : public Stream {
59+
using entry_type = EntryT;
6760

68-
ModuleListStream(std::vector<ParsedModule> Modules = {})
69-
: Stream(StreamKind::ModuleList, minidump::StreamType::ModuleList),
70-
Modules(std::move(Modules)) {}
61+
std::vector<entry_type> Entries;
7162

72-
static bool classof(const Stream *S) {
73-
return S->Kind == StreamKind::ModuleList;
74-
}
63+
explicit ListStream(std::vector<entry_type> Entries = {})
64+
: Stream(EntryT::Kind, EntryT::Type), Entries(std::move(Entries)) {}
65+
66+
static bool classof(const Stream *S) { return S->Kind == EntryT::Kind; }
7567
};
7668

69+
/// A structure containing all data belonging to a single minidump module.
70+
struct ParsedModule {
71+
static constexpr Stream::StreamKind Kind = Stream::StreamKind::ModuleList;
72+
static constexpr minidump::StreamType Type = minidump::StreamType::ModuleList;
73+
74+
minidump::Module Entry;
75+
std::string Name;
76+
yaml::BinaryRef CvRecord;
77+
yaml::BinaryRef MiscRecord;
78+
};
79+
80+
/// A structure containing all data belonging to a single minidump thread.
81+
struct ParsedThread {
82+
static constexpr Stream::StreamKind Kind = Stream::StreamKind::ThreadList;
83+
static constexpr minidump::StreamType Type = minidump::StreamType::ThreadList;
84+
85+
minidump::Thread Entry;
86+
yaml::BinaryRef Stack;
87+
yaml::BinaryRef Context;
88+
};
89+
} // namespace detail
90+
91+
using ModuleListStream = detail::ListStream<detail::ParsedModule>;
92+
using ThreadListStream = detail::ListStream<detail::ParsedThread>;
93+
7794
/// A minidump stream represented as a sequence of hex bytes. This is used as a
7895
/// fallback when no other stream kind is suitable.
7996
struct RawContentStream : public Stream {
@@ -176,6 +193,11 @@ template <> struct MappingTraits<std::unique_ptr<MinidumpYAML::Stream>> {
176193
static StringRef validate(IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S);
177194
};
178195

196+
template <> struct MappingContextTraits<minidump::MemoryDescriptor, BinaryRef> {
197+
static void mapping(IO &IO, minidump::MemoryDescriptor &Memory,
198+
BinaryRef &Content);
199+
};
200+
179201
} // namespace yaml
180202

181203
} // namespace llvm
@@ -188,11 +210,15 @@ LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::CPUInfo::ArmInfo)
188210
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::CPUInfo::OtherInfo)
189211
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::CPUInfo::X86Info)
190212
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::VSFixedFileInfo)
213+
214+
LLVM_YAML_DECLARE_MAPPING_TRAITS(
215+
llvm::MinidumpYAML::ModuleListStream::entry_type)
191216
LLVM_YAML_DECLARE_MAPPING_TRAITS(
192-
llvm::MinidumpYAML::ModuleListStream::ParsedModule)
217+
llvm::MinidumpYAML::ThreadListStream::entry_type)
193218

194219
LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<llvm::MinidumpYAML::Stream>)
195-
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ModuleListStream::ParsedModule)
220+
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ModuleListStream::entry_type)
221+
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ThreadListStream::entry_type)
196222

197223
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::MinidumpYAML::Object)
198224

llvm/lib/ObjectYAML/MinidumpYAML.cpp

Lines changed: 96 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ Stream::StreamKind Stream::getKind(StreamType Type) {
180180
case StreamType::LinuxProcStat:
181181
case StreamType::LinuxProcUptime:
182182
return StreamKind::TextContent;
183+
case StreamType::ThreadList:
184+
return StreamKind::ThreadList;
183185
default:
184186
return StreamKind::RawContent;
185187
}
@@ -196,6 +198,8 @@ std::unique_ptr<Stream> Stream::create(StreamType Type) {
196198
return llvm::make_unique<SystemInfoStream>();
197199
case StreamKind::TextContent:
198200
return llvm::make_unique<TextContentStream>(Type);
201+
case StreamKind::ThreadList:
202+
return llvm::make_unique<ThreadListStream>();
199203
}
200204
llvm_unreachable("Unhandled stream kind!");
201205
}
@@ -323,19 +327,19 @@ void yaml::MappingTraits<VSFixedFileInfo>::mapping(IO &IO,
323327
mapOptionalHex(IO, "File Date Low", Info.FileDateLow, 0);
324328
}
325329

326-
void yaml::MappingTraits<ModuleListStream::ParsedModule>::mapping(
327-
IO &IO, ModuleListStream::ParsedModule &M) {
328-
mapRequiredHex(IO, "Base of Image", M.Module.BaseOfImage);
329-
mapRequiredHex(IO, "Size of Image", M.Module.SizeOfImage);
330-
mapOptionalHex(IO, "Checksum", M.Module.Checksum, 0);
331-
IO.mapOptional("Time Date Stamp", M.Module.TimeDateStamp,
330+
void yaml::MappingTraits<ModuleListStream::entry_type>::mapping(
331+
IO &IO, ModuleListStream::entry_type &M) {
332+
mapRequiredHex(IO, "Base of Image", M.Entry.BaseOfImage);
333+
mapRequiredHex(IO, "Size of Image", M.Entry.SizeOfImage);
334+
mapOptionalHex(IO, "Checksum", M.Entry.Checksum, 0);
335+
IO.mapOptional("Time Date Stamp", M.Entry.TimeDateStamp,
332336
support::ulittle32_t(0));
333337
IO.mapRequired("Module Name", M.Name);
334-
IO.mapOptional("Version Info", M.Module.VersionInfo, VSFixedFileInfo());
338+
IO.mapOptional("Version Info", M.Entry.VersionInfo, VSFixedFileInfo());
335339
IO.mapRequired("CodeView Record", M.CvRecord);
336340
IO.mapOptional("Misc Record", M.MiscRecord, yaml::BinaryRef());
337-
mapOptionalHex(IO, "Reserved0", M.Module.Reserved0, 0);
338-
mapOptionalHex(IO, "Reserved1", M.Module.Reserved1, 0);
341+
mapOptionalHex(IO, "Reserved0", M.Entry.Reserved0, 0);
342+
mapOptionalHex(IO, "Reserved1", M.Entry.Reserved1, 0);
339343
}
340344

341345
static void streamMapping(yaml::IO &IO, RawContentStream &Stream) {
@@ -350,7 +354,7 @@ static StringRef streamValidate(RawContentStream &Stream) {
350354
}
351355

352356
static void streamMapping(yaml::IO &IO, ModuleListStream &Stream) {
353-
IO.mapRequired("Modules", Stream.Modules);
357+
IO.mapRequired("Modules", Stream.Entries);
354358
}
355359

356360
static void streamMapping(yaml::IO &IO, SystemInfoStream &Stream) {
@@ -386,6 +390,27 @@ static void streamMapping(yaml::IO &IO, TextContentStream &Stream) {
386390
IO.mapOptional("Text", Stream.Text);
387391
}
388392

393+
void yaml::MappingContextTraits<MemoryDescriptor, yaml::BinaryRef>::mapping(
394+
IO &IO, MemoryDescriptor &Memory, BinaryRef &Content) {
395+
mapRequiredHex(IO, "Start of Memory Range", Memory.StartOfMemoryRange);
396+
IO.mapRequired("Content", Content);
397+
}
398+
399+
void yaml::MappingTraits<ThreadListStream::entry_type>::mapping(
400+
IO &IO, ThreadListStream::entry_type &T) {
401+
mapRequiredHex(IO, "Thread Id", T.Entry.ThreadId);
402+
mapOptionalHex(IO, "Suspend Count", T.Entry.SuspendCount, 0);
403+
mapOptionalHex(IO, "Priority Class", T.Entry.PriorityClass, 0);
404+
mapOptionalHex(IO, "Priority", T.Entry.Priority, 0);
405+
mapOptionalHex(IO, "Environment Block", T.Entry.EnvironmentBlock, 0);
406+
IO.mapRequired("Context", T.Context);
407+
IO.mapRequired("Stack", T.Entry.Stack, T.Stack);
408+
}
409+
410+
static void streamMapping(yaml::IO &IO, ThreadListStream &Stream) {
411+
IO.mapRequired("Threads", Stream.Entries);
412+
}
413+
389414
void yaml::MappingTraits<std::unique_ptr<Stream>>::mapping(
390415
yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S) {
391416
StreamType Type;
@@ -408,6 +433,9 @@ void yaml::MappingTraits<std::unique_ptr<Stream>>::mapping(
408433
case MinidumpYAML::Stream::StreamKind::TextContent:
409434
streamMapping(IO, llvm::cast<TextContentStream>(*S));
410435
break;
436+
case MinidumpYAML::Stream::StreamKind::ThreadList:
437+
streamMapping(IO, llvm::cast<ThreadListStream>(*S));
438+
break;
411439
}
412440
}
413441

@@ -419,6 +447,7 @@ StringRef yaml::MappingTraits<std::unique_ptr<Stream>>::validate(
419447
case MinidumpYAML::Stream::StreamKind::ModuleList:
420448
case MinidumpYAML::Stream::StreamKind::SystemInfo:
421449
case MinidumpYAML::Stream::StreamKind::TextContent:
450+
case MinidumpYAML::Stream::StreamKind::ThreadList:
422451
return "";
423452
}
424453
llvm_unreachable("Fully covered switch above!");
@@ -432,32 +461,50 @@ void yaml::MappingTraits<Object>::mapping(IO &IO, Object &O) {
432461
IO.mapRequired("Streams", O.Streams);
433462
}
434463

464+
static LocationDescriptor layout(BlobAllocator &File, yaml::BinaryRef Data) {
465+
return {support::ulittle32_t(Data.binary_size()),
466+
support::ulittle32_t(File.allocateBytes(Data))};
467+
}
468+
469+
static void layout(BlobAllocator &File, ModuleListStream::entry_type &M) {
470+
M.Entry.ModuleNameRVA = File.allocateString(M.Name);
471+
472+
M.Entry.CvRecord = layout(File, M.CvRecord);
473+
M.Entry.MiscRecord = layout(File, M.MiscRecord);
474+
}
475+
476+
static void layout(BlobAllocator &File, ThreadListStream::entry_type &T) {
477+
T.Entry.Stack.Memory = layout(File, T.Stack);
478+
T.Entry.Context = layout(File, T.Context);
479+
}
480+
481+
template <typename EntryT>
482+
static size_t layout(BlobAllocator &File,
483+
MinidumpYAML::detail::ListStream<EntryT> &S) {
484+
485+
File.allocateNewObject<support::ulittle32_t>(S.Entries.size());
486+
for (auto &E : S.Entries)
487+
File.allocateObject(E.Entry);
488+
489+
size_t DataEnd = File.tell();
490+
491+
// Lay out the auxiliary data, (which is not a part of the stream).
492+
DataEnd = File.tell();
493+
for (auto &E : S.Entries)
494+
layout(File, E);
495+
496+
return DataEnd;
497+
}
498+
435499
static Directory layout(BlobAllocator &File, Stream &S) {
436500
Directory Result;
437501
Result.Type = S.Type;
438502
Result.Location.RVA = File.tell();
439503
Optional<size_t> DataEnd;
440504
switch (S.Kind) {
441-
case Stream::StreamKind::ModuleList: {
442-
ModuleListStream &List = cast<ModuleListStream>(S);
443-
444-
File.allocateNewObject<support::ulittle32_t>(List.Modules.size());
445-
for (ModuleListStream::ParsedModule &M : List.Modules)
446-
File.allocateObject(M.Module);
447-
448-
// Module names and CodeView/Misc records are not a part of the stream.
449-
DataEnd = File.tell();
450-
for (ModuleListStream::ParsedModule &M : List.Modules) {
451-
M.Module.ModuleNameRVA = File.allocateString(M.Name);
452-
453-
M.Module.CvRecord.RVA = File.allocateBytes(M.CvRecord);
454-
M.Module.CvRecord.DataSize = M.CvRecord.binary_size();
455-
456-
M.Module.MiscRecord.RVA = File.allocateBytes(M.MiscRecord);
457-
M.Module.MiscRecord.DataSize = M.MiscRecord.binary_size();
458-
}
505+
case Stream::StreamKind::ModuleList:
506+
DataEnd = layout(File, cast<ModuleListStream>(S));
459507
break;
460-
}
461508
case Stream::StreamKind::RawContent: {
462509
RawContentStream &Raw = cast<RawContentStream>(S);
463510
File.allocateCallback(Raw.Size, [&Raw](raw_ostream &OS) {
@@ -478,6 +525,9 @@ static Directory layout(BlobAllocator &File, Stream &S) {
478525
case Stream::StreamKind::TextContent:
479526
File.allocateArray(arrayRefFromStringRef(cast<TextContentStream>(S).Text));
480527
break;
528+
case Stream::StreamKind::ThreadList:
529+
DataEnd = layout(File, cast<ThreadListStream>(S));
530+
break;
481531
}
482532
// If DataEnd is not set, we assume everything we generated is a part of the
483533
// stream.
@@ -520,7 +570,7 @@ Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) {
520570
auto ExpectedList = File.getModuleList();
521571
if (!ExpectedList)
522572
return ExpectedList.takeError();
523-
std::vector<ModuleListStream::ParsedModule> Modules;
573+
std::vector<ModuleListStream::entry_type> Modules;
524574
for (const Module &M : *ExpectedList) {
525575
auto ExpectedName = File.getString(M.ModuleNameRVA);
526576
if (!ExpectedName)
@@ -552,6 +602,22 @@ Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) {
552602
case StreamKind::TextContent:
553603
return llvm::make_unique<TextContentStream>(
554604
StreamDesc.Type, toStringRef(File.getRawStream(StreamDesc)));
605+
case StreamKind::ThreadList: {
606+
auto ExpectedList = File.getThreadList();
607+
if (!ExpectedList)
608+
return ExpectedList.takeError();
609+
std::vector<ThreadListStream::entry_type> Threads;
610+
for (const Thread &T : *ExpectedList) {
611+
auto ExpectedStack = File.getRawData(T.Stack.Memory);
612+
if (!ExpectedStack)
613+
return ExpectedStack.takeError();
614+
auto ExpectedContext = File.getRawData(T.Context);
615+
if (!ExpectedContext)
616+
return ExpectedContext.takeError();
617+
Threads.push_back({T, *ExpectedStack, *ExpectedContext});
618+
}
619+
return llvm::make_unique<ThreadListStream>(std::move(Threads));
620+
}
555621
}
556622
llvm_unreachable("Unhandled stream kind!");
557623
}

0 commit comments

Comments
 (0)