From c69acccf9709e8a1812495403bf4aeecc9c1ab2e Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Thu, 2 May 2019 07:45:42 +0000 Subject: [PATCH] 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 --- include/llvm/BinaryFormat/Minidump.h | 21 +++++++++ include/llvm/Object/Minidump.h | 18 +++++++- lib/Object/Minidump.cpp | 19 +++++--- unittests/Object/MinidumpTest.cpp | 69 +++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 9 deletions(-) diff --git a/include/llvm/BinaryFormat/Minidump.h b/include/llvm/BinaryFormat/Minidump.h index 8f3c170f031..65c17d1eb00 100644 --- a/include/llvm/BinaryFormat/Minidump.h +++ b/include/llvm/BinaryFormat/Minidump.h @@ -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 { diff --git a/include/llvm/Object/Minidump.h b/include/llvm/Object/Minidump.h index e234f9c154a..b6ea2da2743 100644 --- a/include/llvm/Object/Minidump.h +++ b/include/llvm/Object/Minidump.h @@ -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> getModuleList() const; + Expected> getModuleList() const { + return getListStream(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> getThreadList() const { + return getListStream(minidump::StreamType::ThreadList); + } private: static Error createError(StringRef Str) { @@ -105,6 +116,11 @@ private: template Expected 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 + Expected> getListStream(minidump::StreamType Stream) const; + const minidump::Header &Header; ArrayRef Streams; DenseMap StreamMap; diff --git a/lib/Object/Minidump.cpp b/lib/Object/Minidump.cpp index 4b1abe52994..a5de579a627 100644 --- a/lib/Object/Minidump.cpp +++ b/lib/Object/Minidump.cpp @@ -53,8 +53,9 @@ Expected MinidumpFile::getString(size_t Offset) const { return Result; } -Expected> MinidumpFile::getModuleList() const { - auto OptionalStream = getRawStream(StreamType::ModuleList); +template +Expected> MinidumpFile::getListStream(StreamType Stream) const { + auto OptionalStream = getRawStream(Stream); if (!OptionalStream) return createError("No such stream"); auto ExpectedSize = @@ -65,14 +66,18 @@ Expected> 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(*OptionalStream, ListOffset, ListSize); + return getDataSliceAs(*OptionalStream, ListOffset, ListSize); } +template Expected> + MinidumpFile::getListStream(StreamType) const; +template Expected> + MinidumpFile::getListStream(StreamType) const; Expected> MinidumpFile::getDataSlice(ArrayRef Data, size_t Offset, size_t Size) { diff --git a/unittests/Object/MinidumpTest.cpp b/unittests/Object/MinidumpTest.cpp index fdb9d22db04..98e31323c99 100644 --- a/unittests/Object/MinidumpTest.cpp +++ b/unittests/Object/MinidumpTest.cpp @@ -343,7 +343,7 @@ TEST(MinidumpFile, getModuleList) { 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1 }; - for (const std::vector &Data : {OneModule, PaddedModule}) { + for (ArrayRef 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()); } + +TEST(MinidumpFile, getThreadList) { + std::vector 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 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 Data : {OneThread, PaddedThread}) { + auto ExpectedFile = create(Data); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + const MinidumpFile &File = **ExpectedFile; + Expected> 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); + } +} -- 2.50.1