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 §ion : *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