xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1349cc55cSDimitry Andric //===-- MinidumpFileBuilder.h ---------------------------------------------===//
2349cc55cSDimitry Andric //
3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6349cc55cSDimitry Andric //
7349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
8349cc55cSDimitry Andric //
9349cc55cSDimitry Andric /// \file
10349cc55cSDimitry Andric /// Structure holding data neccessary for minidump file creation.
11349cc55cSDimitry Andric ///
12349cc55cSDimitry Andric /// The class MinidumpFileWriter is used to hold the data that will eventually
13349cc55cSDimitry Andric /// be dumped to the file.
14349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
15349cc55cSDimitry Andric 
16349cc55cSDimitry Andric #ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H
17349cc55cSDimitry Andric #define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H
18349cc55cSDimitry Andric 
19349cc55cSDimitry Andric #include <cstddef>
20*0fca6ea1SDimitry Andric #include <cstdint>
215f757f3fSDimitry Andric #include <map>
22*0fca6ea1SDimitry Andric #include <unordered_map>
23*0fca6ea1SDimitry Andric #include <utility>
24*0fca6ea1SDimitry Andric #include <variant>
25349cc55cSDimitry Andric 
26*0fca6ea1SDimitry Andric #include "lldb/Target/Process.h"
27349cc55cSDimitry Andric #include "lldb/Target/Target.h"
28349cc55cSDimitry Andric #include "lldb/Utility/DataBufferHeap.h"
29349cc55cSDimitry Andric #include "lldb/Utility/Status.h"
30*0fca6ea1SDimitry Andric #include "lldb/lldb-forward.h"
31*0fca6ea1SDimitry Andric #include "lldb/lldb-types.h"
32349cc55cSDimitry Andric 
33*0fca6ea1SDimitry Andric #include "llvm/BinaryFormat/Minidump.h"
34349cc55cSDimitry Andric #include "llvm/Object/Minidump.h"
35349cc55cSDimitry Andric 
36349cc55cSDimitry Andric // Write std::string to minidump in the UTF16 format(with null termination char)
37349cc55cSDimitry Andric // with the size(without null termination char) preceding the UTF16 string.
38349cc55cSDimitry Andric // Empty strings are also printed with zero length and just null termination
39349cc55cSDimitry Andric // char.
40349cc55cSDimitry Andric lldb_private::Status WriteString(const std::string &to_write,
41349cc55cSDimitry Andric                                  lldb_private::DataBufferHeap *buffer);
42349cc55cSDimitry Andric 
43349cc55cSDimitry Andric /// \class MinidumpFileBuilder
44349cc55cSDimitry Andric /// Minidump writer for Linux
45349cc55cSDimitry Andric ///
46349cc55cSDimitry Andric /// This class provides a Minidump writer that is able to
47*0fca6ea1SDimitry Andric /// snapshot the current process state.
48*0fca6ea1SDimitry Andric ///
49*0fca6ea1SDimitry Andric /// Minidumps are a Microsoft format for dumping process state.
50*0fca6ea1SDimitry Andric /// This class constructs the minidump on disk starting with
51*0fca6ea1SDimitry Andric /// Headers and Directories are written at the top of the file,
52*0fca6ea1SDimitry Andric /// with the amount of bytes being precalculates before any writing takes place
53*0fca6ea1SDimitry Andric /// Then the smaller data sections are written
54*0fca6ea1SDimitry Andric /// SystemInfo, ModuleList, Misc Info.
55*0fca6ea1SDimitry Andric /// Then Threads are emitted, threads are the first section that needs to be
56*0fca6ea1SDimitry Andric /// 'fixed up' this happens when later we emit the memory stream, we identify if
57*0fca6ea1SDimitry Andric /// that stream is the expected stack, and if so we update the stack with the
58*0fca6ea1SDimitry Andric /// current RVA. Lastly the Memory lists are added. For Memory List, this will
59*0fca6ea1SDimitry Andric /// contain everything that can fit within 4.2gb. MemoryList has it's
60*0fca6ea1SDimitry Andric /// descriptors written at the end so it cannot be allowed to overflow.
61*0fca6ea1SDimitry Andric ///
62*0fca6ea1SDimitry Andric /// Memory64List is a special case where it has to be begin before 4.2gb but can
63*0fca6ea1SDimitry Andric /// expand forever The difference in Memory64List is there are no RVA's and all
64*0fca6ea1SDimitry Andric /// the addresses are figured out by starting at the base RVA, and adding the
65*0fca6ea1SDimitry Andric /// antecedent memory sections.
66*0fca6ea1SDimitry Andric ///
67*0fca6ea1SDimitry Andric /// Because Memory64List can be arbitrarily large, this class has to write
68*0fca6ea1SDimitry Andric /// chunks to disk this means we have to precalculate the descriptors and write
69*0fca6ea1SDimitry Andric /// them first, and if we encounter any error, or are unable to read the same
70*0fca6ea1SDimitry Andric /// number of bytes we have to go back and update them on disk.
71*0fca6ea1SDimitry Andric ///
72*0fca6ea1SDimitry Andric /// And as the last step, after all the directories have been added, we go back
73*0fca6ea1SDimitry Andric /// to the top of the file to fill in the header and the redirectory sections
74*0fca6ea1SDimitry Andric /// that we preallocated.
75349cc55cSDimitry Andric class MinidumpFileBuilder {
76349cc55cSDimitry Andric public:
77*0fca6ea1SDimitry Andric   MinidumpFileBuilder(lldb::FileUP &&core_file,
78*0fca6ea1SDimitry Andric                       const lldb::ProcessSP &process_sp)
79*0fca6ea1SDimitry Andric       : m_process_sp(process_sp), m_core_file(std::move(core_file)){};
80349cc55cSDimitry Andric 
81349cc55cSDimitry Andric   MinidumpFileBuilder(const MinidumpFileBuilder &) = delete;
82349cc55cSDimitry Andric   MinidumpFileBuilder &operator=(const MinidumpFileBuilder &) = delete;
83349cc55cSDimitry Andric 
84349cc55cSDimitry Andric   MinidumpFileBuilder(MinidumpFileBuilder &&other) = default;
85349cc55cSDimitry Andric   MinidumpFileBuilder &operator=(MinidumpFileBuilder &&other) = default;
86349cc55cSDimitry Andric 
87349cc55cSDimitry Andric   ~MinidumpFileBuilder() = default;
88349cc55cSDimitry Andric 
89*0fca6ea1SDimitry Andric   // This method only calculates the amount of bytes the header and directories
90*0fca6ea1SDimitry Andric   // will take up. It does not write the directories or headers. This function
91*0fca6ea1SDimitry Andric   // must be called with a followup to fill in the data.
92*0fca6ea1SDimitry Andric   lldb_private::Status AddHeaderAndCalculateDirectories();
93349cc55cSDimitry Andric   // Add SystemInfo stream, used for storing the most basic information
94349cc55cSDimitry Andric   // about the system, platform etc...
95*0fca6ea1SDimitry Andric   lldb_private::Status AddSystemInfo();
96349cc55cSDimitry Andric   // Add ModuleList stream, containing information about all loaded modules
97349cc55cSDimitry Andric   // at the time of saving minidump.
98*0fca6ea1SDimitry Andric   lldb_private::Status AddModuleList();
99349cc55cSDimitry Andric   // Add ThreadList stream, containing information about all threads running
100349cc55cSDimitry Andric   // at the moment of core saving. Contains information about thread
101349cc55cSDimitry Andric   // contexts.
102*0fca6ea1SDimitry Andric   lldb_private::Status AddThreadList();
1035f757f3fSDimitry Andric   // Add Exception streams for any threads that stopped with exceptions.
104*0fca6ea1SDimitry Andric   lldb_private::Status AddExceptions();
105349cc55cSDimitry Andric   // Add MemoryList stream, containing dumps of important memory segments
106*0fca6ea1SDimitry Andric   lldb_private::Status AddMemoryList(lldb::SaveCoreStyle core_style);
107349cc55cSDimitry Andric   // Add MiscInfo stream, mainly providing ProcessId
108*0fca6ea1SDimitry Andric   lldb_private::Status AddMiscInfo();
109349cc55cSDimitry Andric   // Add informative files about a Linux process
110*0fca6ea1SDimitry Andric   lldb_private::Status AddLinuxFileStreams();
111*0fca6ea1SDimitry Andric 
112*0fca6ea1SDimitry Andric   // Run cleanup and write all remaining bytes to file
113*0fca6ea1SDimitry Andric   lldb_private::Status DumpFile();
114349cc55cSDimitry Andric 
115349cc55cSDimitry Andric private:
116*0fca6ea1SDimitry Andric   // Add data to the end of the buffer, if the buffer exceeds the flush level,
117*0fca6ea1SDimitry Andric   // trigger a flush.
118*0fca6ea1SDimitry Andric   lldb_private::Status AddData(const void *data, uint64_t size);
119*0fca6ea1SDimitry Andric   // Add MemoryList stream, containing dumps of important memory segments
120*0fca6ea1SDimitry Andric   lldb_private::Status
121*0fca6ea1SDimitry Andric   AddMemoryList_64(lldb_private::Process::CoreFileMemoryRanges &ranges);
122*0fca6ea1SDimitry Andric   lldb_private::Status
123*0fca6ea1SDimitry Andric   AddMemoryList_32(lldb_private::Process::CoreFileMemoryRanges &ranges);
124*0fca6ea1SDimitry Andric   // Update the thread list on disk with the newly emitted stack RVAs.
125*0fca6ea1SDimitry Andric   lldb_private::Status FixThreadStacks();
126*0fca6ea1SDimitry Andric   lldb_private::Status FlushBufferToDisk();
127*0fca6ea1SDimitry Andric 
128*0fca6ea1SDimitry Andric   lldb_private::Status DumpHeader() const;
129*0fca6ea1SDimitry Andric   lldb_private::Status DumpDirectories() const;
130349cc55cSDimitry Andric   // Add directory of StreamType pointing to the current end of the prepared
131349cc55cSDimitry Andric   // file with the specified size.
132*0fca6ea1SDimitry Andric   lldb_private::Status AddDirectory(llvm::minidump::StreamType type,
133*0fca6ea1SDimitry Andric                                     uint64_t stream_size);
134*0fca6ea1SDimitry Andric   lldb::offset_t GetCurrentDataEndOffset() const;
135*0fca6ea1SDimitry Andric   // Stores directories to fill in later
136349cc55cSDimitry Andric   std::vector<llvm::minidump::Directory> m_directories;
137*0fca6ea1SDimitry Andric   // When we write off the threads for the first time, we need to clean them up
138*0fca6ea1SDimitry Andric   // and give them the correct RVA once we write the stack memory list.
139*0fca6ea1SDimitry Andric   // We save by the end because we only take from the stack pointer up
140*0fca6ea1SDimitry Andric   // So the saved off range base can differ from the memory region the stack
141*0fca6ea1SDimitry Andric   // pointer is in.
142*0fca6ea1SDimitry Andric   std::unordered_map<lldb::addr_t, llvm::minidump::Thread>
143*0fca6ea1SDimitry Andric       m_thread_by_range_end;
144349cc55cSDimitry Andric   // Main data buffer consisting of data without the minidump header and
145349cc55cSDimitry Andric   // directories
146349cc55cSDimitry Andric   lldb_private::DataBufferHeap m_data;
147*0fca6ea1SDimitry Andric   lldb::ProcessSP m_process_sp;
148*0fca6ea1SDimitry Andric 
149*0fca6ea1SDimitry Andric   size_t m_expected_directories = 0;
150*0fca6ea1SDimitry Andric   uint64_t m_saved_data_size = 0;
151*0fca6ea1SDimitry Andric   lldb::offset_t m_thread_list_start = 0;
152*0fca6ea1SDimitry Andric   // We set the max write amount to 128 mb, this is arbitrary
153*0fca6ea1SDimitry Andric   // but we want to try to keep the size of m_data small
154*0fca6ea1SDimitry Andric   // and we will only exceed a 128 mb buffer if we get a memory region
155*0fca6ea1SDimitry Andric   // that is larger than 128 mb.
156*0fca6ea1SDimitry Andric   static constexpr size_t MAX_WRITE_CHUNK_SIZE = (1024 * 1024 * 128);
157*0fca6ea1SDimitry Andric 
158*0fca6ea1SDimitry Andric   static constexpr size_t HEADER_SIZE = sizeof(llvm::minidump::Header);
159*0fca6ea1SDimitry Andric   static constexpr size_t DIRECTORY_SIZE = sizeof(llvm::minidump::Directory);
1605f757f3fSDimitry Andric 
1615f757f3fSDimitry Andric   // More that one place can mention the register thread context locations,
1625f757f3fSDimitry Andric   // so when we emit the thread contents, remember where it is so we don't have
1635f757f3fSDimitry Andric   // to duplicate it in the exception data.
164*0fca6ea1SDimitry Andric   std::unordered_map<lldb::tid_t, llvm::minidump::LocationDescriptor>
165*0fca6ea1SDimitry Andric       m_tid_to_reg_ctx;
166*0fca6ea1SDimitry Andric   lldb::FileUP m_core_file;
167349cc55cSDimitry Andric };
168349cc55cSDimitry Andric 
169349cc55cSDimitry Andric #endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H
170