llvm.org GIT mirror llvm / c69accc
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 Pavel Labath 1 year, 5 months ago
4 changed file(s) with 118 addition(s) and 9 deletion(s). Raw diff Collapse all Expand all
5757 support::ulittle32_t RVA;
5858 };
5959 static_assert(sizeof(LocationDescriptor) == 8, "");
60
61 /// Describes a single memory range (both its VM address and where to find it in
62 /// the file) of the process from which this minidump file was generated.
63 struct MemoryDescriptor {
64 support::ulittle64_t StartOfMemoryRange;
65 LocationDescriptor Memory;
66 };
67 static_assert(sizeof(MemoryDescriptor) == 16, "");
6068
6169 /// Specifies the location and type of a single stream in the minidump file. The
6270 /// minidump stream directory is an array of entries of this type, with its size
158166 };
159167 static_assert(sizeof(Module) == 108, "");
160168
169 /// Describes a single thread in the minidump file. Part of the ThreadList
170 /// stream.
171 struct Thread {
172 support::ulittle32_t ThreadId;
173 support::ulittle32_t SuspendCount;
174 support::ulittle32_t PriorityClass;
175 support::ulittle32_t Priority;
176 support::ulittle64_t EnvironmentBlock;
177 MemoryDescriptor Stack;
178 LocationDescriptor Context;
179 };
180 static_assert(sizeof(Thread) == 48, "");
181
161182 } // namespace minidump
162183
163184 template <> struct DenseMapInfo {
6666 /// not large enough to contain the number of modules declared in the stream
6767 /// header. The consistency of the Module entries themselves is not checked in
6868 /// any way.
69 Expected> getModuleList() const;
69 Expected> getModuleList() const {
70 return getListStream(minidump::StreamType::ModuleList);
71 }
72
73 /// Returns the thread list embedded in the ThreadList stream. An error is
74 /// returned if the file does not contain this stream, or if the stream is
75 /// not large enough to contain the number of threads declared in the stream
76 /// header. The consistency of the Thread entries themselves is not checked in
77 /// any way.
78 Expected> getThreadList() const {
79 return getListStream(minidump::StreamType::ThreadList);
80 }
7081
7182 private:
7283 static Error createError(StringRef Str) {
104115 template
105116 Expected getStream(minidump::StreamType Stream) const;
106117
118 /// Return the contents of a stream which contains a list of fixed-size items,
119 /// prefixed by the list size.
120 template
121 Expected> getListStream(minidump::StreamType Stream) const;
122
107123 const minidump::Header &Header;
108124 ArrayRef Streams;
109125 DenseMap StreamMap;
5252 return Result;
5353 }
5454
55 Expected> MinidumpFile::getModuleList() const {
56 auto OptionalStream = getRawStream(StreamType::ModuleList);
55 template
56 Expected> MinidumpFile::getListStream(StreamType Stream) const {
57 auto OptionalStream = getRawStream(Stream);
5758 if (!OptionalStream)
5859 return createError("No such stream");
5960 auto ExpectedSize =
6465 size_t ListSize = ExpectedSize.get()[0];
6566
6667 size_t ListOffset = 4;
67 // Some producers insert additional padding bytes to align the module list to
68 // 8-byte boundary. Check for that by comparing the module list size with the
69 // overall stream size.
70 if (ListOffset + sizeof(Module) * ListSize < OptionalStream->size())
68 // Some producers insert additional padding bytes to align the list to an
69 // 8-byte boundary. Check for that by comparing the list size with the overall
70 // stream size.
71 if (ListOffset + sizeof(T) * ListSize < OptionalStream->size())
7172 ListOffset = 8;
7273
73 return getDataSliceAs<Module>(*OptionalStream, ListOffset, ListSize);
74 return getDataSliceAs<T>(*OptionalStream, ListOffset, ListSize);
7475 }
76 template Expected>
77 MinidumpFile::getListStream(StreamType) const;
78 template Expected>
79 MinidumpFile::getListStream(StreamType) const;
7580
7681 Expected>
7782 MinidumpFile::getDataSlice(ArrayRef Data, size_t Offset, size_t Size) {
342342 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1
343343 };
344344
345 for (const std::vector &Data : {OneModule, PaddedModule}) {
345 for (ArrayRef Data : {OneModule, PaddedModule}) {
346346 auto ExpectedFile = create(Data);
347347 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
348348 const MinidumpFile &File = **ExpectedFile;
395395 const MinidumpFile &File = **ExpectedFile;
396396 EXPECT_THAT_EXPECTED(File.getModuleList(), Failed());
397397 }
398
399 TEST(MinidumpFile, getThreadList) {
400 std::vector OneThread{
401 // Header
402 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
403 1, 0, 0, 0, // NumberOfStreams,
404 32, 0, 0, 0, // StreamDirectoryRVA
405 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
406 0, 0, 0, 0, 0, 0, 0, 0, // Flags
407 // Stream Directory
408 3, 0, 0, 0, 52, 0, 0, 0, // Type, DataSize,
409 44, 0, 0, 0, // RVA
410 // ThreadList
411 1, 0, 0, 0, // NumberOfThreads
412 1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount
413 9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority
414 7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock
415 // Stack
416 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange
417 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA
418 // Context
419 1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA
420 };
421 // Same as before, but with a padded thread list.
422 std::vector PaddedThread{
423 // Header
424 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
425 1, 0, 0, 0, // NumberOfStreams,
426 32, 0, 0, 0, // StreamDirectoryRVA
427 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
428 0, 0, 0, 0, 0, 0, 0, 0, // Flags
429 // Stream Directory
430 3, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize,
431 44, 0, 0, 0, // RVA
432 // ThreadList
433 1, 0, 0, 0, // NumberOfThreads
434 0, 0, 0, 0, // Padding
435 1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount
436 9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority
437 7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock
438 // Stack
439 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange
440 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA
441 // Context
442 1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA
443 };
444
445 for (ArrayRef Data : {OneThread, PaddedThread}) {
446 auto ExpectedFile = create(Data);
447 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
448 const MinidumpFile &File = **ExpectedFile;
449 Expected> ExpectedThread = File.getThreadList();
450 ASSERT_THAT_EXPECTED(ExpectedThread, Succeeded());
451 ASSERT_EQ(1u, ExpectedThread->size());
452 const Thread &T = ExpectedThread.get()[0];
453 EXPECT_EQ(0x04030201u, T.ThreadId);
454 EXPECT_EQ(0x08070605u, T.SuspendCount);
455 EXPECT_EQ(0x02010009u, T.PriorityClass);
456 EXPECT_EQ(0x06050403u, T.Priority);
457 EXPECT_EQ(0x0403020100090807u, T.EnvironmentBlock);
458 EXPECT_EQ(0x0201000908070605u, T.Stack.StartOfMemoryRange);
459 EXPECT_EQ(0x06050403u, T.Stack.Memory.DataSize);
460 EXPECT_EQ(0x00090807u, T.Stack.Memory.RVA);
461 EXPECT_EQ(0x04030201u, T.Context.DataSize);
462 EXPECT_EQ(0x08070605u, T.Context.RVA);
463 }
464 }