aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h
blob: 46b20f90138fe5f896b5559a83c697d12b8d7686 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//===-- MinidumpFileBuilder.h ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file
/// Structure holding data neccessary for minidump file creation.
///
/// The class MinidumpFileWriter is used to hold the data that will eventually
/// be dumped to the file.
//===----------------------------------------------------------------------===//

#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H
#define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H

#include <cstddef>
#include <cstdint>
#include <map>
#include <unordered_map>
#include <utility>
#include <variant>

#include "lldb/Core/Progress.h"
#include "lldb/Symbol/SaveCoreOptions.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/Status.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-types.h"

#include "llvm/BinaryFormat/Minidump.h"
#include "llvm/Object/Minidump.h"

// Write std::string to minidump in the UTF16 format(with null termination char)
// with the size(without null termination char) preceding the UTF16 string.
// Empty strings are also printed with zero length and just null termination
// char.
lldb_private::Status WriteString(const std::string &to_write,
                                 lldb_private::DataBufferHeap *buffer);

/// \class MinidumpFileBuilder
/// Minidump writer for Linux
///
/// This class provides a Minidump writer that is able to
/// snapshot the current process state.
///
/// Minidumps are a Microsoft format for dumping process state.
/// This class constructs the minidump on disk starting with
/// Headers and Directories are written at the top of the file,
/// with the amount of bytes being precalculates before any writing takes place
/// Then the smaller data sections are written
/// SystemInfo, ModuleList, Misc Info.
/// Then Threads are emitted, threads are the first section that needs to be
/// 'fixed up' this happens when later we emit the memory stream, we identify if
/// that stream is the expected stack, and if so we update the stack with the
/// current RVA. Lastly the Memory lists are added. For Memory List, this will
/// contain everything that can fit within 4.2gb. MemoryList has it's
/// descriptors written at the end so it cannot be allowed to overflow.
///
/// Memory64List is a special case where it has to be begin before 4.2gb but can
/// expand forever The difference in Memory64List is there are no RVA's and all
/// the addresses are figured out by starting at the base RVA, and adding the
/// antecedent memory sections.
///
/// Because Memory64List can be arbitrarily large, this class has to write
/// chunks to disk this means we have to precalculate the descriptors and write
/// them first, and if we encounter any error, or are unable to read the same
/// number of bytes we have to go back and update them on disk.
///
/// And as the last step, after all the directories have been added, we go back
/// to the top of the file to fill in the header and the redirectory sections
/// that we preallocated.
class MinidumpFileBuilder {
public:
  MinidumpFileBuilder(lldb::FileUP &&core_file,
                      const lldb::ProcessSP &process_sp,
                      lldb_private::SaveCoreOptions &save_core_options)
      : m_process_sp(process_sp), m_core_file(std::move(core_file)),
        m_save_core_options(save_core_options) {}

  MinidumpFileBuilder(const MinidumpFileBuilder &) = delete;
  MinidumpFileBuilder &operator=(const MinidumpFileBuilder &) = delete;

  MinidumpFileBuilder(MinidumpFileBuilder &&other) = default;
  MinidumpFileBuilder &operator=(MinidumpFileBuilder &&other) = default;

  ~MinidumpFileBuilder() = default;

  // This method only calculates the amount of bytes the header and directories
  // will take up. It does not write the directories or headers. This function
  // must be called with a followup to fill in the data.
  lldb_private::Status AddHeaderAndCalculateDirectories();
  // Add SystemInfo stream, used for storing the most basic information
  // about the system, platform etc...
  lldb_private::Status AddSystemInfo();
  // Add ModuleList stream, containing information about all loaded modules
  // at the time of saving minidump.
  lldb_private::Status AddModuleList();
  // Add ThreadList stream, containing information about all threads running
  // at the moment of core saving. Contains information about thread
  // contexts.
  lldb_private::Status AddThreadList();
  // Add Exception streams for any threads that stopped with exceptions.
  lldb_private::Status AddExceptions();
  // Add MemoryList stream, containing dumps of important memory segments
  lldb_private::Status AddMemoryList();
  // Add MiscInfo stream, mainly providing ProcessId
  lldb_private::Status AddMiscInfo();
  // Add informative files about a Linux process
  lldb_private::Status AddLinuxFileStreams();

  // Run cleanup and write all remaining bytes to file
  lldb_private::Status DumpFile();

  // Delete the file if it exists
  void DeleteFile() noexcept;

private:
  lldb_private::Status AddLLDBGeneratedStream();
  // Add data to the end of the buffer, if the buffer exceeds the flush level,
  // trigger a flush.
  lldb_private::Status AddData(const void *data, uint64_t size);
  // Add MemoryList stream, containing dumps of important memory segments
  lldb_private::Status
  AddMemoryList_64(std::vector<lldb_private::CoreFileMemoryRange> &ranges,
                   lldb_private::Progress &progress);
  lldb_private::Status
  AddMemoryList_32(std::vector<lldb_private::CoreFileMemoryRange> &ranges,
                   lldb_private::Progress &progress);
  // Update the thread list on disk with the newly emitted stack RVAs.
  lldb_private::Status FixThreadStacks();
  lldb_private::Status FlushBufferToDisk();

  lldb_private::Status DumpHeader() const;
  lldb_private::Status DumpDirectories() const;
  // Add directory of StreamType pointing to the current end of the prepared
  // file with the specified size.
  lldb_private::Status AddDirectory(llvm::minidump::StreamType type,
                                    uint64_t stream_size);
  lldb::offset_t GetCurrentDataEndOffset() const;

  // Read a memory region from the process and write it to the file
  // in fixed size chunks.
  lldb_private::Status
  ReadWriteMemoryInChunks(lldb_private::DataBufferHeap &data_buffer,
                          const lldb_private::CoreFileMemoryRange &range,
                          uint64_t &bytes_read);

  // Stores directories to fill in later
  std::vector<llvm::minidump::Directory> m_directories;
  // When we write off the threads for the first time, we need to clean them up
  // and give them the correct RVA once we write the stack memory list.
  // We save by the end because we only take from the stack pointer up
  // So the saved off range base can differ from the memory region the stack
  // pointer is in.
  std::unordered_map<lldb::addr_t, llvm::minidump::Thread>
      m_thread_by_range_end;
  // Main data buffer consisting of data without the minidump header and
  // directories
  lldb_private::DataBufferHeap m_data;
  lldb::ProcessSP m_process_sp;

  size_t m_expected_directories = 0;
  uint64_t m_saved_data_size = 0;
  lldb::offset_t m_thread_list_start = 0;
  // We set the max write amount to 128 mb, this is arbitrary
  // but we want to try to keep the size of m_data small
  // and we will only exceed a 128 mb buffer if we get a memory region
  // that is larger than 128 mb.
  static constexpr uint64_t MAX_WRITE_CHUNK_SIZE = (1024 * 1024 * 128);

  static constexpr size_t HEADER_SIZE = sizeof(llvm::minidump::Header);
  static constexpr size_t DIRECTORY_SIZE = sizeof(llvm::minidump::Directory);

  // More that one place can mention the register thread context locations,
  // so when we emit the thread contents, remember where it is so we don't have
  // to duplicate it in the exception data.
  std::unordered_map<lldb::tid_t, llvm::minidump::LocationDescriptor>
      m_tid_to_reg_ctx;
  lldb::FileUP m_core_file;
  lldb_private::SaveCoreOptions m_save_core_options;
};
#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H