Index: llvm/trunk/include/llvm/BinaryFormat/Minidump.h =================================================================== --- llvm/trunk/include/llvm/BinaryFormat/Minidump.h +++ llvm/trunk/include/llvm/BinaryFormat/Minidump.h @@ -59,6 +59,14 @@ }; 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 @@ }; 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 { Index: llvm/trunk/include/llvm/Object/Minidump.h =================================================================== --- llvm/trunk/include/llvm/Object/Minidump.h +++ llvm/trunk/include/llvm/Object/Minidump.h @@ -67,7 +67,18 @@ /// 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 @@ 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; Index: llvm/trunk/lib/Object/Minidump.cpp =================================================================== --- llvm/trunk/lib/Object/Minidump.cpp +++ llvm/trunk/lib/Object/Minidump.cpp @@ -53,8 +53,9 @@ 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 @@ 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) { Index: llvm/trunk/unittests/Object/MinidumpTest.cpp =================================================================== --- llvm/trunk/unittests/Object/MinidumpTest.cpp +++ llvm/trunk/unittests/Object/MinidumpTest.cpp @@ -343,7 +343,7 @@ 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 @@ 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); + } +}