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 
11eee687a6SAndrej Korman #include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h"
12eee687a6SAndrej Korman 
13eee687a6SAndrej Korman #include "lldb/Core/Module.h"
14eee687a6SAndrej Korman #include "lldb/Core/ModuleList.h"
15eee687a6SAndrej Korman #include "lldb/Core/Section.h"
16eee687a6SAndrej Korman #include "lldb/Target/MemoryRegionInfo.h"
17eee687a6SAndrej Korman #include "lldb/Target/Process.h"
18eee687a6SAndrej Korman #include "lldb/Target/RegisterContext.h"
19eee687a6SAndrej Korman #include "lldb/Target/StopInfo.h"
20eee687a6SAndrej Korman #include "lldb/Target/ThreadList.h"
21eee687a6SAndrej Korman #include "lldb/Utility/DataExtractor.h"
22eee687a6SAndrej Korman #include "lldb/Utility/RegisterValue.h"
23eee687a6SAndrej Korman 
24eee687a6SAndrej Korman #include "llvm/ADT/StringRef.h"
25eee687a6SAndrej Korman #include "llvm/BinaryFormat/Minidump.h"
26eee687a6SAndrej Korman #include "llvm/Support/ConvertUTF.h"
27eee687a6SAndrej Korman #include "llvm/Support/Error.h"
28eee687a6SAndrej Korman 
29eee687a6SAndrej Korman #include "Plugins/Process/minidump/MinidumpTypes.h"
30eee687a6SAndrej Korman 
31e69d3598SFangrui Song #include <cinttypes>
32e69d3598SFangrui Song 
33eee687a6SAndrej Korman using namespace lldb;
34eee687a6SAndrej Korman using namespace lldb_private;
35eee687a6SAndrej Korman using namespace llvm::minidump;
36eee687a6SAndrej Korman 
AddDirectory(StreamType type,size_t stream_size)37eee687a6SAndrej Korman void MinidumpFileBuilder::AddDirectory(StreamType type, size_t stream_size) {
38eee687a6SAndrej Korman   LocationDescriptor loc;
39eee687a6SAndrej Korman   loc.DataSize = static_cast<llvm::support::ulittle32_t>(stream_size);
40eee687a6SAndrej Korman   // Stream will begin at the current end of data section
41eee687a6SAndrej Korman   loc.RVA = static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset());
42eee687a6SAndrej Korman 
43eee687a6SAndrej Korman   Directory dir;
44eee687a6SAndrej Korman   dir.Type = static_cast<llvm::support::little_t<StreamType>>(type);
45eee687a6SAndrej Korman   dir.Location = loc;
46eee687a6SAndrej Korman 
47eee687a6SAndrej Korman   m_directories.push_back(dir);
48eee687a6SAndrej Korman }
49eee687a6SAndrej Korman 
AddSystemInfo(const llvm::Triple & target_triple)50eee687a6SAndrej Korman Status MinidumpFileBuilder::AddSystemInfo(const llvm::Triple &target_triple) {
51eee687a6SAndrej Korman   Status error;
52eee687a6SAndrej Korman   AddDirectory(StreamType::SystemInfo, sizeof(llvm::minidump::SystemInfo));
53eee687a6SAndrej Korman 
54eee687a6SAndrej Korman   llvm::minidump::ProcessorArchitecture arch;
55eee687a6SAndrej Korman   switch (target_triple.getArch()) {
56eee687a6SAndrej Korman   case llvm::Triple::ArchType::x86_64:
57eee687a6SAndrej Korman     arch = ProcessorArchitecture::AMD64;
58eee687a6SAndrej Korman     break;
59eee687a6SAndrej Korman   case llvm::Triple::ArchType::x86:
60eee687a6SAndrej Korman     arch = ProcessorArchitecture::X86;
61eee687a6SAndrej Korman     break;
62eee687a6SAndrej Korman   case llvm::Triple::ArchType::arm:
63eee687a6SAndrej Korman     arch = ProcessorArchitecture::ARM;
64eee687a6SAndrej Korman     break;
65eee687a6SAndrej Korman   case llvm::Triple::ArchType::aarch64:
66eee687a6SAndrej Korman     arch = ProcessorArchitecture::ARM64;
67eee687a6SAndrej Korman     break;
68eee687a6SAndrej Korman   case llvm::Triple::ArchType::mips64:
69eee687a6SAndrej Korman   case llvm::Triple::ArchType::mips64el:
70eee687a6SAndrej Korman   case llvm::Triple::ArchType::mips:
71eee687a6SAndrej Korman   case llvm::Triple::ArchType::mipsel:
72eee687a6SAndrej Korman     arch = ProcessorArchitecture::MIPS;
73eee687a6SAndrej Korman     break;
74eee687a6SAndrej Korman   case llvm::Triple::ArchType::ppc64:
75eee687a6SAndrej Korman   case llvm::Triple::ArchType::ppc:
76eee687a6SAndrej Korman   case llvm::Triple::ArchType::ppc64le:
77eee687a6SAndrej Korman     arch = ProcessorArchitecture::PPC;
78eee687a6SAndrej Korman     break;
79eee687a6SAndrej Korman   default:
80eee687a6SAndrej Korman     error.SetErrorStringWithFormat("Architecture %s not supported.",
81eee687a6SAndrej Korman                                    target_triple.getArchName().str().c_str());
82eee687a6SAndrej Korman     return error;
83eee687a6SAndrej Korman   };
84eee687a6SAndrej Korman 
85eee687a6SAndrej Korman   llvm::support::little_t<OSPlatform> platform_id;
86eee687a6SAndrej Korman   switch (target_triple.getOS()) {
87eee687a6SAndrej Korman   case llvm::Triple::OSType::Linux:
88eee687a6SAndrej Korman     if (target_triple.getEnvironment() ==
89eee687a6SAndrej Korman         llvm::Triple::EnvironmentType::Android)
90eee687a6SAndrej Korman       platform_id = OSPlatform::Android;
91eee687a6SAndrej Korman     else
92eee687a6SAndrej Korman       platform_id = OSPlatform::Linux;
93eee687a6SAndrej Korman     break;
94eee687a6SAndrej Korman   case llvm::Triple::OSType::Win32:
95eee687a6SAndrej Korman     platform_id = OSPlatform::Win32NT;
96eee687a6SAndrej Korman     break;
97eee687a6SAndrej Korman   case llvm::Triple::OSType::MacOSX:
98eee687a6SAndrej Korman     platform_id = OSPlatform::MacOSX;
99eee687a6SAndrej Korman     break;
100eee687a6SAndrej Korman   case llvm::Triple::OSType::IOS:
101eee687a6SAndrej Korman     platform_id = OSPlatform::IOS;
102eee687a6SAndrej Korman     break;
103eee687a6SAndrej Korman   default:
104eee687a6SAndrej Korman     error.SetErrorStringWithFormat("OS %s not supported.",
105eee687a6SAndrej Korman                                    target_triple.getOSName().str().c_str());
106eee687a6SAndrej Korman     return error;
107eee687a6SAndrej Korman   };
108eee687a6SAndrej Korman 
109eee687a6SAndrej Korman   llvm::minidump::SystemInfo sys_info;
110eee687a6SAndrej Korman   sys_info.ProcessorArch =
111eee687a6SAndrej Korman       static_cast<llvm::support::little_t<ProcessorArchitecture>>(arch);
112eee687a6SAndrej Korman   // Global offset to beginning of a csd_string in a data section
113eee687a6SAndrej Korman   sys_info.CSDVersionRVA = static_cast<llvm::support::ulittle32_t>(
114eee687a6SAndrej Korman       GetCurrentDataEndOffset() + sizeof(llvm::minidump::SystemInfo));
115eee687a6SAndrej Korman   sys_info.PlatformId = platform_id;
116eee687a6SAndrej Korman   m_data.AppendData(&sys_info, sizeof(llvm::minidump::SystemInfo));
117eee687a6SAndrej Korman 
1180542d152SKazu Hirata   std::string csd_string;
119eee687a6SAndrej Korman 
120eee687a6SAndrej Korman   error = WriteString(csd_string, &m_data);
121eee687a6SAndrej Korman   if (error.Fail()) {
122eee687a6SAndrej Korman     error.SetErrorString("Unable to convert the csd string to UTF16.");
123eee687a6SAndrej Korman     return error;
124eee687a6SAndrej Korman   }
125eee687a6SAndrej Korman 
126eee687a6SAndrej Korman   return error;
127eee687a6SAndrej Korman }
128eee687a6SAndrej Korman 
WriteString(const std::string & to_write,lldb_private::DataBufferHeap * buffer)129eee687a6SAndrej Korman Status WriteString(const std::string &to_write,
130eee687a6SAndrej Korman                    lldb_private::DataBufferHeap *buffer) {
131eee687a6SAndrej Korman   Status error;
132eee687a6SAndrej Korman   // let the StringRef eat also null termination char
133eee687a6SAndrej Korman   llvm::StringRef to_write_ref(to_write.c_str(), to_write.size() + 1);
134eee687a6SAndrej Korman   llvm::SmallVector<llvm::UTF16, 128> to_write_utf16;
135eee687a6SAndrej Korman 
136eee687a6SAndrej Korman   bool converted = convertUTF8ToUTF16String(to_write_ref, to_write_utf16);
137eee687a6SAndrej Korman   if (!converted) {
138eee687a6SAndrej Korman     error.SetErrorStringWithFormat(
139eee687a6SAndrej Korman         "Unable to convert the string to UTF16. Failed to convert %s",
140eee687a6SAndrej Korman         to_write.c_str());
141eee687a6SAndrej Korman     return error;
142eee687a6SAndrej Korman   }
143eee687a6SAndrej Korman 
144eee687a6SAndrej Korman   // size of the UTF16 string should be written without the null termination
145eee687a6SAndrej Korman   // character that is stored in 2 bytes
146eee687a6SAndrej Korman   llvm::support::ulittle32_t to_write_size(to_write_utf16.size_in_bytes() - 2);
147eee687a6SAndrej Korman 
148eee687a6SAndrej Korman   buffer->AppendData(&to_write_size, sizeof(llvm::support::ulittle32_t));
149eee687a6SAndrej Korman   buffer->AppendData(to_write_utf16.data(), to_write_utf16.size_in_bytes());
150eee687a6SAndrej Korman 
151eee687a6SAndrej Korman   return error;
152eee687a6SAndrej Korman }
153eee687a6SAndrej Korman 
getModuleFileSize(Target & target,const ModuleSP & mod)154eee687a6SAndrej Korman llvm::Expected<uint64_t> getModuleFileSize(Target &target,
155eee687a6SAndrej Korman                                            const ModuleSP &mod) {
156eee687a6SAndrej Korman   SectionSP sect_sp = mod->GetObjectFile()->GetBaseAddress().GetSection();
157eee687a6SAndrej Korman   uint64_t SizeOfImage = 0;
158eee687a6SAndrej Korman 
159eee687a6SAndrej Korman   if (!sect_sp) {
160eee687a6SAndrej Korman     return llvm::createStringError(std::errc::operation_not_supported,
161eee687a6SAndrej Korman                                    "Couldn't obtain the section information.");
162eee687a6SAndrej Korman   }
163eee687a6SAndrej Korman   lldb::addr_t sect_addr = sect_sp->GetLoadBaseAddress(&target);
164eee687a6SAndrej Korman   // Use memory size since zero fill sections, like ".bss", will be smaller on
165eee687a6SAndrej Korman   // disk.
166eee687a6SAndrej Korman   lldb::addr_t sect_size = sect_sp->GetByteSize();
167eee687a6SAndrej Korman   // This will usually be zero, but make sure to calculate the BaseOfImage
168eee687a6SAndrej Korman   // offset.
169eee687a6SAndrej Korman   const lldb::addr_t base_sect_offset =
170eee687a6SAndrej Korman       mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target) -
171eee687a6SAndrej Korman       sect_addr;
172eee687a6SAndrej Korman   SizeOfImage = sect_size - base_sect_offset;
173eee687a6SAndrej Korman   lldb::addr_t next_sect_addr = sect_addr + sect_size;
174eee687a6SAndrej Korman   Address sect_so_addr;
175eee687a6SAndrej Korman   target.ResolveLoadAddress(next_sect_addr, sect_so_addr);
176eee687a6SAndrej Korman   lldb::SectionSP next_sect_sp = sect_so_addr.GetSection();
177eee687a6SAndrej Korman   while (next_sect_sp &&
178eee687a6SAndrej Korman          next_sect_sp->GetLoadBaseAddress(&target) == next_sect_addr) {
179eee687a6SAndrej Korman     sect_size = sect_sp->GetByteSize();
180eee687a6SAndrej Korman     SizeOfImage += sect_size;
181eee687a6SAndrej Korman     next_sect_addr += sect_size;
182eee687a6SAndrej Korman     target.ResolveLoadAddress(next_sect_addr, sect_so_addr);
183eee687a6SAndrej Korman     next_sect_sp = sect_so_addr.GetSection();
184eee687a6SAndrej Korman   }
185eee687a6SAndrej Korman 
186eee687a6SAndrej Korman   return SizeOfImage;
187eee687a6SAndrej Korman }
188eee687a6SAndrej Korman 
189eee687a6SAndrej Korman // ModuleList stream consists of a number of modules, followed by an array
190eee687a6SAndrej Korman // of llvm::minidump::Module's structures. Every structure informs about a
191eee687a6SAndrej Korman // single module. Additional data of variable length, such as module's names,
192eee687a6SAndrej Korman // are stored just after the ModuleList stream. The llvm::minidump::Module
193eee687a6SAndrej Korman // structures point to this helper data by global offset.
AddModuleList(Target & target)194eee687a6SAndrej Korman Status MinidumpFileBuilder::AddModuleList(Target &target) {
195eee687a6SAndrej Korman   constexpr size_t minidump_module_size = sizeof(llvm::minidump::Module);
196eee687a6SAndrej Korman   Status error;
197eee687a6SAndrej Korman 
198eee687a6SAndrej Korman   const ModuleList &modules = target.GetImages();
199eee687a6SAndrej Korman   llvm::support::ulittle32_t modules_count =
200eee687a6SAndrej Korman       static_cast<llvm::support::ulittle32_t>(modules.GetSize());
201eee687a6SAndrej Korman 
202eee687a6SAndrej Korman   // This helps us with getting the correct global offset in minidump
203eee687a6SAndrej Korman   // file later, when we will be setting up offsets from the
204eee687a6SAndrej Korman   // the llvm::minidump::Module's structures into helper data
205eee687a6SAndrej Korman   size_t size_before = GetCurrentDataEndOffset();
206eee687a6SAndrej Korman 
207eee687a6SAndrej Korman   // This is the size of the main part of the ModuleList stream.
208eee687a6SAndrej Korman   // It consists of a module number and corresponding number of
209eee687a6SAndrej Korman   // structs describing individual modules
210eee687a6SAndrej Korman   size_t module_stream_size =
211eee687a6SAndrej Korman       sizeof(llvm::support::ulittle32_t) + modules_count * minidump_module_size;
212eee687a6SAndrej Korman 
213eee687a6SAndrej Korman   // Adding directory describing this stream.
214eee687a6SAndrej Korman   AddDirectory(StreamType::ModuleList, module_stream_size);
215eee687a6SAndrej Korman 
216eee687a6SAndrej Korman   m_data.AppendData(&modules_count, sizeof(llvm::support::ulittle32_t));
217eee687a6SAndrej Korman 
218eee687a6SAndrej Korman   // Temporary storage for the helper data (of variable length)
219eee687a6SAndrej Korman   // as these cannot be dumped to m_data before dumping entire
220eee687a6SAndrej Korman   // array of module structures.
221eee687a6SAndrej Korman   DataBufferHeap helper_data;
222eee687a6SAndrej Korman 
223eee687a6SAndrej Korman   for (size_t i = 0; i < modules_count; ++i) {
224eee687a6SAndrej Korman     ModuleSP mod = modules.GetModuleAtIndex(i);
225eee687a6SAndrej Korman     std::string module_name = mod->GetSpecificationDescription();
226eee687a6SAndrej Korman     auto maybe_mod_size = getModuleFileSize(target, mod);
227eee687a6SAndrej Korman     if (!maybe_mod_size) {
228eee687a6SAndrej Korman       error.SetErrorStringWithFormat("Unable to get the size of module %s.",
229eee687a6SAndrej Korman                                      module_name.c_str());
230eee687a6SAndrej Korman       return error;
231eee687a6SAndrej Korman     }
232eee687a6SAndrej Korman 
233eee687a6SAndrej Korman     uint64_t mod_size = std::move(*maybe_mod_size);
234eee687a6SAndrej Korman 
235eee687a6SAndrej Korman     llvm::support::ulittle32_t signature =
236eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(
237eee687a6SAndrej Korman             static_cast<uint32_t>(minidump::CvSignature::ElfBuildId));
238eee687a6SAndrej Korman     auto uuid = mod->GetUUID().GetBytes();
239eee687a6SAndrej Korman 
240eee687a6SAndrej Korman     VSFixedFileInfo info;
241eee687a6SAndrej Korman     info.Signature = static_cast<llvm::support::ulittle32_t>(0u);
242eee687a6SAndrej Korman     info.StructVersion = static_cast<llvm::support::ulittle32_t>(0u);
243eee687a6SAndrej Korman     info.FileVersionHigh = static_cast<llvm::support::ulittle32_t>(0u);
244eee687a6SAndrej Korman     info.FileVersionLow = static_cast<llvm::support::ulittle32_t>(0u);
245eee687a6SAndrej Korman     info.ProductVersionHigh = static_cast<llvm::support::ulittle32_t>(0u);
246eee687a6SAndrej Korman     info.ProductVersionLow = static_cast<llvm::support::ulittle32_t>(0u);
247eee687a6SAndrej Korman     info.FileFlagsMask = static_cast<llvm::support::ulittle32_t>(0u);
248eee687a6SAndrej Korman     info.FileFlags = static_cast<llvm::support::ulittle32_t>(0u);
249eee687a6SAndrej Korman     info.FileOS = static_cast<llvm::support::ulittle32_t>(0u);
250eee687a6SAndrej Korman     info.FileType = static_cast<llvm::support::ulittle32_t>(0u);
251eee687a6SAndrej Korman     info.FileSubtype = static_cast<llvm::support::ulittle32_t>(0u);
252eee687a6SAndrej Korman     info.FileDateHigh = static_cast<llvm::support::ulittle32_t>(0u);
253eee687a6SAndrej Korman     info.FileDateLow = static_cast<llvm::support::ulittle32_t>(0u);
254eee687a6SAndrej Korman 
255eee687a6SAndrej Korman     LocationDescriptor ld;
256eee687a6SAndrej Korman     ld.DataSize = static_cast<llvm::support::ulittle32_t>(0u);
257eee687a6SAndrej Korman     ld.RVA = static_cast<llvm::support::ulittle32_t>(0u);
258eee687a6SAndrej Korman 
259eee687a6SAndrej Korman     // Setting up LocationDescriptor for uuid string. The global offset into
260eee687a6SAndrej Korman     // minidump file is calculated.
261eee687a6SAndrej Korman     LocationDescriptor ld_cv;
262eee687a6SAndrej Korman     ld_cv.DataSize = static_cast<llvm::support::ulittle32_t>(
263eee687a6SAndrej Korman         sizeof(llvm::support::ulittle32_t) + uuid.size());
264eee687a6SAndrej Korman     ld_cv.RVA = static_cast<llvm::support::ulittle32_t>(
265eee687a6SAndrej Korman         size_before + module_stream_size + helper_data.GetByteSize());
266eee687a6SAndrej Korman 
267eee687a6SAndrej Korman     helper_data.AppendData(&signature, sizeof(llvm::support::ulittle32_t));
268eee687a6SAndrej Korman     helper_data.AppendData(uuid.begin(), uuid.size());
269eee687a6SAndrej Korman 
270eee687a6SAndrej Korman     llvm::minidump::Module m;
271eee687a6SAndrej Korman     m.BaseOfImage = static_cast<llvm::support::ulittle64_t>(
272eee687a6SAndrej Korman         mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target));
273eee687a6SAndrej Korman     m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(mod_size);
274eee687a6SAndrej Korman     m.Checksum = static_cast<llvm::support::ulittle32_t>(0);
275b8336280SKazu Hirata     m.TimeDateStamp =
276b8336280SKazu Hirata         static_cast<llvm::support::ulittle32_t>(std::time(nullptr));
277eee687a6SAndrej Korman     m.ModuleNameRVA = static_cast<llvm::support::ulittle32_t>(
278eee687a6SAndrej Korman         size_before + module_stream_size + helper_data.GetByteSize());
279eee687a6SAndrej Korman     m.VersionInfo = info;
280eee687a6SAndrej Korman     m.CvRecord = ld_cv;
281eee687a6SAndrej Korman     m.MiscRecord = ld;
282eee687a6SAndrej Korman 
283eee687a6SAndrej Korman     error = WriteString(module_name, &helper_data);
284eee687a6SAndrej Korman 
285eee687a6SAndrej Korman     if (error.Fail())
286eee687a6SAndrej Korman       return error;
287eee687a6SAndrej Korman 
288eee687a6SAndrej Korman     m_data.AppendData(&m, sizeof(llvm::minidump::Module));
289eee687a6SAndrej Korman   }
290eee687a6SAndrej Korman 
291eee687a6SAndrej Korman   m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
292eee687a6SAndrej Korman   return error;
293eee687a6SAndrej Korman }
294eee687a6SAndrej Korman 
read_register_u16_raw(RegisterContext * reg_ctx,const std::string & reg_name)295eee687a6SAndrej Korman uint16_t read_register_u16_raw(RegisterContext *reg_ctx,
296eee687a6SAndrej Korman                                const std::string &reg_name) {
297eee687a6SAndrej Korman   const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
298eee687a6SAndrej Korman   if (!reg_info)
299eee687a6SAndrej Korman     return 0;
300eee687a6SAndrej Korman   lldb_private::RegisterValue reg_value;
301eee687a6SAndrej Korman   bool success = reg_ctx->ReadRegister(reg_info, reg_value);
302eee687a6SAndrej Korman   if (!success)
303eee687a6SAndrej Korman     return 0;
304eee687a6SAndrej Korman   return reg_value.GetAsUInt16();
305eee687a6SAndrej Korman }
306eee687a6SAndrej Korman 
read_register_u32_raw(RegisterContext * reg_ctx,const std::string & reg_name)307eee687a6SAndrej Korman uint32_t read_register_u32_raw(RegisterContext *reg_ctx,
308eee687a6SAndrej Korman                                const std::string &reg_name) {
309eee687a6SAndrej Korman   const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
310eee687a6SAndrej Korman   if (!reg_info)
311eee687a6SAndrej Korman     return 0;
312eee687a6SAndrej Korman   lldb_private::RegisterValue reg_value;
313eee687a6SAndrej Korman   bool success = reg_ctx->ReadRegister(reg_info, reg_value);
314eee687a6SAndrej Korman   if (!success)
315eee687a6SAndrej Korman     return 0;
316eee687a6SAndrej Korman   return reg_value.GetAsUInt32();
317eee687a6SAndrej Korman }
318eee687a6SAndrej Korman 
read_register_u64_raw(RegisterContext * reg_ctx,const std::string & reg_name)319eee687a6SAndrej Korman uint64_t read_register_u64_raw(RegisterContext *reg_ctx,
320eee687a6SAndrej Korman                                const std::string &reg_name) {
321eee687a6SAndrej Korman   const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
322eee687a6SAndrej Korman   if (!reg_info)
323eee687a6SAndrej Korman     return 0;
324eee687a6SAndrej Korman   lldb_private::RegisterValue reg_value;
325eee687a6SAndrej Korman   bool success = reg_ctx->ReadRegister(reg_info, reg_value);
326eee687a6SAndrej Korman   if (!success)
327eee687a6SAndrej Korman     return 0;
328eee687a6SAndrej Korman   return reg_value.GetAsUInt64();
329eee687a6SAndrej Korman }
330eee687a6SAndrej Korman 
read_register_u16(RegisterContext * reg_ctx,const std::string & reg_name)331eee687a6SAndrej Korman llvm::support::ulittle16_t read_register_u16(RegisterContext *reg_ctx,
332eee687a6SAndrej Korman                                              const std::string &reg_name) {
333eee687a6SAndrej Korman   return static_cast<llvm::support::ulittle16_t>(
334eee687a6SAndrej Korman       read_register_u16_raw(reg_ctx, reg_name));
335eee687a6SAndrej Korman }
336eee687a6SAndrej Korman 
read_register_u32(RegisterContext * reg_ctx,const std::string & reg_name)337eee687a6SAndrej Korman llvm::support::ulittle32_t read_register_u32(RegisterContext *reg_ctx,
338eee687a6SAndrej Korman                                              const std::string &reg_name) {
339eee687a6SAndrej Korman   return static_cast<llvm::support::ulittle32_t>(
340eee687a6SAndrej Korman       read_register_u32_raw(reg_ctx, reg_name));
341eee687a6SAndrej Korman }
342eee687a6SAndrej Korman 
read_register_u64(RegisterContext * reg_ctx,const std::string & reg_name)343eee687a6SAndrej Korman llvm::support::ulittle64_t read_register_u64(RegisterContext *reg_ctx,
344eee687a6SAndrej Korman                                              const std::string &reg_name) {
345eee687a6SAndrej Korman   return static_cast<llvm::support::ulittle64_t>(
346eee687a6SAndrej Korman       read_register_u64_raw(reg_ctx, reg_name));
347eee687a6SAndrej Korman }
348eee687a6SAndrej Korman 
349eee687a6SAndrej Korman lldb_private::minidump::MinidumpContext_x86_64
GetThreadContext_64(RegisterContext * reg_ctx)350eee687a6SAndrej Korman GetThreadContext_64(RegisterContext *reg_ctx) {
351*4871dfc6SSlava Gurevich   lldb_private::minidump::MinidumpContext_x86_64 thread_context = {};
352459cfa5eSSlava Gurevich   thread_context.p1_home = {};
353eee687a6SAndrej Korman   thread_context.context_flags = static_cast<uint32_t>(
354eee687a6SAndrej Korman       lldb_private::minidump::MinidumpContext_x86_64_Flags::x86_64_Flag |
355eee687a6SAndrej Korman       lldb_private::minidump::MinidumpContext_x86_64_Flags::Control |
356eee687a6SAndrej Korman       lldb_private::minidump::MinidumpContext_x86_64_Flags::Segments |
357eee687a6SAndrej Korman       lldb_private::minidump::MinidumpContext_x86_64_Flags::Integer);
358eee687a6SAndrej Korman   thread_context.rax = read_register_u64(reg_ctx, "rax");
359eee687a6SAndrej Korman   thread_context.rbx = read_register_u64(reg_ctx, "rbx");
360eee687a6SAndrej Korman   thread_context.rcx = read_register_u64(reg_ctx, "rcx");
361eee687a6SAndrej Korman   thread_context.rdx = read_register_u64(reg_ctx, "rdx");
362eee687a6SAndrej Korman   thread_context.rdi = read_register_u64(reg_ctx, "rdi");
363eee687a6SAndrej Korman   thread_context.rsi = read_register_u64(reg_ctx, "rsi");
364eee687a6SAndrej Korman   thread_context.rbp = read_register_u64(reg_ctx, "rbp");
365eee687a6SAndrej Korman   thread_context.rsp = read_register_u64(reg_ctx, "rsp");
366eee687a6SAndrej Korman   thread_context.r8 = read_register_u64(reg_ctx, "r8");
367eee687a6SAndrej Korman   thread_context.r9 = read_register_u64(reg_ctx, "r9");
368eee687a6SAndrej Korman   thread_context.r10 = read_register_u64(reg_ctx, "r10");
369eee687a6SAndrej Korman   thread_context.r11 = read_register_u64(reg_ctx, "r11");
370eee687a6SAndrej Korman   thread_context.r12 = read_register_u64(reg_ctx, "r12");
371eee687a6SAndrej Korman   thread_context.r13 = read_register_u64(reg_ctx, "r13");
372eee687a6SAndrej Korman   thread_context.r14 = read_register_u64(reg_ctx, "r14");
373eee687a6SAndrej Korman   thread_context.r15 = read_register_u64(reg_ctx, "r15");
374eee687a6SAndrej Korman   thread_context.rip = read_register_u64(reg_ctx, "rip");
375eee687a6SAndrej Korman   thread_context.eflags = read_register_u32(reg_ctx, "rflags");
376eee687a6SAndrej Korman   thread_context.cs = read_register_u16(reg_ctx, "cs");
377eee687a6SAndrej Korman   thread_context.fs = read_register_u16(reg_ctx, "fs");
378eee687a6SAndrej Korman   thread_context.gs = read_register_u16(reg_ctx, "gs");
379eee687a6SAndrej Korman   thread_context.ss = read_register_u16(reg_ctx, "ss");
380eee687a6SAndrej Korman   thread_context.ds = read_register_u16(reg_ctx, "ds");
381eee687a6SAndrej Korman   return thread_context;
382eee687a6SAndrej Korman }
383eee687a6SAndrej Korman 
384eee687a6SAndrej Korman // Function returns start and size of the memory region that contains
385eee687a6SAndrej Korman // memory location pointed to by the current stack pointer.
386eee687a6SAndrej Korman llvm::Expected<std::pair<addr_t, addr_t>>
findStackHelper(const lldb::ProcessSP & process_sp,uint64_t rsp)387eee687a6SAndrej Korman findStackHelper(const lldb::ProcessSP &process_sp, uint64_t rsp) {
388eee687a6SAndrej Korman   MemoryRegionInfo range_info;
389eee687a6SAndrej Korman   Status error = process_sp->GetMemoryRegionInfo(rsp, range_info);
390eee687a6SAndrej Korman   // Skip failed memory region requests or any regions with no permissions.
391eee687a6SAndrej Korman   if (error.Fail() || range_info.GetLLDBPermissions() == 0)
392eee687a6SAndrej Korman     return llvm::createStringError(
393eee687a6SAndrej Korman         std::errc::not_supported,
394eee687a6SAndrej Korman         "unable to load stack segment of the process");
395eee687a6SAndrej Korman 
396eee687a6SAndrej Korman   const addr_t addr = range_info.GetRange().GetRangeBase();
397eee687a6SAndrej Korman   const addr_t size = range_info.GetRange().GetByteSize();
398eee687a6SAndrej Korman 
399eee687a6SAndrej Korman   if (size == 0)
400eee687a6SAndrej Korman     return llvm::createStringError(std::errc::not_supported,
401eee687a6SAndrej Korman                                    "stack segment of the process is empty");
402eee687a6SAndrej Korman 
403eee687a6SAndrej Korman   return std::make_pair(addr, size);
404eee687a6SAndrej Korman }
405eee687a6SAndrej Korman 
AddThreadList(const lldb::ProcessSP & process_sp)406eee687a6SAndrej Korman Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) {
407eee687a6SAndrej Korman   constexpr size_t minidump_thread_size = sizeof(llvm::minidump::Thread);
408eee687a6SAndrej Korman   lldb_private::ThreadList thread_list = process_sp->GetThreadList();
409eee687a6SAndrej Korman 
410eee687a6SAndrej Korman   // size of the entire thread stream consists of:
411eee687a6SAndrej Korman   // number of threads and threads array
412eee687a6SAndrej Korman   size_t thread_stream_size = sizeof(llvm::support::ulittle32_t) +
413eee687a6SAndrej Korman                               thread_list.GetSize() * minidump_thread_size;
414eee687a6SAndrej Korman   // save for the ability to set up RVA
415eee687a6SAndrej Korman   size_t size_before = GetCurrentDataEndOffset();
416eee687a6SAndrej Korman 
417eee687a6SAndrej Korman   AddDirectory(StreamType::ThreadList, thread_stream_size);
418eee687a6SAndrej Korman 
419eee687a6SAndrej Korman   llvm::support::ulittle32_t thread_count =
420eee687a6SAndrej Korman       static_cast<llvm::support::ulittle32_t>(thread_list.GetSize());
421eee687a6SAndrej Korman   m_data.AppendData(&thread_count, sizeof(llvm::support::ulittle32_t));
422eee687a6SAndrej Korman 
423eee687a6SAndrej Korman   DataBufferHeap helper_data;
424eee687a6SAndrej Korman 
425eee687a6SAndrej Korman   const uint32_t num_threads = thread_list.GetSize();
426eee687a6SAndrej Korman 
427eee687a6SAndrej Korman   for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
428eee687a6SAndrej Korman     ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
429eee687a6SAndrej Korman     RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
430eee687a6SAndrej Korman     Status error;
431eee687a6SAndrej Korman 
432eee687a6SAndrej Korman     if (!reg_ctx_sp) {
433eee687a6SAndrej Korman       error.SetErrorString("Unable to get the register context.");
434eee687a6SAndrej Korman       return error;
435eee687a6SAndrej Korman     }
436eee687a6SAndrej Korman     RegisterContext *reg_ctx = reg_ctx_sp.get();
437eee687a6SAndrej Korman     auto thread_context = GetThreadContext_64(reg_ctx);
438eee687a6SAndrej Korman     uint64_t rsp = read_register_u64_raw(reg_ctx, "rsp");
439eee687a6SAndrej Korman     auto expected_address_range = findStackHelper(process_sp, rsp);
440eee687a6SAndrej Korman 
441eee687a6SAndrej Korman     if (!expected_address_range) {
442eee687a6SAndrej Korman       error.SetErrorString("Unable to get the stack address.");
443eee687a6SAndrej Korman       return error;
444eee687a6SAndrej Korman     }
445eee687a6SAndrej Korman 
446eee687a6SAndrej Korman     std::pair<uint64_t, uint64_t> range = std::move(*expected_address_range);
447eee687a6SAndrej Korman     uint64_t addr = range.first;
448eee687a6SAndrej Korman     uint64_t size = range.second;
449eee687a6SAndrej Korman 
450eee687a6SAndrej Korman     auto data_up = std::make_unique<DataBufferHeap>(size, 0);
451eee687a6SAndrej Korman     const size_t stack_bytes_read =
452eee687a6SAndrej Korman         process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);
453eee687a6SAndrej Korman 
454eee687a6SAndrej Korman     if (error.Fail())
455eee687a6SAndrej Korman       return error;
456eee687a6SAndrej Korman 
457eee687a6SAndrej Korman     LocationDescriptor stack_memory;
458eee687a6SAndrej Korman     stack_memory.DataSize =
459eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(stack_bytes_read);
460eee687a6SAndrej Korman     stack_memory.RVA = static_cast<llvm::support::ulittle32_t>(
461eee687a6SAndrej Korman         size_before + thread_stream_size + helper_data.GetByteSize());
462eee687a6SAndrej Korman 
463eee687a6SAndrej Korman     MemoryDescriptor stack;
464eee687a6SAndrej Korman     stack.StartOfMemoryRange = static_cast<llvm::support::ulittle64_t>(addr);
465eee687a6SAndrej Korman     stack.Memory = stack_memory;
466eee687a6SAndrej Korman 
467eee687a6SAndrej Korman     helper_data.AppendData(data_up->GetBytes(), stack_bytes_read);
468eee687a6SAndrej Korman 
469eee687a6SAndrej Korman     LocationDescriptor thread_context_memory_locator;
470eee687a6SAndrej Korman     thread_context_memory_locator.DataSize =
471eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(sizeof(thread_context));
472eee687a6SAndrej Korman     thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
473eee687a6SAndrej Korman         size_before + thread_stream_size + helper_data.GetByteSize());
474eee687a6SAndrej Korman 
475eee687a6SAndrej Korman     helper_data.AppendData(
476eee687a6SAndrej Korman         &thread_context,
477eee687a6SAndrej Korman         sizeof(lldb_private::minidump::MinidumpContext_x86_64));
478eee687a6SAndrej Korman 
479eee687a6SAndrej Korman     llvm::minidump::Thread t;
480eee687a6SAndrej Korman     t.ThreadId = static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
481eee687a6SAndrej Korman     t.SuspendCount = static_cast<llvm::support::ulittle32_t>(
482eee687a6SAndrej Korman         (thread_sp->GetState() == StateType::eStateSuspended) ? 1 : 0);
483eee687a6SAndrej Korman     t.PriorityClass = static_cast<llvm::support::ulittle32_t>(0);
484eee687a6SAndrej Korman     t.Priority = static_cast<llvm::support::ulittle32_t>(0);
485eee687a6SAndrej Korman     t.EnvironmentBlock = static_cast<llvm::support::ulittle64_t>(0);
486eee687a6SAndrej Korman     t.Stack = stack, t.Context = thread_context_memory_locator;
487eee687a6SAndrej Korman 
488eee687a6SAndrej Korman     m_data.AppendData(&t, sizeof(llvm::minidump::Thread));
489eee687a6SAndrej Korman   }
490eee687a6SAndrej Korman 
491eee687a6SAndrej Korman   m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
492eee687a6SAndrej Korman   return Status();
493eee687a6SAndrej Korman }
494eee687a6SAndrej Korman 
AddException(const lldb::ProcessSP & process_sp)495eee687a6SAndrej Korman Status MinidumpFileBuilder::AddException(const lldb::ProcessSP &process_sp) {
496eee687a6SAndrej Korman   Status error;
497eee687a6SAndrej Korman   lldb_private::ThreadList thread_list = process_sp->GetThreadList();
498eee687a6SAndrej Korman 
499eee687a6SAndrej Korman   const uint32_t num_threads = thread_list.GetSize();
500eee687a6SAndrej Korman   uint32_t stop_reason_thread_idx = 0;
501eee687a6SAndrej Korman   for (stop_reason_thread_idx = 0; stop_reason_thread_idx < num_threads;
502eee687a6SAndrej Korman        ++stop_reason_thread_idx) {
503eee687a6SAndrej Korman     ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx));
504eee687a6SAndrej Korman     StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
505eee687a6SAndrej Korman 
506eee687a6SAndrej Korman     if (stop_info_sp && stop_info_sp->IsValid())
507eee687a6SAndrej Korman       break;
508eee687a6SAndrej Korman   }
509eee687a6SAndrej Korman 
510eee687a6SAndrej Korman   if (stop_reason_thread_idx == num_threads) {
511eee687a6SAndrej Korman     error.SetErrorString("No stop reason thread found.");
512eee687a6SAndrej Korman     return error;
513eee687a6SAndrej Korman   }
514eee687a6SAndrej Korman 
515eee687a6SAndrej Korman   constexpr size_t minidump_exception_size =
516eee687a6SAndrej Korman       sizeof(llvm::minidump::ExceptionStream);
517eee687a6SAndrej Korman   AddDirectory(StreamType::Exception, minidump_exception_size);
518eee687a6SAndrej Korman   size_t size_before = GetCurrentDataEndOffset();
519eee687a6SAndrej Korman 
520eee687a6SAndrej Korman   ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx));
521eee687a6SAndrej Korman   RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
522eee687a6SAndrej Korman   RegisterContext *reg_ctx = reg_ctx_sp.get();
523eee687a6SAndrej Korman   auto thread_context = GetThreadContext_64(reg_ctx);
524eee687a6SAndrej Korman   StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
525eee687a6SAndrej Korman 
526eee687a6SAndrej Korman   DataBufferHeap helper_data;
527eee687a6SAndrej Korman 
528eee687a6SAndrej Korman   LocationDescriptor thread_context_memory_locator;
529eee687a6SAndrej Korman   thread_context_memory_locator.DataSize =
530eee687a6SAndrej Korman       static_cast<llvm::support::ulittle32_t>(sizeof(thread_context));
531eee687a6SAndrej Korman   thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
532eee687a6SAndrej Korman       size_before + minidump_exception_size + helper_data.GetByteSize());
533eee687a6SAndrej Korman 
534eee687a6SAndrej Korman   helper_data.AppendData(
535eee687a6SAndrej Korman       &thread_context, sizeof(lldb_private::minidump::MinidumpContext_x86_64));
536eee687a6SAndrej Korman 
537*4871dfc6SSlava Gurevich   Exception exp_record = {};
538eee687a6SAndrej Korman   exp_record.ExceptionCode =
539eee687a6SAndrej Korman       static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());
540eee687a6SAndrej Korman   exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0);
541eee687a6SAndrej Korman   exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);
542eee687a6SAndrej Korman   exp_record.ExceptionAddress = read_register_u64(reg_ctx, "rip");
543eee687a6SAndrej Korman   exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0);
544eee687a6SAndrej Korman   exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
545eee687a6SAndrej Korman   // exp_record.ExceptionInformation;
546eee687a6SAndrej Korman 
547eee687a6SAndrej Korman   ExceptionStream exp_stream;
548eee687a6SAndrej Korman   exp_stream.ThreadId =
549eee687a6SAndrej Korman       static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
550eee687a6SAndrej Korman   exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
551eee687a6SAndrej Korman   exp_stream.ExceptionRecord = exp_record;
552eee687a6SAndrej Korman   exp_stream.ThreadContext = thread_context_memory_locator;
553eee687a6SAndrej Korman 
554eee687a6SAndrej Korman   m_data.AppendData(&exp_stream, minidump_exception_size);
555eee687a6SAndrej Korman   m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
556eee687a6SAndrej Korman   return error;
557eee687a6SAndrej Korman }
558eee687a6SAndrej Korman 
559eee687a6SAndrej Korman lldb_private::Status
AddMemoryList(const lldb::ProcessSP & process_sp)560eee687a6SAndrej Korman MinidumpFileBuilder::AddMemoryList(const lldb::ProcessSP &process_sp) {
561eee687a6SAndrej Korman   Status error;
562eee687a6SAndrej Korman 
563eee687a6SAndrej Korman   if (error.Fail()) {
564eee687a6SAndrej Korman     error.SetErrorString("Process doesn't support getting memory region info.");
565eee687a6SAndrej Korman     return error;
566eee687a6SAndrej Korman   }
567eee687a6SAndrej Korman 
568eee687a6SAndrej Korman   // Get interesting addresses
569eee687a6SAndrej Korman   std::vector<size_t> interesting_addresses;
570eee687a6SAndrej Korman   auto thread_list = process_sp->GetThreadList();
571eee687a6SAndrej Korman   for (size_t i = 0; i < thread_list.GetSize(); ++i) {
572eee687a6SAndrej Korman     ThreadSP thread_sp(thread_list.GetThreadAtIndex(i));
573eee687a6SAndrej Korman     RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
574eee687a6SAndrej Korman     RegisterContext *reg_ctx = reg_ctx_sp.get();
575eee687a6SAndrej Korman 
576eee687a6SAndrej Korman     interesting_addresses.push_back(read_register_u64(reg_ctx, "rsp"));
577eee687a6SAndrej Korman     interesting_addresses.push_back(read_register_u64(reg_ctx, "rip"));
578eee687a6SAndrej Korman   }
579eee687a6SAndrej Korman 
580eee687a6SAndrej Korman   DataBufferHeap helper_data;
581eee687a6SAndrej Korman   std::vector<MemoryDescriptor> mem_descriptors;
582eee687a6SAndrej Korman 
583eee687a6SAndrej Korman   std::set<addr_t> visited_region_base_addresses;
584eee687a6SAndrej Korman   for (size_t interesting_address : interesting_addresses) {
585eee687a6SAndrej Korman     MemoryRegionInfo range_info;
586eee687a6SAndrej Korman     error = process_sp->GetMemoryRegionInfo(interesting_address, range_info);
587eee687a6SAndrej Korman     // Skip failed memory region requests or any regions with no permissions.
588eee687a6SAndrej Korman     if (error.Fail() || range_info.GetLLDBPermissions() == 0)
589eee687a6SAndrej Korman       continue;
590eee687a6SAndrej Korman     const addr_t addr = range_info.GetRange().GetRangeBase();
591eee687a6SAndrej Korman     // Skip any regions we have already saved out.
592eee687a6SAndrej Korman     if (visited_region_base_addresses.insert(addr).second == false)
593eee687a6SAndrej Korman       continue;
594eee687a6SAndrej Korman     const addr_t size = range_info.GetRange().GetByteSize();
595eee687a6SAndrej Korman     if (size == 0)
596eee687a6SAndrej Korman       continue;
597eee687a6SAndrej Korman     auto data_up = std::make_unique<DataBufferHeap>(size, 0);
598eee687a6SAndrej Korman     const size_t bytes_read =
599eee687a6SAndrej Korman         process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);
600eee687a6SAndrej Korman     if (bytes_read == 0)
601eee687a6SAndrej Korman       continue;
602eee687a6SAndrej Korman     // We have a good memory region with valid bytes to store.
603eee687a6SAndrej Korman     LocationDescriptor memory_dump;
604eee687a6SAndrej Korman     memory_dump.DataSize = static_cast<llvm::support::ulittle32_t>(bytes_read);
605eee687a6SAndrej Korman     memory_dump.RVA =
606eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset());
607eee687a6SAndrej Korman     MemoryDescriptor memory_desc;
608eee687a6SAndrej Korman     memory_desc.StartOfMemoryRange =
609eee687a6SAndrej Korman         static_cast<llvm::support::ulittle64_t>(addr);
610eee687a6SAndrej Korman     memory_desc.Memory = memory_dump;
611eee687a6SAndrej Korman     mem_descriptors.push_back(memory_desc);
612eee687a6SAndrej Korman     m_data.AppendData(data_up->GetBytes(), bytes_read);
613eee687a6SAndrej Korman   }
614eee687a6SAndrej Korman 
615eee687a6SAndrej Korman   AddDirectory(StreamType::MemoryList,
616eee687a6SAndrej Korman                sizeof(llvm::support::ulittle32_t) +
617eee687a6SAndrej Korman                    mem_descriptors.size() *
618eee687a6SAndrej Korman                        sizeof(llvm::minidump::MemoryDescriptor));
619eee687a6SAndrej Korman   llvm::support::ulittle32_t memory_ranges_num(mem_descriptors.size());
620eee687a6SAndrej Korman 
621eee687a6SAndrej Korman   m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle32_t));
622eee687a6SAndrej Korman   for (auto memory_descriptor : mem_descriptors) {
623eee687a6SAndrej Korman     m_data.AppendData(&memory_descriptor,
624eee687a6SAndrej Korman                       sizeof(llvm::minidump::MemoryDescriptor));
625eee687a6SAndrej Korman   }
626eee687a6SAndrej Korman 
627eee687a6SAndrej Korman   return error;
628eee687a6SAndrej Korman }
629eee687a6SAndrej Korman 
AddMiscInfo(const lldb::ProcessSP & process_sp)630eee687a6SAndrej Korman void MinidumpFileBuilder::AddMiscInfo(const lldb::ProcessSP &process_sp) {
631eee687a6SAndrej Korman   AddDirectory(StreamType::MiscInfo,
632eee687a6SAndrej Korman                sizeof(lldb_private::minidump::MinidumpMiscInfo));
633eee687a6SAndrej Korman 
634eee687a6SAndrej Korman   lldb_private::minidump::MinidumpMiscInfo misc_info;
635eee687a6SAndrej Korman   misc_info.size = static_cast<llvm::support::ulittle32_t>(
636eee687a6SAndrej Korman       sizeof(lldb_private::minidump::MinidumpMiscInfo));
637eee687a6SAndrej Korman   // Default set flags1 to 0, in case that we will not be able to
638eee687a6SAndrej Korman   // get any information
639eee687a6SAndrej Korman   misc_info.flags1 = static_cast<llvm::support::ulittle32_t>(0);
640eee687a6SAndrej Korman 
641eee687a6SAndrej Korman   lldb_private::ProcessInstanceInfo process_info;
642eee687a6SAndrej Korman   process_sp->GetProcessInfo(process_info);
643eee687a6SAndrej Korman   if (process_info.ProcessIDIsValid()) {
644eee687a6SAndrej Korman     // Set flags1 to reflect that PID is filled in
645eee687a6SAndrej Korman     misc_info.flags1 =
646eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(static_cast<uint32_t>(
647eee687a6SAndrej Korman             lldb_private::minidump::MinidumpMiscInfoFlags::ProcessID));
648eee687a6SAndrej Korman     misc_info.process_id =
649eee687a6SAndrej Korman         static_cast<llvm::support::ulittle32_t>(process_info.GetProcessID());
650eee687a6SAndrej Korman   }
651eee687a6SAndrej Korman 
652eee687a6SAndrej Korman   m_data.AppendData(&misc_info,
653eee687a6SAndrej Korman                     sizeof(lldb_private::minidump::MinidumpMiscInfo));
654eee687a6SAndrej Korman }
655eee687a6SAndrej Korman 
656eee687a6SAndrej Korman std::unique_ptr<llvm::MemoryBuffer>
getFileStreamHelper(const std::string & path)657eee687a6SAndrej Korman getFileStreamHelper(const std::string &path) {
658eee687a6SAndrej Korman   auto maybe_stream = llvm::MemoryBuffer::getFileAsStream(path);
659eee687a6SAndrej Korman   if (!maybe_stream)
660eee687a6SAndrej Korman     return nullptr;
661eee687a6SAndrej Korman   return std::move(maybe_stream.get());
662eee687a6SAndrej Korman }
663eee687a6SAndrej Korman 
AddLinuxFileStreams(const lldb::ProcessSP & process_sp)664eee687a6SAndrej Korman void MinidumpFileBuilder::AddLinuxFileStreams(
665eee687a6SAndrej Korman     const lldb::ProcessSP &process_sp) {
666eee687a6SAndrej Korman   std::vector<std::pair<StreamType, std::string>> files_with_stream_types = {
667eee687a6SAndrej Korman       {StreamType::LinuxCPUInfo, "/proc/cpuinfo"},
668eee687a6SAndrej Korman       {StreamType::LinuxLSBRelease, "/etc/lsb-release"},
669eee687a6SAndrej Korman   };
670eee687a6SAndrej Korman 
671eee687a6SAndrej Korman   lldb_private::ProcessInstanceInfo process_info;
672eee687a6SAndrej Korman   process_sp->GetProcessInfo(process_info);
673eee687a6SAndrej Korman   if (process_info.ProcessIDIsValid()) {
674eee687a6SAndrej Korman     lldb::pid_t pid = process_info.GetProcessID();
675eee687a6SAndrej Korman     std::string pid_str = std::to_string(pid);
676eee687a6SAndrej Korman     files_with_stream_types.push_back(
677eee687a6SAndrej Korman         {StreamType::LinuxProcStatus, "/proc/" + pid_str + "/status"});
678eee687a6SAndrej Korman     files_with_stream_types.push_back(
679eee687a6SAndrej Korman         {StreamType::LinuxCMDLine, "/proc/" + pid_str + "/cmdline"});
680eee687a6SAndrej Korman     files_with_stream_types.push_back(
681eee687a6SAndrej Korman         {StreamType::LinuxEnviron, "/proc/" + pid_str + "/environ"});
682eee687a6SAndrej Korman     files_with_stream_types.push_back(
683eee687a6SAndrej Korman         {StreamType::LinuxAuxv, "/proc/" + pid_str + "/auxv"});
684eee687a6SAndrej Korman     files_with_stream_types.push_back(
685eee687a6SAndrej Korman         {StreamType::LinuxMaps, "/proc/" + pid_str + "/maps"});
686eee687a6SAndrej Korman     files_with_stream_types.push_back(
687eee687a6SAndrej Korman         {StreamType::LinuxProcStat, "/proc/" + pid_str + "/stat"});
688eee687a6SAndrej Korman     files_with_stream_types.push_back(
689eee687a6SAndrej Korman         {StreamType::LinuxProcFD, "/proc/" + pid_str + "/fd"});
690eee687a6SAndrej Korman   }
691eee687a6SAndrej Korman 
692eee687a6SAndrej Korman   for (const auto &entry : files_with_stream_types) {
693eee687a6SAndrej Korman     StreamType stream = entry.first;
694eee687a6SAndrej Korman     std::string path = entry.second;
695eee687a6SAndrej Korman     auto memory_buffer = getFileStreamHelper(path);
696eee687a6SAndrej Korman 
697eee687a6SAndrej Korman     if (memory_buffer) {
698eee687a6SAndrej Korman       size_t size = memory_buffer->getBufferSize();
699eee687a6SAndrej Korman       if (size == 0)
700eee687a6SAndrej Korman         continue;
701eee687a6SAndrej Korman       AddDirectory(stream, size);
702eee687a6SAndrej Korman       m_data.AppendData(memory_buffer->getBufferStart(), size);
703eee687a6SAndrej Korman     }
704eee687a6SAndrej Korman   }
705eee687a6SAndrej Korman }
706eee687a6SAndrej Korman 
Dump(lldb::FileUP & core_file) const707eee687a6SAndrej Korman Status MinidumpFileBuilder::Dump(lldb::FileUP &core_file) const {
708eee687a6SAndrej Korman   constexpr size_t header_size = sizeof(llvm::minidump::Header);
709eee687a6SAndrej Korman   constexpr size_t directory_size = sizeof(llvm::minidump::Directory);
710eee687a6SAndrej Korman 
711eee687a6SAndrej Korman   // write header
712eee687a6SAndrej Korman   llvm::minidump::Header header;
713eee687a6SAndrej Korman   header.Signature = static_cast<llvm::support::ulittle32_t>(
714eee687a6SAndrej Korman       llvm::minidump::Header::MagicSignature);
715eee687a6SAndrej Korman   header.Version = static_cast<llvm::support::ulittle32_t>(
716eee687a6SAndrej Korman       llvm::minidump::Header::MagicVersion);
717eee687a6SAndrej Korman   header.NumberOfStreams =
718eee687a6SAndrej Korman       static_cast<llvm::support::ulittle32_t>(GetDirectoriesNum());
719eee687a6SAndrej Korman   header.StreamDirectoryRVA =
720eee687a6SAndrej Korman       static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset());
721eee687a6SAndrej Korman   header.Checksum = static_cast<llvm::support::ulittle32_t>(
722eee687a6SAndrej Korman       0u), // not used in most of the writers
723eee687a6SAndrej Korman       header.TimeDateStamp =
724b8336280SKazu Hirata           static_cast<llvm::support::ulittle32_t>(std::time(nullptr));
725eee687a6SAndrej Korman   header.Flags =
726eee687a6SAndrej Korman       static_cast<llvm::support::ulittle64_t>(0u); // minidump normal flag
727eee687a6SAndrej Korman 
728eee687a6SAndrej Korman   Status error;
729eee687a6SAndrej Korman   size_t bytes_written;
730eee687a6SAndrej Korman 
731eee687a6SAndrej Korman   bytes_written = header_size;
732eee687a6SAndrej Korman   error = core_file->Write(&header, bytes_written);
733eee687a6SAndrej Korman   if (error.Fail() || bytes_written != header_size) {
734eee687a6SAndrej Korman     if (bytes_written != header_size)
735eee687a6SAndrej Korman       error.SetErrorStringWithFormat(
736e69d3598SFangrui Song           "unable to write the header (written %zd/%zd)", bytes_written,
737eee687a6SAndrej Korman           header_size);
738eee687a6SAndrej Korman     return error;
739eee687a6SAndrej Korman   }
740eee687a6SAndrej Korman 
741eee687a6SAndrej Korman   // write data
742eee687a6SAndrej Korman   bytes_written = m_data.GetByteSize();
743eee687a6SAndrej Korman   error = core_file->Write(m_data.GetBytes(), bytes_written);
744eee687a6SAndrej Korman   if (error.Fail() || bytes_written != m_data.GetByteSize()) {
745eee687a6SAndrej Korman     if (bytes_written != m_data.GetByteSize())
746eee687a6SAndrej Korman       error.SetErrorStringWithFormat(
747e69d3598SFangrui Song           "unable to write the data (written %zd/%" PRIu64 ")", bytes_written,
748eee687a6SAndrej Korman           m_data.GetByteSize());
749eee687a6SAndrej Korman     return error;
750eee687a6SAndrej Korman   }
751eee687a6SAndrej Korman 
752eee687a6SAndrej Korman   // write directories
753eee687a6SAndrej Korman   for (const Directory &dir : m_directories) {
754eee687a6SAndrej Korman     bytes_written = directory_size;
755eee687a6SAndrej Korman     error = core_file->Write(&dir, bytes_written);
756eee687a6SAndrej Korman     if (error.Fail() || bytes_written != directory_size) {
757eee687a6SAndrej Korman       if (bytes_written != directory_size)
758eee687a6SAndrej Korman         error.SetErrorStringWithFormat(
759e69d3598SFangrui Song             "unable to write the directory (written %zd/%zd)", bytes_written,
760eee687a6SAndrej Korman             directory_size);
761eee687a6SAndrej Korman       return error;
762eee687a6SAndrej Korman     }
763eee687a6SAndrej Korman   }
764eee687a6SAndrej Korman 
765eee687a6SAndrej Korman   return error;
766eee687a6SAndrej Korman }
767eee687a6SAndrej Korman 
GetDirectoriesNum() const768eee687a6SAndrej Korman size_t MinidumpFileBuilder::GetDirectoriesNum() const {
769eee687a6SAndrej Korman   return m_directories.size();
770eee687a6SAndrej Korman }
771eee687a6SAndrej Korman 
GetCurrentDataEndOffset() const772eee687a6SAndrej Korman size_t MinidumpFileBuilder::GetCurrentDataEndOffset() const {
773eee687a6SAndrej Korman   return sizeof(llvm::minidump::Header) + m_data.GetByteSize();
774eee687a6SAndrej Korman }
775