Object/Minidump: Add support for the ThreadList stream

Summary:
The stream contains the list of threads belonging to the process
described by the minidump. Its structure is the same as the ModuleList
stream, and in fact, I have generalized the ModuleList reading code to
handle this stream too.

Reviewers: amccarth, jhenderson, clayborg

Subscribers: llvm-commits, lldb-commits, markmentovai, zturner

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D61064

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@359762 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Pavel Labath 2019-05-02 07:45:42 +00:00
parent a65cc965db
commit c69acccf97
4 changed files with 118 additions and 9 deletions

View File

@ -59,6 +59,14 @@ struct LocationDescriptor {
};
static_assert(sizeof(LocationDescriptor) == 8, "");
/// Describes a single memory range (both its VM address and where to find it in
/// the file) of the process from which this minidump file was generated.
struct MemoryDescriptor {
support::ulittle64_t StartOfMemoryRange;
LocationDescriptor Memory;
};
static_assert(sizeof(MemoryDescriptor) == 16, "");
/// Specifies the location and type of a single stream in the minidump file. The
/// minidump stream directory is an array of entries of this type, with its size
/// given by Header.NumberOfStreams.
@ -159,6 +167,19 @@ struct Module {
};
static_assert(sizeof(Module) == 108, "");
/// Describes a single thread in the minidump file. Part of the ThreadList
/// stream.
struct Thread {
support::ulittle32_t ThreadId;
support::ulittle32_t SuspendCount;
support::ulittle32_t PriorityClass;
support::ulittle32_t Priority;
support::ulittle64_t EnvironmentBlock;
MemoryDescriptor Stack;
LocationDescriptor Context;
};
static_assert(sizeof(Thread) == 48, "");
} // namespace minidump
template <> struct DenseMapInfo<minidump::StreamType> {

View File

@ -67,7 +67,18 @@ public:
/// not large enough to contain the number of modules declared in the stream
/// header. The consistency of the Module entries themselves is not checked in
/// any way.
Expected<ArrayRef<minidump::Module>> getModuleList() const;
Expected<ArrayRef<minidump::Module>> getModuleList() const {
return getListStream<minidump::Module>(minidump::StreamType::ModuleList);
}
/// Returns the thread list embedded in the ThreadList stream. An error is
/// returned if the file does not contain this stream, or if the stream is
/// not large enough to contain the number of threads declared in the stream
/// header. The consistency of the Thread entries themselves is not checked in
/// any way.
Expected<ArrayRef<minidump::Thread>> getThreadList() const {
return getListStream<minidump::Thread>(minidump::StreamType::ThreadList);
}
private:
static Error createError(StringRef Str) {
@ -105,6 +116,11 @@ private:
template <typename T>
Expected<const T &> getStream(minidump::StreamType Stream) const;
/// Return the contents of a stream which contains a list of fixed-size items,
/// prefixed by the list size.
template <typename T>
Expected<ArrayRef<T>> getListStream(minidump::StreamType Stream) const;
const minidump::Header &Header;
ArrayRef<minidump::Directory> Streams;
DenseMap<minidump::StreamType, std::size_t> StreamMap;

View File

@ -53,8 +53,9 @@ Expected<std::string> MinidumpFile::getString(size_t Offset) const {
return Result;
}
Expected<ArrayRef<Module>> MinidumpFile::getModuleList() const {
auto OptionalStream = getRawStream(StreamType::ModuleList);
template <typename T>
Expected<ArrayRef<T>> MinidumpFile::getListStream(StreamType Stream) const {
auto OptionalStream = getRawStream(Stream);
if (!OptionalStream)
return createError("No such stream");
auto ExpectedSize =
@ -65,14 +66,18 @@ Expected<ArrayRef<Module>> MinidumpFile::getModuleList() const {
size_t ListSize = ExpectedSize.get()[0];
size_t ListOffset = 4;
// Some producers insert additional padding bytes to align the module list to
// 8-byte boundary. Check for that by comparing the module list size with the
// overall stream size.
if (ListOffset + sizeof(Module) * ListSize < OptionalStream->size())
// Some producers insert additional padding bytes to align the list to an
// 8-byte boundary. Check for that by comparing the list size with the overall
// stream size.
if (ListOffset + sizeof(T) * ListSize < OptionalStream->size())
ListOffset = 8;
return getDataSliceAs<Module>(*OptionalStream, ListOffset, ListSize);
return getDataSliceAs<T>(*OptionalStream, ListOffset, ListSize);
}
template Expected<ArrayRef<Module>>
MinidumpFile::getListStream(StreamType) const;
template Expected<ArrayRef<Thread>>
MinidumpFile::getListStream(StreamType) const;
Expected<ArrayRef<uint8_t>>
MinidumpFile::getDataSlice(ArrayRef<uint8_t> Data, size_t Offset, size_t Size) {

View File

@ -343,7 +343,7 @@ TEST(MinidumpFile, getModuleList) {
5, 6, 7, 8, 9, 0, 1, 2, // Reserved1
};
for (const std::vector<uint8_t> &Data : {OneModule, PaddedModule}) {
for (ArrayRef<uint8_t> Data : {OneModule, PaddedModule}) {
auto ExpectedFile = create(Data);
ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
const MinidumpFile &File = **ExpectedFile;
@ -396,3 +396,70 @@ TEST(MinidumpFile, getModuleList) {
const MinidumpFile &File = **ExpectedFile;
EXPECT_THAT_EXPECTED(File.getModuleList(), Failed<BinaryError>());
}
TEST(MinidumpFile, getThreadList) {
std::vector<uint8_t> OneThread{
// Header
'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
1, 0, 0, 0, // NumberOfStreams,
32, 0, 0, 0, // StreamDirectoryRVA
0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
0, 0, 0, 0, 0, 0, 0, 0, // Flags
// Stream Directory
3, 0, 0, 0, 52, 0, 0, 0, // Type, DataSize,
44, 0, 0, 0, // RVA
// ThreadList
1, 0, 0, 0, // NumberOfThreads
1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount
9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority
7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock
// Stack
5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange
3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA
// Context
1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA
};
// Same as before, but with a padded thread list.
std::vector<uint8_t> PaddedThread{
// Header
'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
1, 0, 0, 0, // NumberOfStreams,
32, 0, 0, 0, // StreamDirectoryRVA
0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
0, 0, 0, 0, 0, 0, 0, 0, // Flags
// Stream Directory
3, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize,
44, 0, 0, 0, // RVA
// ThreadList
1, 0, 0, 0, // NumberOfThreads
0, 0, 0, 0, // Padding
1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount
9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority
7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock
// Stack
5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange
3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA
// Context
1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA
};
for (ArrayRef<uint8_t> Data : {OneThread, PaddedThread}) {
auto ExpectedFile = create(Data);
ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
const MinidumpFile &File = **ExpectedFile;
Expected<ArrayRef<Thread>> ExpectedThread = File.getThreadList();
ASSERT_THAT_EXPECTED(ExpectedThread, Succeeded());
ASSERT_EQ(1u, ExpectedThread->size());
const Thread &T = ExpectedThread.get()[0];
EXPECT_EQ(0x04030201u, T.ThreadId);
EXPECT_EQ(0x08070605u, T.SuspendCount);
EXPECT_EQ(0x02010009u, T.PriorityClass);
EXPECT_EQ(0x06050403u, T.Priority);
EXPECT_EQ(0x0403020100090807u, T.EnvironmentBlock);
EXPECT_EQ(0x0201000908070605u, T.Stack.StartOfMemoryRange);
EXPECT_EQ(0x06050403u, T.Stack.Memory.DataSize);
EXPECT_EQ(0x00090807u, T.Stack.Memory.RVA);
EXPECT_EQ(0x04030201u, T.Context.DataSize);
EXPECT_EQ(0x08070605u, T.Context.RVA);
}
}