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