xref: /llvm-project/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp (revision accd4a4ad5ec7a8682dc701fd7072610d40cc436)
1eee687a6SAndrej Korman //===-- MinidumpFileBuilder.cpp -------------------------------------------===//
2eee687a6SAndrej Korman //
3eee687a6SAndrej Korman // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4eee687a6SAndrej Korman // See https://llvm.org/LICENSE.txt for license information.
5eee687a6SAndrej Korman // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6eee687a6SAndrej Korman //
7eee687a6SAndrej Korman //===----------------------------------------------------------------------===//
8eee687a6SAndrej Korman 
9eee687a6SAndrej Korman #include "MinidumpFileBuilder.h"
10eee687a6SAndrej Korman 
11beb702c0SGreg Clayton #include "Plugins/Process/minidump/RegisterContextMinidump_ARM64.h"
12eee687a6SAndrej Korman #include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h"
13eee687a6SAndrej Korman 
14eee687a6SAndrej Korman #include "lldb/Core/Module.h"
15eee687a6SAndrej Korman #include "lldb/Core/ModuleList.h"
16eee687a6SAndrej Korman #include "lldb/Core/Section.h"
1747d80ec1SJacob Lalonde #include "lldb/Target/ABI.h"
18eee687a6SAndrej Korman #include "lldb/Target/MemoryRegionInfo.h"
19eee687a6SAndrej Korman #include "lldb/Target/Process.h"
20eee687a6SAndrej Korman #include "lldb/Target/RegisterContext.h"
21eee687a6SAndrej Korman #include "lldb/Target/StopInfo.h"
22eee687a6SAndrej Korman #include "lldb/Target/ThreadList.h"
23a27164cbSJacob Lalonde #include "lldb/Utility/DataBufferHeap.h"
24eee687a6SAndrej Korman #include "lldb/Utility/DataExtractor.h"
256870ac20Sjeffreytan81 #include "lldb/Utility/LLDBLog.h"
2692631a48SMiro Bucko #include "lldb/Utility/Log.h"
27a27164cbSJacob Lalonde #include "lldb/Utility/RangeMap.h"
28eee687a6SAndrej Korman #include "lldb/Utility/RegisterValue.h"
29eee687a6SAndrej Korman 
30eee687a6SAndrej Korman #include "llvm/ADT/StringRef.h"
31eee687a6SAndrej Korman #include "llvm/BinaryFormat/Minidump.h"
32eee687a6SAndrej Korman #include "llvm/Support/ConvertUTF.h"
33a27164cbSJacob Lalonde #include "llvm/Support/Endian.h"
34eee687a6SAndrej Korman #include "llvm/Support/Error.h"
35a27164cbSJacob Lalonde #include "llvm/TargetParser/Triple.h"
36eee687a6SAndrej Korman 
37eee687a6SAndrej Korman #include "Plugins/Process/minidump/MinidumpTypes.h"
38a27164cbSJacob Lalonde #include "lldb/lldb-enumerations.h"
39a27164cbSJacob Lalonde #include "lldb/lldb-forward.h"
40a27164cbSJacob Lalonde #include "lldb/lldb-types.h"
41eee687a6SAndrej Korman 
42a27164cbSJacob Lalonde #include <algorithm>
43e69d3598SFangrui Song #include <cinttypes>
44a27164cbSJacob Lalonde #include <climits>
45a27164cbSJacob Lalonde #include <cstddef>
46a27164cbSJacob Lalonde #include <cstdint>
47a27164cbSJacob Lalonde #include <functional>
48a27164cbSJacob Lalonde #include <iostream>
49a27164cbSJacob Lalonde #include <set>
50a27164cbSJacob Lalonde #include <utility>
51a27164cbSJacob Lalonde #include <vector>
52e69d3598SFangrui Song 
53eee687a6SAndrej Korman using namespace lldb;
54eee687a6SAndrej Korman using namespace lldb_private;
55eee687a6SAndrej Korman using namespace llvm::minidump;
56eee687a6SAndrej Korman 
57a27164cbSJacob Lalonde Status MinidumpFileBuilder::AddHeaderAndCalculateDirectories() {
58a27164cbSJacob Lalonde   // First set the offset on the file, and on the bytes saved
59a27164cbSJacob Lalonde   m_saved_data_size = HEADER_SIZE;
60a27164cbSJacob Lalonde   // We know we will have at least Misc, SystemInfo, Modules, and ThreadList
61*accd4a4aSJacob Lalonde   // (corresponding memory list for stacks), an additional memory list for
62*accd4a4aSJacob Lalonde   // non-stacks, and a stream to mark this minidump was generated by LLDB.
63a27164cbSJacob Lalonde   lldb_private::Target &target = m_process_sp->GetTarget();
64a27164cbSJacob Lalonde   m_expected_directories = 6;
65a27164cbSJacob Lalonde   // Check if OS is linux and reserve directory space for all linux specific
66a27164cbSJacob Lalonde   // breakpad extension directories.
67a27164cbSJacob Lalonde   if (target.GetArchitecture().GetTriple().getOS() ==
68a27164cbSJacob Lalonde       llvm::Triple::OSType::Linux)
69a27164cbSJacob Lalonde     m_expected_directories += 9;
70a27164cbSJacob Lalonde 
71a27164cbSJacob Lalonde   // Go through all of the threads and check for exceptions.
72572943e7SJacob Lalonde   std::vector<lldb::ThreadSP> threads =
73572943e7SJacob Lalonde       m_process_sp->CalculateCoreFileThreadList(m_save_core_options);
74572943e7SJacob Lalonde   for (const ThreadSP &thread_sp : threads) {
75a27164cbSJacob Lalonde     StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
76a27164cbSJacob Lalonde     if (stop_info_sp) {
77a27164cbSJacob Lalonde       const StopReason &stop_reason = stop_info_sp->GetStopReason();
785033ea73SJacob Lalonde       if (stop_reason != lldb::eStopReasonInvalid)
79a27164cbSJacob Lalonde         m_expected_directories++;
80a27164cbSJacob Lalonde     }
81a27164cbSJacob Lalonde   }
82a27164cbSJacob Lalonde 
83a27164cbSJacob Lalonde   m_saved_data_size +=
84a27164cbSJacob Lalonde       m_expected_directories * sizeof(llvm::minidump::Directory);
85a27164cbSJacob Lalonde   Status error;
86a27164cbSJacob Lalonde   offset_t new_offset = m_core_file->SeekFromStart(m_saved_data_size);
87a27164cbSJacob Lalonde   if (new_offset != m_saved_data_size)
880642cd76SAdrian Prantl     error = Status::FromErrorStringWithFormat(
890642cd76SAdrian Prantl         "Failed to fill in header and directory "
900642cd76SAdrian Prantl         "sections. Written / Expected (%" PRIx64 " / %" PRIx64 ")",
91a27164cbSJacob Lalonde         new_offset, m_saved_data_size);
92a27164cbSJacob Lalonde 
93*accd4a4aSJacob Lalonde   if (error.Fail())
94a27164cbSJacob Lalonde     return error;
95*accd4a4aSJacob Lalonde 
96*accd4a4aSJacob Lalonde   return AddLLDBGeneratedStream();
97a27164cbSJacob Lalonde }
98a27164cbSJacob Lalonde 
99a27164cbSJacob Lalonde Status MinidumpFileBuilder::AddDirectory(StreamType type,
100a27164cbSJacob Lalonde                                          uint64_t stream_size) {
101a27164cbSJacob Lalonde   // We explicitly cast type, an 32b enum, to uint32_t to avoid warnings.
102a27164cbSJacob Lalonde   Status error;
103a27164cbSJacob Lalonde   if (GetCurrentDataEndOffset() > UINT32_MAX) {
1040642cd76SAdrian Prantl     error = Status::FromErrorStringWithFormat(
1050642cd76SAdrian Prantl         "Unable to add directory for stream type "
106a27164cbSJacob Lalonde         "%x, offset is greater then 32 bit limit.",
107a27164cbSJacob Lalonde         (uint32_t)type);
108a27164cbSJacob Lalonde     return error;
109a27164cbSJacob Lalonde   }
110a27164cbSJacob Lalonde 
111a27164cbSJacob Lalonde   if (m_directories.size() + 1 > m_expected_directories) {
1120642cd76SAdrian Prantl     error = Status::FromErrorStringWithFormat(
113a27164cbSJacob Lalonde         "Unable to add directory for stream type %x, exceeded expected number "
114361543e4SJacob Lalonde         "of directories %zu.",
115a27164cbSJacob Lalonde         (uint32_t)type, m_expected_directories);
116a27164cbSJacob Lalonde     return error;
117a27164cbSJacob Lalonde   }
118a27164cbSJacob Lalonde 
119eee687a6SAndrej Korman   LocationDescriptor loc;
120eee687a6SAndrej Korman   loc.DataSize = static_cast<llvm::support::ulittle32_t>(stream_size);
121eee687a6SAndrej Korman   // Stream will begin at the current end of data section
122eee687a6SAndrej Korman   loc.RVA = static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset());
123eee687a6SAndrej Korman 
124eee687a6SAndrej Korman   Directory dir;
125eee687a6SAndrej Korman   dir.Type = static_cast<llvm::support::little_t<StreamType>>(type);
126eee687a6SAndrej Korman   dir.Location = loc;
127eee687a6SAndrej Korman 
128eee687a6SAndrej Korman   m_directories.push_back(dir);
129a27164cbSJacob Lalonde   return error;
130eee687a6SAndrej Korman }
131eee687a6SAndrej Korman 
132*accd4a4aSJacob Lalonde Status MinidumpFileBuilder::AddLLDBGeneratedStream() {
133*accd4a4aSJacob Lalonde   Status error;
134*accd4a4aSJacob Lalonde   StreamType type = StreamType::LLDBGenerated;
135*accd4a4aSJacob Lalonde   return AddDirectory(type, 0);
136*accd4a4aSJacob Lalonde }
137*accd4a4aSJacob Lalonde 
138a27164cbSJacob Lalonde Status MinidumpFileBuilder::AddSystemInfo() {
139eee687a6SAndrej Korman   Status error;
140a27164cbSJacob Lalonde   const llvm::Triple &target_triple =
141a27164cbSJacob Lalonde       m_process_sp->GetTarget().GetArchitecture().GetTriple();
142a27164cbSJacob Lalonde   error =
143eee687a6SAndrej Korman       AddDirectory(StreamType::SystemInfo, sizeof(llvm::minidump::SystemInfo));
144a27164cbSJacob Lalonde   if (error.Fail())
145a27164cbSJacob Lalonde     return error;
146eee687a6SAndrej Korman 
147eee687a6SAndrej Korman   llvm::minidump::ProcessorArchitecture arch;
148eee687a6SAndrej Korman   switch (target_triple.getArch()) {
149eee687a6SAndrej Korman   case llvm::Triple::ArchType::x86_64:
150eee687a6SAndrej Korman     arch = ProcessorArchitecture::AMD64;
151eee687a6SAndrej Korman     break;
152eee687a6SAndrej Korman   case llvm::Triple::ArchType::x86:
153eee687a6SAndrej Korman     arch = ProcessorArchitecture::X86;
154eee687a6SAndrej Korman     break;
155eee687a6SAndrej Korman   case llvm::Triple::ArchType::arm:
156eee687a6SAndrej Korman     arch = ProcessorArchitecture::ARM;
157eee687a6SAndrej Korman     break;
158eee687a6SAndrej Korman   case llvm::Triple::ArchType::aarch64:
159eee687a6SAndrej Korman     arch = ProcessorArchitecture::ARM64;
160eee687a6SAndrej Korman     break;
161eee687a6SAndrej Korman   case llvm::Triple::ArchType::mips64:
162eee687a6SAndrej Korman   case llvm::Triple::ArchType::mips64el:
163eee687a6SAndrej Korman   case llvm::Triple::ArchType::mips:
164eee687a6SAndrej Korman   case llvm::Triple::ArchType::mipsel:
165eee687a6SAndrej Korman     arch = ProcessorArchitecture::MIPS;
166eee687a6SAndrej Korman     break;
167eee687a6SAndrej Korman   case llvm::Triple::ArchType::ppc64:
168eee687a6SAndrej Korman   case llvm::Triple::ArchType::ppc:
169eee687a6SAndrej Korman   case llvm::Triple::ArchType::ppc64le:
170eee687a6SAndrej Korman     arch = ProcessorArchitecture::PPC;
171eee687a6SAndrej Korman     break;
172eee687a6SAndrej Korman   default:
1730642cd76SAdrian Prantl     error = Status::FromErrorStringWithFormat(
1740642cd76SAdrian Prantl         "Architecture %s not supported.",
175eee687a6SAndrej Korman         target_triple.getArchName().str().c_str());
176eee687a6SAndrej Korman     return error;
177eee687a6SAndrej Korman   };
178eee687a6SAndrej Korman 
179eee687a6SAndrej Korman   llvm::support::little_t<OSPlatform> platform_id;
180eee687a6SAndrej Korman   switch (target_triple.getOS()) {
181eee687a6SAndrej Korman   case llvm::Triple::OSType::Linux:
182eee687a6SAndrej Korman     if (target_triple.getEnvironment() ==
183eee687a6SAndrej Korman         llvm::Triple::EnvironmentType::Android)
184eee687a6SAndrej Korman       platform_id = OSPlatform::Android;
185eee687a6SAndrej Korman     else
186eee687a6SAndrej Korman       platform_id = OSPlatform::Linux;
187eee687a6SAndrej Korman     break;
188eee687a6SAndrej Korman   case llvm::Triple::OSType::Win32:
189eee687a6SAndrej Korman     platform_id = OSPlatform::Win32NT;
190eee687a6SAndrej Korman     break;
191eee687a6SAndrej Korman   case llvm::Triple::OSType::MacOSX:
192eee687a6SAndrej Korman     platform_id = OSPlatform::MacOSX;
193eee687a6SAndrej Korman     break;
194eee687a6SAndrej Korman   case llvm::Triple::OSType::IOS:
195eee687a6SAndrej Korman     platform_id = OSPlatform::IOS;
196eee687a6SAndrej Korman     break;
197eee687a6SAndrej Korman   default:
1980642cd76SAdrian Prantl     error = Status::FromErrorStringWithFormat(
1990642cd76SAdrian Prantl         "OS %s not supported.", target_triple.getOSName().str().c_str());
200eee687a6SAndrej Korman     return error;
201eee687a6SAndrej Korman   };
202eee687a6SAndrej Korman 
203eee687a6SAndrej Korman   llvm::minidump::SystemInfo sys_info;
204eee687a6SAndrej Korman   sys_info.ProcessorArch =
205eee687a6SAndrej Korman       static_cast<llvm::support::little_t<ProcessorArchitecture>>(arch);
206eee687a6SAndrej Korman   // Global offset to beginning of a csd_string in a data section
207eee687a6SAndrej Korman   sys_info.CSDVersionRVA = static_cast<llvm::support::ulittle32_t>(
208eee687a6SAndrej Korman       GetCurrentDataEndOffset() + sizeof(llvm::minidump::SystemInfo));
209eee687a6SAndrej Korman   sys_info.PlatformId = platform_id;
210eee687a6SAndrej Korman   m_data.AppendData(&sys_info, sizeof(llvm::minidump::SystemInfo));
211eee687a6SAndrej Korman 
2120542d152SKazu Hirata   std::string csd_string;
213eee687a6SAndrej Korman 
214eee687a6SAndrej Korman   error = WriteString(csd_string, &m_data);
215eee687a6SAndrej Korman   if (error.Fail()) {
2160642cd76SAdrian Prantl     error =
2170642cd76SAdrian Prantl         Status::FromErrorString("Unable to convert the csd string to UTF16.");
218eee687a6SAndrej Korman     return error;
219eee687a6SAndrej Korman   }
220eee687a6SAndrej Korman 
221eee687a6SAndrej Korman   return error;
222eee687a6SAndrej Korman }
223eee687a6SAndrej Korman 
224eee687a6SAndrej Korman Status WriteString(const std::string &to_write,
225eee687a6SAndrej Korman                    lldb_private::DataBufferHeap *buffer) {
226eee687a6SAndrej Korman   Status error;
227eee687a6SAndrej Korman   // let the StringRef eat also null termination char
228eee687a6SAndrej Korman   llvm::StringRef to_write_ref(to_write.c_str(), to_write.size() + 1);
229eee687a6SAndrej Korman   llvm::SmallVector<llvm::UTF16, 128> to_write_utf16;
230eee687a6SAndrej Korman 
231eee687a6SAndrej Korman   bool converted = convertUTF8ToUTF16String(to_write_ref, to_write_utf16);
232eee687a6SAndrej Korman   if (!converted) {
2330642cd76SAdrian Prantl     error = Status::FromErrorStringWithFormat(
234eee687a6SAndrej Korman         "Unable to convert the string to UTF16. Failed to convert %s",
235eee687a6SAndrej Korman         to_write.c_str());
236eee687a6SAndrej Korman     return error;
237eee687a6SAndrej Korman   }
238eee687a6SAndrej Korman 
239eee687a6SAndrej Korman   // size of the UTF16 string should be written without the null termination
240eee687a6SAndrej Korman   // character that is stored in 2 bytes
241eee687a6SAndrej Korman   llvm::support::ulittle32_t to_write_size(to_write_utf16.size_in_bytes() - 2);
242eee687a6SAndrej Korman 
243eee687a6SAndrej Korman   buffer->AppendData(&to_write_size, sizeof(llvm::support::ulittle32_t));
244eee687a6SAndrej Korman   buffer->AppendData(to_write_utf16.data(), to_write_utf16.size_in_bytes());
245eee687a6SAndrej Korman 
246eee687a6SAndrej Korman   return error;
247eee687a6SAndrej Korman }
248eee687a6SAndrej Korman 
249eee687a6SAndrej Korman llvm::Expected<uint64_t> getModuleFileSize(Target &target,
250eee687a6SAndrej Korman                                            const ModuleSP &mod) {
2516870ac20Sjeffreytan81   // JIT module has the same vm and file size.
252eee687a6SAndrej Korman   uint64_t SizeOfImage = 0;
2536870ac20Sjeffreytan81   if (mod->GetObjectFile()->CalculateType() == ObjectFile::Type::eTypeJIT) {
2546870ac20Sjeffreytan81     for (const auto &section : *mod->GetObjectFile()->GetSectionList()) {
2556870ac20Sjeffreytan81       SizeOfImage += section->GetByteSize();
2566870ac20Sjeffreytan81     }
2576870ac20Sjeffreytan81     return SizeOfImage;
2586870ac20Sjeffreytan81   }
2596870ac20Sjeffreytan81   SectionSP sect_sp = mod->GetObjectFile()->GetBaseAddress().GetSection();
260eee687a6SAndrej Korman 
261eee687a6SAndrej Korman   if (!sect_sp) {
262eee687a6SAndrej Korman     return llvm::createStringError(std::errc::operation_not_supported,
263eee687a6SAndrej Korman                                    "Couldn't obtain the section information.");
264eee687a6SAndrej Korman   }
265eee687a6SAndrej Korman   lldb::addr_t sect_addr = sect_sp->GetLoadBaseAddress(&target);
266eee687a6SAndrej Korman   // Use memory size since zero fill sections, like ".bss", will be smaller on
267eee687a6SAndrej Korman   // disk.
268eee687a6SAndrej Korman   lldb::addr_t sect_size = sect_sp->GetByteSize();
269eee687a6SAndrej Korman   // This will usually be zero, but make sure to calculate the BaseOfImage
270eee687a6SAndrej Korman   // offset.
271eee687a6SAndrej Korman   const lldb::addr_t base_sect_offset =
272eee687a6SAndrej Korman       mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target) -
273eee687a6SAndrej Korman       sect_addr;
274eee687a6SAndrej Korman   SizeOfImage = sect_size - base_sect_offset;
275eee687a6SAndrej Korman   lldb::addr_t next_sect_addr = sect_addr + sect_size;
276eee687a6SAndrej Korman   Address sect_so_addr;
277eee687a6SAndrej Korman   target.ResolveLoadAddress(next_sect_addr, sect_so_addr);
278eee687a6SAndrej Korman   lldb::SectionSP next_sect_sp = sect_so_addr.GetSection();
279eee687a6SAndrej Korman   while (next_sect_sp &&
280eee687a6SAndrej Korman          next_sect_sp->GetLoadBaseAddress(&target) == next_sect_addr) {
281eee687a6SAndrej Korman     sect_size = sect_sp->GetByteSize();
282eee687a6SAndrej Korman     SizeOfImage += sect_size;
283eee687a6SAndrej Korman     next_sect_addr += sect_size;
284eee687a6SAndrej Korman     target.ResolveLoadAddress(next_sect_addr, sect_so_addr);
285eee687a6SAndrej Korman     next_sect_sp = sect_so_addr.GetSection();
286eee687a6SAndrej Korman   }
287eee687a6SAndrej Korman 
288eee687a6SAndrej Korman   return SizeOfImage;
289eee687a6SAndrej Korman }
290eee687a6SAndrej Korman 
291eee687a6SAndrej Korman // ModuleList stream consists of a number of modules, followed by an array
292eee687a6SAndrej Korman // of llvm::minidump::Module's structures. Every structure informs about a
293eee687a6SAndrej Korman // single module. Additional data of variable length, such as module's names,
294eee687a6SAndrej Korman // are stored just after the ModuleList stream. The llvm::minidump::Module
295eee687a6SAndrej Korman // structures point to this helper data by global offset.
296a27164cbSJacob Lalonde Status MinidumpFileBuilder::AddModuleList() {
297eee687a6SAndrej Korman   constexpr size_t minidump_module_size = sizeof(llvm::minidump::Module);
298eee687a6SAndrej Korman   Status error;
299eee687a6SAndrej Korman 
300a27164cbSJacob Lalonde   lldb_private::Target &target = m_process_sp->GetTarget();
301eee687a6SAndrej Korman   const ModuleList &modules = target.GetImages();
302eee687a6SAndrej Korman   llvm::support::ulittle32_t modules_count =
303eee687a6SAndrej Korman       static_cast<llvm::support::ulittle32_t>(modules.GetSize());
304eee687a6SAndrej Korman 
305eee687a6SAndrej Korman   // This helps us with getting the correct global offset in minidump
306eee687a6SAndrej Korman   // file later, when we will be setting up offsets from the
307eee687a6SAndrej Korman   // the llvm::minidump::Module's structures into helper data
308eee687a6SAndrej Korman   size_t size_before = GetCurrentDataEndOffset();
309eee687a6SAndrej Korman 
310eee687a6SAndrej Korman   // This is the size of the main part of the ModuleList stream.
311eee687a6SAndrej Korman   // It consists of a module number and corresponding number of
312eee687a6SAndrej Korman   // structs describing individual modules
313eee687a6SAndrej Korman   size_t module_stream_size =
314eee687a6SAndrej Korman       sizeof(llvm::support::ulittle32_t) + modules_count * minidump_module_size;
315eee687a6SAndrej Korman 
316eee687a6SAndrej Korman   // Adding directory describing this stream.
317a27164cbSJacob Lalonde   error = AddDirectory(StreamType::ModuleList, module_stream_size);
318a27164cbSJacob Lalonde   if (error.Fail())
319a27164cbSJacob Lalonde     return error;
320eee687a6SAndrej Korman 
321eee687a6SAndrej Korman   m_data.AppendData(&modules_count, sizeof(llvm::support::ulittle32_t));
322eee687a6SAndrej Korman 
323eee687a6SAndrej Korman   // Temporary storage for the helper data (of variable length)
324eee687a6SAndrej Korman   // as these cannot be dumped to m_data before dumping entire
325eee687a6SAndrej Korman   // array of module structures.
326eee687a6SAndrej Korman   DataBufferHeap helper_data;
327eee687a6SAndrej Korman 
328eee687a6SAndrej Korman   for (size_t i = 0; i < modules_count; ++i) {
329eee687a6SAndrej Korman     ModuleSP mod = modules.GetModuleAtIndex(i);
330eee687a6SAndrej Korman     std::string module_name = mod->GetSpecificationDescription();
331eee687a6SAndrej Korman     auto maybe_mod_size = getModuleFileSize(target, mod);
332eee687a6SAndrej Korman     if (!maybe_mod_size) {
3336870ac20Sjeffreytan81       llvm::Error mod_size_err = maybe_mod_size.takeError();
3346870ac20Sjeffreytan81       llvm::handleAllErrors(std::move(mod_size_err),
3356870ac20Sjeffreytan81                             [&](const llvm::ErrorInfoBase &E) {
3360642cd76SAdrian Prantl                               error = Status::FromErrorStringWithFormat(
3376870ac20Sjeffreytan81                                   "Unable to get the size of module %s: %s.",
3386870ac20Sjeffreytan81                                   module_name.c_str(), E.message().c_str());
3396870ac20Sjeffreytan81                             });
340eee687a6SAndrej Korman       return error;
341eee687a6SAndrej Korman     }
342eee687a6SAndrej Korman 
343eee687a6SAndrej Korman     uint64_t mod_size = std::move(*maybe_mod_size);
344eee687a6SAndrej Korman 
345eee687a6SAndrej Korman     llvm::support::ulittle32_t signature =
346eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(
347eee687a6SAndrej Korman             static_cast<uint32_t>(minidump::CvSignature::ElfBuildId));
348eee687a6SAndrej Korman     auto uuid = mod->GetUUID().GetBytes();
349eee687a6SAndrej Korman 
350eee687a6SAndrej Korman     VSFixedFileInfo info;
351eee687a6SAndrej Korman     info.Signature = static_cast<llvm::support::ulittle32_t>(0u);
352eee687a6SAndrej Korman     info.StructVersion = static_cast<llvm::support::ulittle32_t>(0u);
353eee687a6SAndrej Korman     info.FileVersionHigh = static_cast<llvm::support::ulittle32_t>(0u);
354eee687a6SAndrej Korman     info.FileVersionLow = static_cast<llvm::support::ulittle32_t>(0u);
355eee687a6SAndrej Korman     info.ProductVersionHigh = static_cast<llvm::support::ulittle32_t>(0u);
356eee687a6SAndrej Korman     info.ProductVersionLow = static_cast<llvm::support::ulittle32_t>(0u);
357eee687a6SAndrej Korman     info.FileFlagsMask = static_cast<llvm::support::ulittle32_t>(0u);
358eee687a6SAndrej Korman     info.FileFlags = static_cast<llvm::support::ulittle32_t>(0u);
359eee687a6SAndrej Korman     info.FileOS = static_cast<llvm::support::ulittle32_t>(0u);
360eee687a6SAndrej Korman     info.FileType = static_cast<llvm::support::ulittle32_t>(0u);
361eee687a6SAndrej Korman     info.FileSubtype = static_cast<llvm::support::ulittle32_t>(0u);
362eee687a6SAndrej Korman     info.FileDateHigh = static_cast<llvm::support::ulittle32_t>(0u);
363eee687a6SAndrej Korman     info.FileDateLow = static_cast<llvm::support::ulittle32_t>(0u);
364eee687a6SAndrej Korman 
365eee687a6SAndrej Korman     LocationDescriptor ld;
366eee687a6SAndrej Korman     ld.DataSize = static_cast<llvm::support::ulittle32_t>(0u);
367eee687a6SAndrej Korman     ld.RVA = static_cast<llvm::support::ulittle32_t>(0u);
368eee687a6SAndrej Korman 
369eee687a6SAndrej Korman     // Setting up LocationDescriptor for uuid string. The global offset into
370eee687a6SAndrej Korman     // minidump file is calculated.
371eee687a6SAndrej Korman     LocationDescriptor ld_cv;
372eee687a6SAndrej Korman     ld_cv.DataSize = static_cast<llvm::support::ulittle32_t>(
373eee687a6SAndrej Korman         sizeof(llvm::support::ulittle32_t) + uuid.size());
374eee687a6SAndrej Korman     ld_cv.RVA = static_cast<llvm::support::ulittle32_t>(
375eee687a6SAndrej Korman         size_before + module_stream_size + helper_data.GetByteSize());
376eee687a6SAndrej Korman 
377eee687a6SAndrej Korman     helper_data.AppendData(&signature, sizeof(llvm::support::ulittle32_t));
378eee687a6SAndrej Korman     helper_data.AppendData(uuid.begin(), uuid.size());
379eee687a6SAndrej Korman 
380eee687a6SAndrej Korman     llvm::minidump::Module m;
381eee687a6SAndrej Korman     m.BaseOfImage = static_cast<llvm::support::ulittle64_t>(
382eee687a6SAndrej Korman         mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target));
383eee687a6SAndrej Korman     m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(mod_size);
384eee687a6SAndrej Korman     m.Checksum = static_cast<llvm::support::ulittle32_t>(0);
385b8336280SKazu Hirata     m.TimeDateStamp =
386b8336280SKazu Hirata         static_cast<llvm::support::ulittle32_t>(std::time(nullptr));
387eee687a6SAndrej Korman     m.ModuleNameRVA = static_cast<llvm::support::ulittle32_t>(
388eee687a6SAndrej Korman         size_before + module_stream_size + helper_data.GetByteSize());
389eee687a6SAndrej Korman     m.VersionInfo = info;
390eee687a6SAndrej Korman     m.CvRecord = ld_cv;
391eee687a6SAndrej Korman     m.MiscRecord = ld;
392eee687a6SAndrej Korman 
393eee687a6SAndrej Korman     error = WriteString(module_name, &helper_data);
394eee687a6SAndrej Korman 
395eee687a6SAndrej Korman     if (error.Fail())
396eee687a6SAndrej Korman       return error;
397eee687a6SAndrej Korman 
398eee687a6SAndrej Korman     m_data.AppendData(&m, sizeof(llvm::minidump::Module));
399eee687a6SAndrej Korman   }
400eee687a6SAndrej Korman 
401eee687a6SAndrej Korman   m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
402eee687a6SAndrej Korman   return error;
403eee687a6SAndrej Korman }
404eee687a6SAndrej Korman 
405eee687a6SAndrej Korman uint16_t read_register_u16_raw(RegisterContext *reg_ctx,
406beb702c0SGreg Clayton                                llvm::StringRef reg_name) {
407eee687a6SAndrej Korman   const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
408eee687a6SAndrej Korman   if (!reg_info)
409eee687a6SAndrej Korman     return 0;
410eee687a6SAndrej Korman   lldb_private::RegisterValue reg_value;
411eee687a6SAndrej Korman   bool success = reg_ctx->ReadRegister(reg_info, reg_value);
412eee687a6SAndrej Korman   if (!success)
413eee687a6SAndrej Korman     return 0;
414eee687a6SAndrej Korman   return reg_value.GetAsUInt16();
415eee687a6SAndrej Korman }
416eee687a6SAndrej Korman 
417eee687a6SAndrej Korman uint32_t read_register_u32_raw(RegisterContext *reg_ctx,
418beb702c0SGreg Clayton                                llvm::StringRef reg_name) {
419eee687a6SAndrej Korman   const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
420eee687a6SAndrej Korman   if (!reg_info)
421eee687a6SAndrej Korman     return 0;
422eee687a6SAndrej Korman   lldb_private::RegisterValue reg_value;
423eee687a6SAndrej Korman   bool success = reg_ctx->ReadRegister(reg_info, reg_value);
424eee687a6SAndrej Korman   if (!success)
425eee687a6SAndrej Korman     return 0;
426eee687a6SAndrej Korman   return reg_value.GetAsUInt32();
427eee687a6SAndrej Korman }
428eee687a6SAndrej Korman 
429eee687a6SAndrej Korman uint64_t read_register_u64_raw(RegisterContext *reg_ctx,
430beb702c0SGreg Clayton                                llvm::StringRef reg_name) {
431eee687a6SAndrej Korman   const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
432eee687a6SAndrej Korman   if (!reg_info)
433eee687a6SAndrej Korman     return 0;
434eee687a6SAndrej Korman   lldb_private::RegisterValue reg_value;
435eee687a6SAndrej Korman   bool success = reg_ctx->ReadRegister(reg_info, reg_value);
436eee687a6SAndrej Korman   if (!success)
437eee687a6SAndrej Korman     return 0;
438eee687a6SAndrej Korman   return reg_value.GetAsUInt64();
439eee687a6SAndrej Korman }
440eee687a6SAndrej Korman 
441eee687a6SAndrej Korman llvm::support::ulittle16_t read_register_u16(RegisterContext *reg_ctx,
442beb702c0SGreg Clayton                                              llvm::StringRef reg_name) {
443eee687a6SAndrej Korman   return static_cast<llvm::support::ulittle16_t>(
444eee687a6SAndrej Korman       read_register_u16_raw(reg_ctx, reg_name));
445eee687a6SAndrej Korman }
446eee687a6SAndrej Korman 
447eee687a6SAndrej Korman llvm::support::ulittle32_t read_register_u32(RegisterContext *reg_ctx,
448beb702c0SGreg Clayton                                              llvm::StringRef reg_name) {
449eee687a6SAndrej Korman   return static_cast<llvm::support::ulittle32_t>(
450eee687a6SAndrej Korman       read_register_u32_raw(reg_ctx, reg_name));
451eee687a6SAndrej Korman }
452eee687a6SAndrej Korman 
453eee687a6SAndrej Korman llvm::support::ulittle64_t read_register_u64(RegisterContext *reg_ctx,
454beb702c0SGreg Clayton                                              llvm::StringRef reg_name) {
455eee687a6SAndrej Korman   return static_cast<llvm::support::ulittle64_t>(
456eee687a6SAndrej Korman       read_register_u64_raw(reg_ctx, reg_name));
457eee687a6SAndrej Korman }
458eee687a6SAndrej Korman 
459beb702c0SGreg Clayton void read_register_u128(RegisterContext *reg_ctx, llvm::StringRef reg_name,
460beb702c0SGreg Clayton                         uint8_t *dst) {
461beb702c0SGreg Clayton   const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
462beb702c0SGreg Clayton   if (reg_info) {
463beb702c0SGreg Clayton     lldb_private::RegisterValue reg_value;
464beb702c0SGreg Clayton     if (reg_ctx->ReadRegister(reg_info, reg_value)) {
465beb702c0SGreg Clayton       Status error;
466beb702c0SGreg Clayton       uint32_t bytes_copied = reg_value.GetAsMemoryData(
467beb702c0SGreg Clayton           *reg_info, dst, 16, lldb::ByteOrder::eByteOrderLittle, error);
468beb702c0SGreg Clayton       if (bytes_copied == 16)
469beb702c0SGreg Clayton         return;
470beb702c0SGreg Clayton     }
471beb702c0SGreg Clayton   }
472beb702c0SGreg Clayton   // If anything goes wrong, then zero out the register value.
473beb702c0SGreg Clayton   memset(dst, 0, 16);
474beb702c0SGreg Clayton }
475beb702c0SGreg Clayton 
476eee687a6SAndrej Korman lldb_private::minidump::MinidumpContext_x86_64
477beb702c0SGreg Clayton GetThreadContext_x86_64(RegisterContext *reg_ctx) {
4784871dfc6SSlava Gurevich   lldb_private::minidump::MinidumpContext_x86_64 thread_context = {};
479459cfa5eSSlava Gurevich   thread_context.p1_home = {};
480eee687a6SAndrej Korman   thread_context.context_flags = static_cast<uint32_t>(
481eee687a6SAndrej Korman       lldb_private::minidump::MinidumpContext_x86_64_Flags::x86_64_Flag |
482eee687a6SAndrej Korman       lldb_private::minidump::MinidumpContext_x86_64_Flags::Control |
483eee687a6SAndrej Korman       lldb_private::minidump::MinidumpContext_x86_64_Flags::Segments |
4842ed510dcSJacob Lalonde       lldb_private::minidump::MinidumpContext_x86_64_Flags::Integer |
4852ed510dcSJacob Lalonde       lldb_private::minidump::MinidumpContext_x86_64_Flags::LLDBSpecific);
486eee687a6SAndrej Korman   thread_context.rax = read_register_u64(reg_ctx, "rax");
487eee687a6SAndrej Korman   thread_context.rbx = read_register_u64(reg_ctx, "rbx");
488eee687a6SAndrej Korman   thread_context.rcx = read_register_u64(reg_ctx, "rcx");
489eee687a6SAndrej Korman   thread_context.rdx = read_register_u64(reg_ctx, "rdx");
490eee687a6SAndrej Korman   thread_context.rdi = read_register_u64(reg_ctx, "rdi");
491eee687a6SAndrej Korman   thread_context.rsi = read_register_u64(reg_ctx, "rsi");
492eee687a6SAndrej Korman   thread_context.rbp = read_register_u64(reg_ctx, "rbp");
493eee687a6SAndrej Korman   thread_context.rsp = read_register_u64(reg_ctx, "rsp");
494eee687a6SAndrej Korman   thread_context.r8 = read_register_u64(reg_ctx, "r8");
495eee687a6SAndrej Korman   thread_context.r9 = read_register_u64(reg_ctx, "r9");
496eee687a6SAndrej Korman   thread_context.r10 = read_register_u64(reg_ctx, "r10");
497eee687a6SAndrej Korman   thread_context.r11 = read_register_u64(reg_ctx, "r11");
498eee687a6SAndrej Korman   thread_context.r12 = read_register_u64(reg_ctx, "r12");
499eee687a6SAndrej Korman   thread_context.r13 = read_register_u64(reg_ctx, "r13");
500eee687a6SAndrej Korman   thread_context.r14 = read_register_u64(reg_ctx, "r14");
501eee687a6SAndrej Korman   thread_context.r15 = read_register_u64(reg_ctx, "r15");
502eee687a6SAndrej Korman   thread_context.rip = read_register_u64(reg_ctx, "rip");
50382ebd333SJacob Lalonde   // To make our code agnostic to whatever type the register value identifies
50482ebd333SJacob Lalonde   // itself as, we read as a u64 and truncate to u32/u16 ourselves.
50582ebd333SJacob Lalonde   thread_context.eflags = read_register_u64(reg_ctx, "rflags");
50682ebd333SJacob Lalonde   thread_context.cs = read_register_u64(reg_ctx, "cs");
50782ebd333SJacob Lalonde   thread_context.fs = read_register_u64(reg_ctx, "fs");
50882ebd333SJacob Lalonde   thread_context.gs = read_register_u64(reg_ctx, "gs");
50982ebd333SJacob Lalonde   thread_context.ss = read_register_u64(reg_ctx, "ss");
51082ebd333SJacob Lalonde   thread_context.ds = read_register_u64(reg_ctx, "ds");
5112ed510dcSJacob Lalonde   thread_context.fs_base = read_register_u64(reg_ctx, "fs_base");
5122ed510dcSJacob Lalonde   thread_context.gs_base = read_register_u64(reg_ctx, "gs_base");
513eee687a6SAndrej Korman   return thread_context;
514eee687a6SAndrej Korman }
515eee687a6SAndrej Korman 
516beb702c0SGreg Clayton minidump::RegisterContextMinidump_ARM64::Context
517beb702c0SGreg Clayton GetThreadContext_ARM64(RegisterContext *reg_ctx) {
518beb702c0SGreg Clayton   minidump::RegisterContextMinidump_ARM64::Context thread_context = {};
519beb702c0SGreg Clayton   thread_context.context_flags = static_cast<uint32_t>(
520beb702c0SGreg Clayton       minidump::RegisterContextMinidump_ARM64::Flags::ARM64_Flag |
521beb702c0SGreg Clayton       minidump::RegisterContextMinidump_ARM64::Flags::Integer |
522beb702c0SGreg Clayton       minidump::RegisterContextMinidump_ARM64::Flags::FloatingPoint);
523beb702c0SGreg Clayton   char reg_name[16];
524beb702c0SGreg Clayton   for (uint32_t i = 0; i < 31; ++i) {
525beb702c0SGreg Clayton     snprintf(reg_name, sizeof(reg_name), "x%u", i);
526beb702c0SGreg Clayton     thread_context.x[i] = read_register_u64(reg_ctx, reg_name);
527beb702c0SGreg Clayton   }
528beb702c0SGreg Clayton   // Work around a bug in debugserver where "sp" on arm64 doesn't have the alt
529beb702c0SGreg Clayton   // name set to "x31"
530beb702c0SGreg Clayton   thread_context.x[31] = read_register_u64(reg_ctx, "sp");
531beb702c0SGreg Clayton   thread_context.pc = read_register_u64(reg_ctx, "pc");
532beb702c0SGreg Clayton   thread_context.cpsr = read_register_u32(reg_ctx, "cpsr");
533beb702c0SGreg Clayton   thread_context.fpsr = read_register_u32(reg_ctx, "fpsr");
534beb702c0SGreg Clayton   thread_context.fpcr = read_register_u32(reg_ctx, "fpcr");
535beb702c0SGreg Clayton   for (uint32_t i = 0; i < 32; ++i) {
536beb702c0SGreg Clayton     snprintf(reg_name, sizeof(reg_name), "v%u", i);
537beb702c0SGreg Clayton     read_register_u128(reg_ctx, reg_name, &thread_context.v[i * 16]);
538beb702c0SGreg Clayton   }
539beb702c0SGreg Clayton   return thread_context;
540beb702c0SGreg Clayton }
541beb702c0SGreg Clayton 
542beb702c0SGreg Clayton class ArchThreadContexts {
543beb702c0SGreg Clayton   llvm::Triple::ArchType m_arch;
544beb702c0SGreg Clayton   union {
545beb702c0SGreg Clayton     lldb_private::minidump::MinidumpContext_x86_64 x86_64;
546beb702c0SGreg Clayton     lldb_private::minidump::RegisterContextMinidump_ARM64::Context arm64;
547beb702c0SGreg Clayton   };
548beb702c0SGreg Clayton 
549beb702c0SGreg Clayton public:
550beb702c0SGreg Clayton   ArchThreadContexts(llvm::Triple::ArchType arch) : m_arch(arch) {}
551beb702c0SGreg Clayton 
552beb702c0SGreg Clayton   bool prepareRegisterContext(RegisterContext *reg_ctx) {
553beb702c0SGreg Clayton     switch (m_arch) {
554beb702c0SGreg Clayton     case llvm::Triple::ArchType::x86_64:
555beb702c0SGreg Clayton       x86_64 = GetThreadContext_x86_64(reg_ctx);
556beb702c0SGreg Clayton       return true;
557beb702c0SGreg Clayton     case llvm::Triple::ArchType::aarch64:
558beb702c0SGreg Clayton       arm64 = GetThreadContext_ARM64(reg_ctx);
559beb702c0SGreg Clayton       return true;
560beb702c0SGreg Clayton     default:
561beb702c0SGreg Clayton       break;
562beb702c0SGreg Clayton     }
563beb702c0SGreg Clayton     return false;
564beb702c0SGreg Clayton   }
565beb702c0SGreg Clayton 
566beb702c0SGreg Clayton   const void *data() const { return &x86_64; }
567beb702c0SGreg Clayton 
568beb702c0SGreg Clayton   size_t size() const {
569beb702c0SGreg Clayton     switch (m_arch) {
570beb702c0SGreg Clayton     case llvm::Triple::ArchType::x86_64:
571beb702c0SGreg Clayton       return sizeof(x86_64);
572beb702c0SGreg Clayton     case llvm::Triple::ArchType::aarch64:
573beb702c0SGreg Clayton       return sizeof(arm64);
574beb702c0SGreg Clayton     default:
575beb702c0SGreg Clayton       break;
576beb702c0SGreg Clayton     }
577beb702c0SGreg Clayton     return 0;
578beb702c0SGreg Clayton   }
579beb702c0SGreg Clayton };
580beb702c0SGreg Clayton 
581a27164cbSJacob Lalonde Status MinidumpFileBuilder::FixThreadStacks() {
582a27164cbSJacob Lalonde   Status error;
583a27164cbSJacob Lalonde   // If we have anything in the heap flush it.
584a27164cbSJacob Lalonde   FlushBufferToDisk();
585a27164cbSJacob Lalonde   m_core_file->SeekFromStart(m_thread_list_start);
586a27164cbSJacob Lalonde   for (auto &pair : m_thread_by_range_end) {
587a27164cbSJacob Lalonde     // The thread objects will get a new memory descriptor added
588a27164cbSJacob Lalonde     // When we are emitting the memory list and then we write it here
589a27164cbSJacob Lalonde     const llvm::minidump::Thread &thread = pair.second;
590a27164cbSJacob Lalonde     size_t bytes_to_write = sizeof(llvm::minidump::Thread);
591a27164cbSJacob Lalonde     size_t bytes_written = bytes_to_write;
592a27164cbSJacob Lalonde     error = m_core_file->Write(&thread, bytes_written);
593a27164cbSJacob Lalonde     if (error.Fail() || bytes_to_write != bytes_written) {
5940642cd76SAdrian Prantl       error = Status::FromErrorStringWithFormat(
595a27164cbSJacob Lalonde           "Wrote incorrect number of bytes to minidump file. (written %zd/%zd)",
596a27164cbSJacob Lalonde           bytes_written, bytes_to_write);
597a27164cbSJacob Lalonde       return error;
598a27164cbSJacob Lalonde     }
59947d80ec1SJacob Lalonde   }
60047d80ec1SJacob Lalonde 
601a27164cbSJacob Lalonde   return error;
602eee687a6SAndrej Korman }
603eee687a6SAndrej Korman 
604a27164cbSJacob Lalonde Status MinidumpFileBuilder::AddThreadList() {
605eee687a6SAndrej Korman   constexpr size_t minidump_thread_size = sizeof(llvm::minidump::Thread);
606572943e7SJacob Lalonde   std::vector<ThreadSP> thread_list =
607572943e7SJacob Lalonde       m_process_sp->CalculateCoreFileThreadList(m_save_core_options);
608eee687a6SAndrej Korman 
609eee687a6SAndrej Korman   // size of the entire thread stream consists of:
610eee687a6SAndrej Korman   // number of threads and threads array
611eee687a6SAndrej Korman   size_t thread_stream_size = sizeof(llvm::support::ulittle32_t) +
612572943e7SJacob Lalonde                               thread_list.size() * minidump_thread_size;
613eee687a6SAndrej Korman   // save for the ability to set up RVA
614eee687a6SAndrej Korman   size_t size_before = GetCurrentDataEndOffset();
615a27164cbSJacob Lalonde   Status error;
616a27164cbSJacob Lalonde   error = AddDirectory(StreamType::ThreadList, thread_stream_size);
617a27164cbSJacob Lalonde   if (error.Fail())
618a27164cbSJacob Lalonde     return error;
619eee687a6SAndrej Korman 
620eee687a6SAndrej Korman   llvm::support::ulittle32_t thread_count =
621572943e7SJacob Lalonde       static_cast<llvm::support::ulittle32_t>(thread_list.size());
622eee687a6SAndrej Korman   m_data.AppendData(&thread_count, sizeof(llvm::support::ulittle32_t));
623eee687a6SAndrej Korman 
624a27164cbSJacob Lalonde   // Take the offset after the thread count.
625a27164cbSJacob Lalonde   m_thread_list_start = GetCurrentDataEndOffset();
626eee687a6SAndrej Korman   DataBufferHeap helper_data;
627eee687a6SAndrej Korman 
628a27164cbSJacob Lalonde   Log *log = GetLog(LLDBLog::Object);
629572943e7SJacob Lalonde   for (const ThreadSP &thread_sp : thread_list) {
630eee687a6SAndrej Korman     RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
631eee687a6SAndrej Korman 
632eee687a6SAndrej Korman     if (!reg_ctx_sp) {
6330642cd76SAdrian Prantl       error = Status::FromErrorString("Unable to get the register context.");
634eee687a6SAndrej Korman       return error;
635eee687a6SAndrej Korman     }
636eee687a6SAndrej Korman     RegisterContext *reg_ctx = reg_ctx_sp.get();
637a27164cbSJacob Lalonde     Target &target = m_process_sp->GetTarget();
638beb702c0SGreg Clayton     const ArchSpec &arch = target.GetArchitecture();
639beb702c0SGreg Clayton     ArchThreadContexts thread_context(arch.GetMachine());
640beb702c0SGreg Clayton     if (!thread_context.prepareRegisterContext(reg_ctx)) {
6410642cd76SAdrian Prantl       error = Status::FromErrorStringWithFormat(
642beb702c0SGreg Clayton           "architecture %s not supported.",
643beb702c0SGreg Clayton           arch.GetTriple().getArchName().str().c_str());
644beb702c0SGreg Clayton       return error;
645beb702c0SGreg Clayton     }
646a27164cbSJacob Lalonde 
647beb702c0SGreg Clayton     uint64_t sp = reg_ctx->GetSP();
648a27164cbSJacob Lalonde     MemoryRegionInfo sp_region;
649a27164cbSJacob Lalonde     m_process_sp->GetMemoryRegionInfo(sp, sp_region);
650eee687a6SAndrej Korman 
651a27164cbSJacob Lalonde     // Emit a blank descriptor
652eee687a6SAndrej Korman     MemoryDescriptor stack;
653a27164cbSJacob Lalonde     LocationDescriptor empty_label;
654a27164cbSJacob Lalonde     empty_label.DataSize = 0;
655a27164cbSJacob Lalonde     empty_label.RVA = 0;
656a27164cbSJacob Lalonde     stack.Memory = empty_label;
657a27164cbSJacob Lalonde     stack.StartOfMemoryRange = 0;
658eee687a6SAndrej Korman     LocationDescriptor thread_context_memory_locator;
659eee687a6SAndrej Korman     thread_context_memory_locator.DataSize =
660beb702c0SGreg Clayton         static_cast<llvm::support::ulittle32_t>(thread_context.size());
661eee687a6SAndrej Korman     thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
662eee687a6SAndrej Korman         size_before + thread_stream_size + helper_data.GetByteSize());
663beb702c0SGreg Clayton     // Cache thie thread context memory so we can reuse for exceptions.
664beb702c0SGreg Clayton     m_tid_to_reg_ctx[thread_sp->GetID()] = thread_context_memory_locator;
665eee687a6SAndrej Korman 
666a27164cbSJacob Lalonde     LLDB_LOGF(log, "AddThreadList for thread %d: thread_context %zu bytes",
667572943e7SJacob Lalonde               thread_sp->GetIndexID(), thread_context.size());
668beb702c0SGreg Clayton     helper_data.AppendData(thread_context.data(), thread_context.size());
669eee687a6SAndrej Korman 
670eee687a6SAndrej Korman     llvm::minidump::Thread t;
671eee687a6SAndrej Korman     t.ThreadId = static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
672eee687a6SAndrej Korman     t.SuspendCount = static_cast<llvm::support::ulittle32_t>(
673eee687a6SAndrej Korman         (thread_sp->GetState() == StateType::eStateSuspended) ? 1 : 0);
674eee687a6SAndrej Korman     t.PriorityClass = static_cast<llvm::support::ulittle32_t>(0);
675eee687a6SAndrej Korman     t.Priority = static_cast<llvm::support::ulittle32_t>(0);
676eee687a6SAndrej Korman     t.EnvironmentBlock = static_cast<llvm::support::ulittle64_t>(0);
677eee687a6SAndrej Korman     t.Stack = stack, t.Context = thread_context_memory_locator;
678eee687a6SAndrej Korman 
679a27164cbSJacob Lalonde     // We save off the stack object so we can circle back and clean it up.
680a27164cbSJacob Lalonde     m_thread_by_range_end[sp_region.GetRange().GetRangeEnd()] = t;
681eee687a6SAndrej Korman     m_data.AppendData(&t, sizeof(llvm::minidump::Thread));
682eee687a6SAndrej Korman   }
683eee687a6SAndrej Korman 
684a27164cbSJacob Lalonde   LLDB_LOGF(log, "AddThreadList(): total helper_data %" PRIx64 " bytes",
685a27164cbSJacob Lalonde             helper_data.GetByteSize());
686eee687a6SAndrej Korman   m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
687eee687a6SAndrej Korman   return Status();
688eee687a6SAndrej Korman }
689eee687a6SAndrej Korman 
690a27164cbSJacob Lalonde Status MinidumpFileBuilder::AddExceptions() {
691572943e7SJacob Lalonde   std::vector<ThreadSP> thread_list =
692572943e7SJacob Lalonde       m_process_sp->CalculateCoreFileThreadList(m_save_core_options);
693a27164cbSJacob Lalonde   Status error;
694572943e7SJacob Lalonde   for (const ThreadSP &thread_sp : thread_list) {
695eee687a6SAndrej Korman     StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
6965033ea73SJacob Lalonde     // If we don't have a stop info, or if it's invalid, skip.
6975033ea73SJacob Lalonde     if (!stop_info_sp ||
6985033ea73SJacob Lalonde         stop_info_sp->GetStopReason() == lldb::eStopReasonInvalid)
6995033ea73SJacob Lalonde       continue;
7005033ea73SJacob Lalonde 
701eee687a6SAndrej Korman     constexpr size_t minidump_exception_size =
702eee687a6SAndrej Korman         sizeof(llvm::minidump::ExceptionStream);
703a27164cbSJacob Lalonde     error = AddDirectory(StreamType::Exception, minidump_exception_size);
704a27164cbSJacob Lalonde     if (error.Fail())
705a27164cbSJacob Lalonde       return error;
706a27164cbSJacob Lalonde 
707beb702c0SGreg Clayton     RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
7084871dfc6SSlava Gurevich     Exception exp_record = {};
709eee687a6SAndrej Korman     exp_record.ExceptionCode =
710eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());
7115033ea73SJacob Lalonde     exp_record.ExceptionFlags =
7125033ea73SJacob Lalonde         static_cast<llvm::support::ulittle32_t>(Exception::LLDB_FLAG);
713eee687a6SAndrej Korman     exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);
714beb702c0SGreg Clayton     exp_record.ExceptionAddress = reg_ctx_sp->GetPC();
7155033ea73SJacob Lalonde     exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(1);
7165033ea73SJacob Lalonde     std::string description = stop_info_sp->GetDescription();
7175033ea73SJacob Lalonde     // We have 120 bytes to work with and it's unlikely description will
7185033ea73SJacob Lalonde     // overflow, but we gotta check.
7195033ea73SJacob Lalonde     memcpy(&exp_record.ExceptionInformation, description.c_str(),
7205033ea73SJacob Lalonde            std::max(description.size(), Exception::MaxParameterBytes));
721eee687a6SAndrej Korman     exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
722eee687a6SAndrej Korman     ExceptionStream exp_stream;
723eee687a6SAndrej Korman     exp_stream.ThreadId =
724eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
725eee687a6SAndrej Korman     exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
726eee687a6SAndrej Korman     exp_stream.ExceptionRecord = exp_record;
727beb702c0SGreg Clayton     auto Iter = m_tid_to_reg_ctx.find(thread_sp->GetID());
728beb702c0SGreg Clayton     if (Iter != m_tid_to_reg_ctx.end()) {
729beb702c0SGreg Clayton       exp_stream.ThreadContext = Iter->second;
730beb702c0SGreg Clayton     } else {
731beb702c0SGreg Clayton       exp_stream.ThreadContext.DataSize = 0;
732beb702c0SGreg Clayton       exp_stream.ThreadContext.RVA = 0;
733beb702c0SGreg Clayton     }
734eee687a6SAndrej Korman     m_data.AppendData(&exp_stream, minidump_exception_size);
735beb702c0SGreg Clayton   }
736a27164cbSJacob Lalonde 
737a27164cbSJacob Lalonde   return error;
738eee687a6SAndrej Korman }
739eee687a6SAndrej Korman 
740a27164cbSJacob Lalonde lldb_private::Status MinidumpFileBuilder::AddMiscInfo() {
741eee687a6SAndrej Korman   Status error;
742a27164cbSJacob Lalonde   error = AddDirectory(StreamType::MiscInfo,
743eee687a6SAndrej Korman                        sizeof(lldb_private::minidump::MinidumpMiscInfo));
744a27164cbSJacob Lalonde   if (error.Fail())
745a27164cbSJacob Lalonde     return error;
746eee687a6SAndrej Korman 
747eee687a6SAndrej Korman   lldb_private::minidump::MinidumpMiscInfo misc_info;
748eee687a6SAndrej Korman   misc_info.size = static_cast<llvm::support::ulittle32_t>(
749eee687a6SAndrej Korman       sizeof(lldb_private::minidump::MinidumpMiscInfo));
750eee687a6SAndrej Korman   // Default set flags1 to 0, in case that we will not be able to
751eee687a6SAndrej Korman   // get any information
752eee687a6SAndrej Korman   misc_info.flags1 = static_cast<llvm::support::ulittle32_t>(0);
753eee687a6SAndrej Korman 
754eee687a6SAndrej Korman   lldb_private::ProcessInstanceInfo process_info;
755a27164cbSJacob Lalonde   m_process_sp->GetProcessInfo(process_info);
756eee687a6SAndrej Korman   if (process_info.ProcessIDIsValid()) {
757eee687a6SAndrej Korman     // Set flags1 to reflect that PID is filled in
758eee687a6SAndrej Korman     misc_info.flags1 =
759eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(static_cast<uint32_t>(
760eee687a6SAndrej Korman             lldb_private::minidump::MinidumpMiscInfoFlags::ProcessID));
761eee687a6SAndrej Korman     misc_info.process_id =
762eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(process_info.GetProcessID());
763eee687a6SAndrej Korman   }
764eee687a6SAndrej Korman 
765eee687a6SAndrej Korman   m_data.AppendData(&misc_info,
766eee687a6SAndrej Korman                     sizeof(lldb_private::minidump::MinidumpMiscInfo));
767a27164cbSJacob Lalonde   return error;
768eee687a6SAndrej Korman }
769eee687a6SAndrej Korman 
770eee687a6SAndrej Korman std::unique_ptr<llvm::MemoryBuffer>
771eee687a6SAndrej Korman getFileStreamHelper(const std::string &path) {
772eee687a6SAndrej Korman   auto maybe_stream = llvm::MemoryBuffer::getFileAsStream(path);
773eee687a6SAndrej Korman   if (!maybe_stream)
774eee687a6SAndrej Korman     return nullptr;
775eee687a6SAndrej Korman   return std::move(maybe_stream.get());
776eee687a6SAndrej Korman }
777eee687a6SAndrej Korman 
778a27164cbSJacob Lalonde Status MinidumpFileBuilder::AddLinuxFileStreams() {
779a27164cbSJacob Lalonde   Status error;
780a27164cbSJacob Lalonde   // No-op if we are not on linux.
781a27164cbSJacob Lalonde   if (m_process_sp->GetTarget().GetArchitecture().GetTriple().getOS() !=
782a27164cbSJacob Lalonde       llvm::Triple::Linux)
783a27164cbSJacob Lalonde     return error;
784a27164cbSJacob Lalonde 
785eee687a6SAndrej Korman   std::vector<std::pair<StreamType, std::string>> files_with_stream_types = {
786eee687a6SAndrej Korman       {StreamType::LinuxCPUInfo, "/proc/cpuinfo"},
787eee687a6SAndrej Korman       {StreamType::LinuxLSBRelease, "/etc/lsb-release"},
788eee687a6SAndrej Korman   };
789eee687a6SAndrej Korman 
790eee687a6SAndrej Korman   lldb_private::ProcessInstanceInfo process_info;
791a27164cbSJacob Lalonde   m_process_sp->GetProcessInfo(process_info);
792eee687a6SAndrej Korman   if (process_info.ProcessIDIsValid()) {
793eee687a6SAndrej Korman     lldb::pid_t pid = process_info.GetProcessID();
794eee687a6SAndrej Korman     std::string pid_str = std::to_string(pid);
795eee687a6SAndrej Korman     files_with_stream_types.push_back(
796eee687a6SAndrej Korman         {StreamType::LinuxProcStatus, "/proc/" + pid_str + "/status"});
797eee687a6SAndrej Korman     files_with_stream_types.push_back(
798eee687a6SAndrej Korman         {StreamType::LinuxCMDLine, "/proc/" + pid_str + "/cmdline"});
799eee687a6SAndrej Korman     files_with_stream_types.push_back(
800eee687a6SAndrej Korman         {StreamType::LinuxEnviron, "/proc/" + pid_str + "/environ"});
801eee687a6SAndrej Korman     files_with_stream_types.push_back(
802eee687a6SAndrej Korman         {StreamType::LinuxAuxv, "/proc/" + pid_str + "/auxv"});
803eee687a6SAndrej Korman     files_with_stream_types.push_back(
804eee687a6SAndrej Korman         {StreamType::LinuxMaps, "/proc/" + pid_str + "/maps"});
805eee687a6SAndrej Korman     files_with_stream_types.push_back(
806eee687a6SAndrej Korman         {StreamType::LinuxProcStat, "/proc/" + pid_str + "/stat"});
807eee687a6SAndrej Korman     files_with_stream_types.push_back(
808eee687a6SAndrej Korman         {StreamType::LinuxProcFD, "/proc/" + pid_str + "/fd"});
809eee687a6SAndrej Korman   }
810eee687a6SAndrej Korman 
811eee687a6SAndrej Korman   for (const auto &entry : files_with_stream_types) {
812eee687a6SAndrej Korman     StreamType stream = entry.first;
813eee687a6SAndrej Korman     std::string path = entry.second;
814eee687a6SAndrej Korman     auto memory_buffer = getFileStreamHelper(path);
815eee687a6SAndrej Korman 
816eee687a6SAndrej Korman     if (memory_buffer) {
817eee687a6SAndrej Korman       size_t size = memory_buffer->getBufferSize();
818eee687a6SAndrej Korman       if (size == 0)
819eee687a6SAndrej Korman         continue;
820a27164cbSJacob Lalonde       error = AddDirectory(stream, size);
821a27164cbSJacob Lalonde       if (error.Fail())
822a27164cbSJacob Lalonde         return error;
823eee687a6SAndrej Korman       m_data.AppendData(memory_buffer->getBufferStart(), size);
824eee687a6SAndrej Korman     }
825eee687a6SAndrej Korman   }
826a27164cbSJacob Lalonde 
827a27164cbSJacob Lalonde   return error;
828eee687a6SAndrej Korman }
829eee687a6SAndrej Korman 
830572943e7SJacob Lalonde Status MinidumpFileBuilder::AddMemoryList() {
831a27164cbSJacob Lalonde   Status error;
832eee687a6SAndrej Korman 
833a27164cbSJacob Lalonde   // We first save the thread stacks to ensure they fit in the first UINT32_MAX
834a27164cbSJacob Lalonde   // bytes of the core file. Thread structures in minidump files can only use
835a27164cbSJacob Lalonde   // 32 bit memory descriptiors, so we emit them first to ensure the memory is
836a27164cbSJacob Lalonde   // in accessible with a 32 bit offset.
83796b7c64bSJacob Lalonde   std::vector<CoreFileMemoryRange> ranges_32;
83896b7c64bSJacob Lalonde   std::vector<CoreFileMemoryRange> ranges_64;
83996b7c64bSJacob Lalonde   CoreFileMemoryRanges all_core_memory_ranges;
840572943e7SJacob Lalonde   error = m_process_sp->CalculateCoreFileSaveRanges(m_save_core_options,
841572943e7SJacob Lalonde                                                     all_core_memory_ranges);
84296b7c64bSJacob Lalonde 
8430975e2acSJacob Lalonde   if (error.Fail())
8440975e2acSJacob Lalonde     return error;
8450975e2acSJacob Lalonde 
8460975e2acSJacob Lalonde   lldb_private::Progress progress("Saving Minidump File", "",
8470975e2acSJacob Lalonde                                   all_core_memory_ranges.GetSize());
84896b7c64bSJacob Lalonde   std::vector<CoreFileMemoryRange> all_core_memory_vec;
84996b7c64bSJacob Lalonde   // Extract all the data into just a vector of data. So we can mutate this in
85096b7c64bSJacob Lalonde   // place.
85196b7c64bSJacob Lalonde   for (const auto &core_range : all_core_memory_ranges)
85296b7c64bSJacob Lalonde     all_core_memory_vec.push_back(core_range.data);
85396b7c64bSJacob Lalonde 
854572943e7SJacob Lalonde   // Start by saving all of the stacks and ensuring they fit under the 32b
855572943e7SJacob Lalonde   // limit.
856a27164cbSJacob Lalonde   uint64_t total_size = GetCurrentDataEndOffset();
85796b7c64bSJacob Lalonde   auto iterator = all_core_memory_vec.begin();
85896b7c64bSJacob Lalonde   while (iterator != all_core_memory_vec.end()) {
85978ff3401SJacob Lalonde     if (m_thread_by_range_end.count(iterator->range.end()) > 0) {
860572943e7SJacob Lalonde       // We don't save stacks twice.
861572943e7SJacob Lalonde       ranges_32.push_back(*iterator);
862572943e7SJacob Lalonde       total_size +=
863572943e7SJacob Lalonde           iterator->range.size() + sizeof(llvm::minidump::MemoryDescriptor);
86496b7c64bSJacob Lalonde       iterator = all_core_memory_vec.erase(iterator);
865572943e7SJacob Lalonde     } else {
866572943e7SJacob Lalonde       iterator++;
867572943e7SJacob Lalonde     }
868a27164cbSJacob Lalonde   }
869a27164cbSJacob Lalonde 
870a27164cbSJacob Lalonde   if (total_size >= UINT32_MAX) {
8710642cd76SAdrian Prantl     error = Status::FromErrorStringWithFormat(
8720642cd76SAdrian Prantl         "Unable to write minidump. Stack memory "
873a27164cbSJacob Lalonde         "exceeds 32b limit. (Num Stacks %zu)",
874a27164cbSJacob Lalonde         ranges_32.size());
875a27164cbSJacob Lalonde     return error;
876a27164cbSJacob Lalonde   }
877a27164cbSJacob Lalonde 
878a27164cbSJacob Lalonde   // After saving the stacks, we start packing as much as we can into 32b.
879a27164cbSJacob Lalonde   // We apply a generous padding here so that the Directory, MemoryList and
880a27164cbSJacob Lalonde   // Memory64List sections all begin in 32b addressable space.
881a27164cbSJacob Lalonde   // Then anything overflow extends into 64b addressable space.
882a27164cbSJacob Lalonde   // All core memeroy ranges will either container nothing on stacks only
883a27164cbSJacob Lalonde   // or all the memory ranges including stacks
88496b7c64bSJacob Lalonde   if (!all_core_memory_vec.empty())
88596b7c64bSJacob Lalonde     total_size += 256 + (all_core_memory_vec.size() *
886572943e7SJacob Lalonde                          sizeof(llvm::minidump::MemoryDescriptor_64));
887a27164cbSJacob Lalonde 
88896b7c64bSJacob Lalonde   for (const auto &core_range : all_core_memory_vec) {
889a27164cbSJacob Lalonde     const addr_t range_size = core_range.range.size();
890572943e7SJacob Lalonde     // We don't need to check for stacks here because we already removed them
891572943e7SJacob Lalonde     // from all_core_memory_ranges.
892a27164cbSJacob Lalonde     if (total_size + range_size < UINT32_MAX) {
893a27164cbSJacob Lalonde       ranges_32.push_back(core_range);
894a27164cbSJacob Lalonde       total_size += range_size;
895a27164cbSJacob Lalonde     } else {
896a27164cbSJacob Lalonde       ranges_64.push_back(core_range);
897a27164cbSJacob Lalonde     }
898a27164cbSJacob Lalonde   }
899a27164cbSJacob Lalonde 
9000975e2acSJacob Lalonde   error = AddMemoryList_32(ranges_32, progress);
901a27164cbSJacob Lalonde   if (error.Fail())
902a27164cbSJacob Lalonde     return error;
903a27164cbSJacob Lalonde 
904a27164cbSJacob Lalonde   // Add the remaining memory as a 64b range.
905a27164cbSJacob Lalonde   if (!ranges_64.empty()) {
9060975e2acSJacob Lalonde     error = AddMemoryList_64(ranges_64, progress);
907a27164cbSJacob Lalonde     if (error.Fail())
908a27164cbSJacob Lalonde       return error;
909a27164cbSJacob Lalonde   }
910a27164cbSJacob Lalonde 
911a27164cbSJacob Lalonde   return FixThreadStacks();
912a27164cbSJacob Lalonde }
913a27164cbSJacob Lalonde 
914a27164cbSJacob Lalonde Status MinidumpFileBuilder::DumpHeader() const {
915eee687a6SAndrej Korman   // write header
916eee687a6SAndrej Korman   llvm::minidump::Header header;
917eee687a6SAndrej Korman   header.Signature = static_cast<llvm::support::ulittle32_t>(
918eee687a6SAndrej Korman       llvm::minidump::Header::MagicSignature);
919eee687a6SAndrej Korman   header.Version = static_cast<llvm::support::ulittle32_t>(
920eee687a6SAndrej Korman       llvm::minidump::Header::MagicVersion);
921eee687a6SAndrej Korman   header.NumberOfStreams =
922a27164cbSJacob Lalonde       static_cast<llvm::support::ulittle32_t>(m_directories.size());
923a27164cbSJacob Lalonde   // We write the directories right after the header.
924eee687a6SAndrej Korman   header.StreamDirectoryRVA =
925a27164cbSJacob Lalonde       static_cast<llvm::support::ulittle32_t>(HEADER_SIZE);
926eee687a6SAndrej Korman   header.Checksum = static_cast<llvm::support::ulittle32_t>(
927eee687a6SAndrej Korman       0u), // not used in most of the writers
928eee687a6SAndrej Korman       header.TimeDateStamp =
929b8336280SKazu Hirata           static_cast<llvm::support::ulittle32_t>(std::time(nullptr));
930eee687a6SAndrej Korman   header.Flags =
931eee687a6SAndrej Korman       static_cast<llvm::support::ulittle64_t>(0u); // minidump normal flag
932eee687a6SAndrej Korman 
933eee687a6SAndrej Korman   Status error;
934eee687a6SAndrej Korman   size_t bytes_written;
935eee687a6SAndrej Korman 
936a27164cbSJacob Lalonde   m_core_file->SeekFromStart(0);
937a27164cbSJacob Lalonde   bytes_written = HEADER_SIZE;
938a27164cbSJacob Lalonde   error = m_core_file->Write(&header, bytes_written);
939a27164cbSJacob Lalonde   if (error.Fail() || bytes_written != HEADER_SIZE) {
940a27164cbSJacob Lalonde     if (bytes_written != HEADER_SIZE)
9410642cd76SAdrian Prantl       error = Status::FromErrorStringWithFormat(
942a27164cbSJacob Lalonde           "Unable to write the minidump header (written %zd/%zd)",
943a27164cbSJacob Lalonde           bytes_written, HEADER_SIZE);
944a27164cbSJacob Lalonde     return error;
945a27164cbSJacob Lalonde   }
946eee687a6SAndrej Korman   return error;
947eee687a6SAndrej Korman }
948eee687a6SAndrej Korman 
949a27164cbSJacob Lalonde offset_t MinidumpFileBuilder::GetCurrentDataEndOffset() const {
950a27164cbSJacob Lalonde   return m_data.GetByteSize() + m_saved_data_size;
951eee687a6SAndrej Korman }
952eee687a6SAndrej Korman 
953a27164cbSJacob Lalonde Status MinidumpFileBuilder::DumpDirectories() const {
954a27164cbSJacob Lalonde   Status error;
955a27164cbSJacob Lalonde   size_t bytes_written;
956a27164cbSJacob Lalonde   m_core_file->SeekFromStart(HEADER_SIZE);
957eee687a6SAndrej Korman   for (const Directory &dir : m_directories) {
958a27164cbSJacob Lalonde     bytes_written = DIRECTORY_SIZE;
959a27164cbSJacob Lalonde     error = m_core_file->Write(&dir, bytes_written);
960a27164cbSJacob Lalonde     if (error.Fail() || bytes_written != DIRECTORY_SIZE) {
961a27164cbSJacob Lalonde       if (bytes_written != DIRECTORY_SIZE)
9620642cd76SAdrian Prantl         error = Status::FromErrorStringWithFormat(
963e69d3598SFangrui Song             "unable to write the directory (written %zd/%zd)", bytes_written,
964a27164cbSJacob Lalonde             DIRECTORY_SIZE);
965eee687a6SAndrej Korman       return error;
966eee687a6SAndrej Korman     }
967eee687a6SAndrej Korman   }
968eee687a6SAndrej Korman 
969eee687a6SAndrej Korman   return error;
970eee687a6SAndrej Korman }
971eee687a6SAndrej Korman 
972a27164cbSJacob Lalonde static uint64_t
97396b7c64bSJacob Lalonde GetLargestRangeSize(const std::vector<CoreFileMemoryRange> &ranges) {
974a27164cbSJacob Lalonde   uint64_t max_size = 0;
975a27164cbSJacob Lalonde   for (const auto &core_range : ranges)
976a27164cbSJacob Lalonde     max_size = std::max(max_size, core_range.range.size());
977a27164cbSJacob Lalonde   return max_size;
978eee687a6SAndrej Korman }
979eee687a6SAndrej Korman 
9800975e2acSJacob Lalonde Status
9810975e2acSJacob Lalonde MinidumpFileBuilder::AddMemoryList_32(std::vector<CoreFileMemoryRange> &ranges,
9820975e2acSJacob Lalonde                                       Progress &progress) {
983a27164cbSJacob Lalonde   std::vector<MemoryDescriptor> descriptors;
984a27164cbSJacob Lalonde   Status error;
985a27164cbSJacob Lalonde   if (ranges.size() == 0)
986a27164cbSJacob Lalonde     return error;
987a27164cbSJacob Lalonde 
988a27164cbSJacob Lalonde   Log *log = GetLog(LLDBLog::Object);
989a27164cbSJacob Lalonde   size_t region_index = 0;
990a27164cbSJacob Lalonde   auto data_up =
991a27164cbSJacob Lalonde       std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0);
992a27164cbSJacob Lalonde   for (const auto &core_range : ranges) {
993a27164cbSJacob Lalonde     // Take the offset before we write.
994a27164cbSJacob Lalonde     const offset_t offset_for_data = GetCurrentDataEndOffset();
995a27164cbSJacob Lalonde     const addr_t addr = core_range.range.start();
996a27164cbSJacob Lalonde     const addr_t size = core_range.range.size();
997a27164cbSJacob Lalonde     const addr_t end = core_range.range.end();
998a27164cbSJacob Lalonde 
999a27164cbSJacob Lalonde     LLDB_LOGF(log,
1000a27164cbSJacob Lalonde               "AddMemoryList %zu/%zu reading memory for region "
1001a27164cbSJacob Lalonde               "(%" PRIx64 " bytes) [%" PRIx64 ", %" PRIx64 ")",
1002a27164cbSJacob Lalonde               region_index, ranges.size(), size, addr, addr + size);
1003a27164cbSJacob Lalonde     ++region_index;
1004a27164cbSJacob Lalonde 
10050975e2acSJacob Lalonde     progress.Increment(1, "Adding Memory Range " + core_range.Dump());
1006a27164cbSJacob Lalonde     const size_t bytes_read =
1007a27164cbSJacob Lalonde         m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);
1008a27164cbSJacob Lalonde     if (error.Fail() || bytes_read == 0) {
1009a27164cbSJacob Lalonde       LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s",
1010a27164cbSJacob Lalonde                 bytes_read, error.AsCString());
1011a27164cbSJacob Lalonde       // Just skip sections with errors or zero bytes in 32b mode
1012a27164cbSJacob Lalonde       continue;
1013a27164cbSJacob Lalonde     } else if (bytes_read != size) {
1014a27164cbSJacob Lalonde       LLDB_LOGF(
1015a27164cbSJacob Lalonde           log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes",
1016a27164cbSJacob Lalonde           addr, size);
1017a27164cbSJacob Lalonde     }
1018a27164cbSJacob Lalonde 
1019a27164cbSJacob Lalonde     MemoryDescriptor descriptor;
1020a27164cbSJacob Lalonde     descriptor.StartOfMemoryRange =
1021a27164cbSJacob Lalonde         static_cast<llvm::support::ulittle64_t>(addr);
1022a27164cbSJacob Lalonde     descriptor.Memory.DataSize =
1023a27164cbSJacob Lalonde         static_cast<llvm::support::ulittle32_t>(bytes_read);
1024a27164cbSJacob Lalonde     descriptor.Memory.RVA =
1025a27164cbSJacob Lalonde         static_cast<llvm::support::ulittle32_t>(offset_for_data);
1026a27164cbSJacob Lalonde     descriptors.push_back(descriptor);
1027a27164cbSJacob Lalonde     if (m_thread_by_range_end.count(end) > 0)
1028a27164cbSJacob Lalonde       m_thread_by_range_end[end].Stack = descriptor;
1029a27164cbSJacob Lalonde 
1030a27164cbSJacob Lalonde     // Add the data to the buffer, flush as needed.
1031a27164cbSJacob Lalonde     error = AddData(data_up->GetBytes(), bytes_read);
1032a27164cbSJacob Lalonde     if (error.Fail())
1033a27164cbSJacob Lalonde       return error;
1034a27164cbSJacob Lalonde   }
1035a27164cbSJacob Lalonde 
1036a27164cbSJacob Lalonde   // Add a directory that references this list
1037a27164cbSJacob Lalonde   // With a size of the number of ranges as a 32 bit num
1038a27164cbSJacob Lalonde   // And then the size of all the ranges
1039a27164cbSJacob Lalonde   error = AddDirectory(StreamType::MemoryList,
10406cb14599SJacob Lalonde                        sizeof(llvm::minidump::MemoryListHeader) +
1041a27164cbSJacob Lalonde                            descriptors.size() *
1042a27164cbSJacob Lalonde                                sizeof(llvm::minidump::MemoryDescriptor));
1043a27164cbSJacob Lalonde   if (error.Fail())
1044a27164cbSJacob Lalonde     return error;
1045a27164cbSJacob Lalonde 
10466cb14599SJacob Lalonde   llvm::minidump::MemoryListHeader list_header;
1047a27164cbSJacob Lalonde   llvm::support::ulittle32_t memory_ranges_num =
1048a27164cbSJacob Lalonde       static_cast<llvm::support::ulittle32_t>(descriptors.size());
10496cb14599SJacob Lalonde   list_header.NumberOfMemoryRanges = memory_ranges_num;
10506cb14599SJacob Lalonde   m_data.AppendData(&list_header, sizeof(llvm::minidump::MemoryListHeader));
1051a27164cbSJacob Lalonde   // For 32b we can get away with writing off the descriptors after the data.
1052a27164cbSJacob Lalonde   // This means no cleanup loop needed.
1053a27164cbSJacob Lalonde   m_data.AppendData(descriptors.data(),
1054a27164cbSJacob Lalonde                     descriptors.size() * sizeof(MemoryDescriptor));
1055a27164cbSJacob Lalonde 
1056a27164cbSJacob Lalonde   return error;
1057a27164cbSJacob Lalonde }
1058a27164cbSJacob Lalonde 
10590975e2acSJacob Lalonde Status
10600975e2acSJacob Lalonde MinidumpFileBuilder::AddMemoryList_64(std::vector<CoreFileMemoryRange> &ranges,
10610975e2acSJacob Lalonde                                       Progress &progress) {
1062a27164cbSJacob Lalonde   Status error;
1063a27164cbSJacob Lalonde   if (ranges.empty())
1064a27164cbSJacob Lalonde     return error;
1065a27164cbSJacob Lalonde 
1066a27164cbSJacob Lalonde   error = AddDirectory(StreamType::Memory64List,
1067a27164cbSJacob Lalonde                        (sizeof(llvm::support::ulittle64_t) * 2) +
1068a27164cbSJacob Lalonde                            ranges.size() *
1069a27164cbSJacob Lalonde                                sizeof(llvm::minidump::MemoryDescriptor_64));
1070a27164cbSJacob Lalonde   if (error.Fail())
1071a27164cbSJacob Lalonde     return error;
1072a27164cbSJacob Lalonde 
10736cb14599SJacob Lalonde   llvm::minidump::Memory64ListHeader list_header;
1074a27164cbSJacob Lalonde   llvm::support::ulittle64_t memory_ranges_num =
1075a27164cbSJacob Lalonde       static_cast<llvm::support::ulittle64_t>(ranges.size());
10766cb14599SJacob Lalonde   list_header.NumberOfMemoryRanges = memory_ranges_num;
1077a27164cbSJacob Lalonde   // Capture the starting offset for all the descriptors so we can clean them up
1078a27164cbSJacob Lalonde   // if needed.
1079a27164cbSJacob Lalonde   offset_t starting_offset =
1080a27164cbSJacob Lalonde       GetCurrentDataEndOffset() + sizeof(llvm::support::ulittle64_t);
1081a27164cbSJacob Lalonde   // The base_rva needs to start after the directories, which is right after
1082a27164cbSJacob Lalonde   // this 8 byte variable.
1083a27164cbSJacob Lalonde   offset_t base_rva =
1084a27164cbSJacob Lalonde       starting_offset +
1085a27164cbSJacob Lalonde       (ranges.size() * sizeof(llvm::minidump::MemoryDescriptor_64));
1086a27164cbSJacob Lalonde   llvm::support::ulittle64_t memory_ranges_base_rva =
1087a27164cbSJacob Lalonde       static_cast<llvm::support::ulittle64_t>(base_rva);
10886cb14599SJacob Lalonde   list_header.BaseRVA = memory_ranges_base_rva;
10896cb14599SJacob Lalonde   m_data.AppendData(&list_header, sizeof(llvm::minidump::Memory64ListHeader));
1090a27164cbSJacob Lalonde 
1091a27164cbSJacob Lalonde   bool cleanup_required = false;
1092a27164cbSJacob Lalonde   std::vector<MemoryDescriptor_64> descriptors;
1093a27164cbSJacob Lalonde   // Enumerate the ranges and create the memory descriptors so we can append
1094a27164cbSJacob Lalonde   // them first
1095a27164cbSJacob Lalonde   for (const auto core_range : ranges) {
1096a27164cbSJacob Lalonde     // Add the space required to store the memory descriptor
1097a27164cbSJacob Lalonde     MemoryDescriptor_64 memory_desc;
1098a27164cbSJacob Lalonde     memory_desc.StartOfMemoryRange =
1099a27164cbSJacob Lalonde         static_cast<llvm::support::ulittle64_t>(core_range.range.start());
1100a27164cbSJacob Lalonde     memory_desc.DataSize =
1101a27164cbSJacob Lalonde         static_cast<llvm::support::ulittle64_t>(core_range.range.size());
1102a27164cbSJacob Lalonde     descriptors.push_back(memory_desc);
1103a27164cbSJacob Lalonde     // Now write this memory descriptor to the buffer.
1104a27164cbSJacob Lalonde     m_data.AppendData(&memory_desc, sizeof(MemoryDescriptor_64));
1105a27164cbSJacob Lalonde   }
1106a27164cbSJacob Lalonde 
1107a27164cbSJacob Lalonde   Log *log = GetLog(LLDBLog::Object);
1108a27164cbSJacob Lalonde   size_t region_index = 0;
1109a27164cbSJacob Lalonde   auto data_up =
1110a27164cbSJacob Lalonde       std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0);
1111a27164cbSJacob Lalonde   for (const auto &core_range : ranges) {
1112a27164cbSJacob Lalonde     const addr_t addr = core_range.range.start();
1113a27164cbSJacob Lalonde     const addr_t size = core_range.range.size();
1114a27164cbSJacob Lalonde 
1115a27164cbSJacob Lalonde     LLDB_LOGF(log,
1116a27164cbSJacob Lalonde               "AddMemoryList_64 %zu/%zu reading memory for region "
1117a27164cbSJacob Lalonde               "(%" PRIx64 "bytes) "
1118a27164cbSJacob Lalonde               "[%" PRIx64 ", %" PRIx64 ")",
1119a27164cbSJacob Lalonde               region_index, ranges.size(), size, addr, addr + size);
1120a27164cbSJacob Lalonde     ++region_index;
1121a27164cbSJacob Lalonde 
11220975e2acSJacob Lalonde     progress.Increment(1, "Adding Memory Range " + core_range.Dump());
1123a27164cbSJacob Lalonde     const size_t bytes_read =
1124a27164cbSJacob Lalonde         m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);
1125a27164cbSJacob Lalonde     if (error.Fail()) {
1126a27164cbSJacob Lalonde       LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s",
1127a27164cbSJacob Lalonde                 bytes_read, error.AsCString());
1128a27164cbSJacob Lalonde       error.Clear();
1129a27164cbSJacob Lalonde       cleanup_required = true;
1130a27164cbSJacob Lalonde       descriptors[region_index].DataSize = 0;
1131a27164cbSJacob Lalonde     }
1132a27164cbSJacob Lalonde     if (bytes_read != size) {
1133a27164cbSJacob Lalonde       LLDB_LOGF(
1134a27164cbSJacob Lalonde           log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes",
1135a27164cbSJacob Lalonde           addr, size);
1136a27164cbSJacob Lalonde       cleanup_required = true;
1137a27164cbSJacob Lalonde       descriptors[region_index].DataSize = bytes_read;
1138a27164cbSJacob Lalonde     }
1139a27164cbSJacob Lalonde 
1140a27164cbSJacob Lalonde     // Add the data to the buffer, flush as needed.
1141a27164cbSJacob Lalonde     error = AddData(data_up->GetBytes(), bytes_read);
1142a27164cbSJacob Lalonde     if (error.Fail())
1143a27164cbSJacob Lalonde       return error;
1144a27164cbSJacob Lalonde   }
1145a27164cbSJacob Lalonde 
1146a27164cbSJacob Lalonde   // Early return if there is no cleanup needed.
1147a27164cbSJacob Lalonde   if (!cleanup_required) {
1148a27164cbSJacob Lalonde     return error;
1149a27164cbSJacob Lalonde   } else {
1150a27164cbSJacob Lalonde     // Flush to disk we can make the fixes in place.
1151a27164cbSJacob Lalonde     FlushBufferToDisk();
1152a27164cbSJacob Lalonde     // Fixup the descriptors that were not read correctly.
1153a27164cbSJacob Lalonde     m_core_file->SeekFromStart(starting_offset);
1154a27164cbSJacob Lalonde     size_t bytes_written = sizeof(MemoryDescriptor_64) * descriptors.size();
1155a27164cbSJacob Lalonde     error = m_core_file->Write(descriptors.data(), bytes_written);
1156a27164cbSJacob Lalonde     if (error.Fail() ||
1157a27164cbSJacob Lalonde         bytes_written != sizeof(MemoryDescriptor_64) * descriptors.size()) {
11580642cd76SAdrian Prantl       error = Status::FromErrorStringWithFormat(
1159a27164cbSJacob Lalonde           "unable to write the memory descriptors (written %zd/%zd)",
1160a27164cbSJacob Lalonde           bytes_written, sizeof(MemoryDescriptor_64) * descriptors.size());
1161a27164cbSJacob Lalonde     }
1162a27164cbSJacob Lalonde 
1163a27164cbSJacob Lalonde     return error;
1164a27164cbSJacob Lalonde   }
1165a27164cbSJacob Lalonde }
1166a27164cbSJacob Lalonde 
1167a27164cbSJacob Lalonde Status MinidumpFileBuilder::AddData(const void *data, uint64_t size) {
1168a27164cbSJacob Lalonde   // This should also get chunked, because worst case we copy over a big
1169a27164cbSJacob Lalonde   // object / memory range, say 5gb. In that case, we'd have to allocate 10gb
1170a27164cbSJacob Lalonde   // 5 gb for the buffer we're copying from, and then 5gb for the buffer we're
1171a27164cbSJacob Lalonde   // copying to. Which will be short lived and immedaitely go to disk, the goal
1172a27164cbSJacob Lalonde   // here is to limit the number of bytes we need to host in memory at any given
1173a27164cbSJacob Lalonde   // time.
1174a27164cbSJacob Lalonde   m_data.AppendData(data, size);
1175a27164cbSJacob Lalonde   if (m_data.GetByteSize() > MAX_WRITE_CHUNK_SIZE)
1176a27164cbSJacob Lalonde     return FlushBufferToDisk();
1177a27164cbSJacob Lalonde 
1178a27164cbSJacob Lalonde   return Status();
1179a27164cbSJacob Lalonde }
1180a27164cbSJacob Lalonde 
1181a27164cbSJacob Lalonde Status MinidumpFileBuilder::FlushBufferToDisk() {
1182a27164cbSJacob Lalonde   Status error;
1183a27164cbSJacob Lalonde   // Set the stream to it's end.
1184a27164cbSJacob Lalonde   m_core_file->SeekFromStart(m_saved_data_size);
1185a27164cbSJacob Lalonde   addr_t starting_size = m_data.GetByteSize();
1186a27164cbSJacob Lalonde   addr_t remaining_bytes = starting_size;
1187a27164cbSJacob Lalonde   offset_t offset = 0;
1188a27164cbSJacob Lalonde 
1189a27164cbSJacob Lalonde   while (remaining_bytes > 0) {
1190a27164cbSJacob Lalonde     size_t bytes_written = remaining_bytes;
1191a27164cbSJacob Lalonde     // We don't care how many bytes we wrote unless we got an error
1192a27164cbSJacob Lalonde     // so just decrement the remaining bytes.
1193a27164cbSJacob Lalonde     error = m_core_file->Write(m_data.GetBytes() + offset, bytes_written);
1194a27164cbSJacob Lalonde     if (error.Fail()) {
11950642cd76SAdrian Prantl       error = Status::FromErrorStringWithFormat(
1196a27164cbSJacob Lalonde           "Wrote incorrect number of bytes to minidump file. (written %" PRIx64
1197a27164cbSJacob Lalonde           "/%" PRIx64 ")",
1198a27164cbSJacob Lalonde           starting_size - remaining_bytes, starting_size);
1199a27164cbSJacob Lalonde       return error;
1200a27164cbSJacob Lalonde     }
1201a27164cbSJacob Lalonde 
1202a27164cbSJacob Lalonde     offset += bytes_written;
1203a27164cbSJacob Lalonde     remaining_bytes -= bytes_written;
1204a27164cbSJacob Lalonde   }
1205a27164cbSJacob Lalonde 
1206a27164cbSJacob Lalonde   m_saved_data_size += starting_size;
1207a27164cbSJacob Lalonde   m_data.Clear();
1208a27164cbSJacob Lalonde   return error;
1209a27164cbSJacob Lalonde }
1210a27164cbSJacob Lalonde 
1211a27164cbSJacob Lalonde Status MinidumpFileBuilder::DumpFile() {
1212a27164cbSJacob Lalonde   Status error;
1213a27164cbSJacob Lalonde   // If anything is left unsaved, dump it.
1214a27164cbSJacob Lalonde   error = FlushBufferToDisk();
1215a27164cbSJacob Lalonde   if (error.Fail())
1216a27164cbSJacob Lalonde     return error;
1217a27164cbSJacob Lalonde 
1218a27164cbSJacob Lalonde   // Overwrite the header which we filled in earlier.
1219a27164cbSJacob Lalonde   error = DumpHeader();
1220a27164cbSJacob Lalonde   if (error.Fail())
1221a27164cbSJacob Lalonde     return error;
1222a27164cbSJacob Lalonde 
1223a27164cbSJacob Lalonde   // Overwrite the space saved for directories
1224a27164cbSJacob Lalonde   error = DumpDirectories();
1225a27164cbSJacob Lalonde   if (error.Fail())
1226a27164cbSJacob Lalonde     return error;
1227a27164cbSJacob Lalonde 
1228a27164cbSJacob Lalonde   return error;
1229eee687a6SAndrej Korman }
1230661382f2SJacob Lalonde 
1231661382f2SJacob Lalonde void MinidumpFileBuilder::DeleteFile() noexcept {
1232661382f2SJacob Lalonde   Log *log = GetLog(LLDBLog::Object);
1233661382f2SJacob Lalonde 
1234661382f2SJacob Lalonde   if (m_core_file) {
1235661382f2SJacob Lalonde     Status error = m_core_file->Close();
1236661382f2SJacob Lalonde     if (error.Fail())
1237661382f2SJacob Lalonde       LLDB_LOGF(log, "Failed to close minidump file: %s", error.AsCString());
1238661382f2SJacob Lalonde 
1239661382f2SJacob Lalonde     m_core_file.reset();
1240661382f2SJacob Lalonde   }
1241661382f2SJacob Lalonde }
1242