VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/DBGFCoreWrite.cpp@ 93554

Last change on this file since 93554 was 93554, checked in by vboxsync, 3 years ago

VMM: Changed PAGE_SIZE -> GUEST_PAGE_SIZE / HOST_PAGE_SIZE, PAGE_SHIFT -> GUEST_PAGE_SHIFT / HOST_PAGE_SHIFT, and PAGE_OFFSET_MASK -> GUEST_PAGE_OFFSET_MASK / HOST_PAGE_OFFSET_MASK. Also removed most usage of ASMMemIsZeroPage and ASMMemZeroPage since the host and guest page size doesn't need to be the same any more. Some work left to do in the page pool code. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.1 KB
Line 
1/* $Id: DBGFCoreWrite.cpp 93554 2022-02-02 22:57:02Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, Guest Core Dump.
4 */
5
6/*
7 * Copyright (C) 2010-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_dbgf_vmcore VMCore Format
19 *
20 * The VirtualBox VMCore Format:
21 * [ ELF 64 Header] -- Only 1
22 *
23 * [ PT_NOTE ] -- Only 1
24 * - Offset into CoreDescriptor followed by list of Notes (Note Hdr + data) of VBox CPUs.
25 * - (Any Additional custom Note sections).
26 *
27 * [ PT_LOAD ] -- One for each contiguous memory chunk
28 * - Memory offset (physical).
29 * - File offset.
30 *
31 * CoreDescriptor
32 * - Magic, VBox version.
33 * - Number of CPus.
34 *
35 * Per-CPU register dump
36 * - CPU 1 Note Hdr + Data.
37 * - CPU 2 Note Hdr + Data.
38 * ...
39 * (Additional custom notes Hdr+data)
40 * - VBox 1 Note Hdr + Data.
41 * - VBox 2 Note Hdr + Data.
42 * ...
43 * Memory dump
44 *
45 */
46
47
48/*********************************************************************************************************************************
49* Header Files *
50*********************************************************************************************************************************/
51#define LOG_GROUP LOG_GROUP_DBGF
52#include <iprt/param.h>
53#include <iprt/file.h>
54#include <iprt/mem.h>
55#include <iprt/formats/elf64.h>
56
57#include "DBGFInternal.h"
58
59#include <VBox/vmm/cpum.h>
60#include <VBox/vmm/pgm.h>
61#include <VBox/vmm/apic.h>
62#include <VBox/vmm/dbgf.h>
63#include <VBox/vmm/dbgfcorefmt.h>
64#include <VBox/vmm/mm.h>
65#include <VBox/vmm/vm.h>
66#include <VBox/vmm/uvm.h>
67
68#include <VBox/err.h>
69#include <VBox/log.h>
70#include <VBox/version.h>
71
72
73/*********************************************************************************************************************************
74* Defined Constants And Macros *
75*********************************************************************************************************************************/
76#define DBGFLOG_NAME "DBGFCoreWrite"
77
78
79/*********************************************************************************************************************************
80* Global Variables *
81*********************************************************************************************************************************/
82static const int g_NoteAlign = 8;
83static const int g_cbNoteName = 16;
84
85/* The size of these strings (incl. NULL terminator) must align to 8 bytes (g_NoteAlign) and -not- 4 bytes. */
86static const char *g_pcszCoreVBoxCore = "VBCORE";
87static const char *g_pcszCoreVBoxCpu = "VBCPU";
88
89
90/*********************************************************************************************************************************
91* Structures and Typedefs *
92*********************************************************************************************************************************/
93/**
94 * Guest core writer data.
95 *
96 * Used to pass parameters from DBGFR3CoreWrite to dbgfR3CoreWriteRendezvous().
97 */
98typedef struct DBGFCOREDATA
99{
100 /** The name of the file to write the file to. */
101 const char *pszFilename;
102 /** Whether to replace (/overwrite) any existing file. */
103 bool fReplaceFile;
104} DBGFCOREDATA;
105/** Pointer to the guest core writer data. */
106typedef DBGFCOREDATA *PDBGFCOREDATA;
107
108
109
110/**
111 * ELF function to write 64-bit ELF header.
112 *
113 * @param hFile The file to write to.
114 * @param cProgHdrs Number of program headers.
115 * @param cSecHdrs Number of section headers.
116 *
117 * @return IPRT status code.
118 */
119static int Elf64WriteElfHdr(RTFILE hFile, uint16_t cProgHdrs, uint16_t cSecHdrs)
120{
121 Elf64_Ehdr ElfHdr;
122 RT_ZERO(ElfHdr);
123 ElfHdr.e_ident[EI_MAG0] = ELFMAG0;
124 ElfHdr.e_ident[EI_MAG1] = ELFMAG1;
125 ElfHdr.e_ident[EI_MAG2] = ELFMAG2;
126 ElfHdr.e_ident[EI_MAG3] = ELFMAG3;
127 ElfHdr.e_ident[EI_DATA] = ELFDATA2LSB;
128 ElfHdr.e_type = ET_CORE;
129 ElfHdr.e_version = EV_CURRENT;
130 ElfHdr.e_ident[EI_CLASS] = ELFCLASS64;
131 /* 32-bit builds will produce cores with e_machine EM_386. */
132#ifdef RT_ARCH_AMD64
133 ElfHdr.e_machine = EM_X86_64;
134#else
135 ElfHdr.e_machine = EM_386;
136#endif
137 ElfHdr.e_phnum = cProgHdrs;
138 ElfHdr.e_shnum = cSecHdrs;
139 ElfHdr.e_ehsize = sizeof(ElfHdr);
140 ElfHdr.e_phoff = sizeof(ElfHdr);
141 ElfHdr.e_phentsize = sizeof(Elf64_Phdr);
142 ElfHdr.e_shentsize = sizeof(Elf64_Shdr);
143
144 return RTFileWrite(hFile, &ElfHdr, sizeof(ElfHdr), NULL /* all */);
145}
146
147
148/**
149 * ELF function to write 64-bit program header.
150 *
151 * @param hFile The file to write to.
152 * @param Type Type of program header (PT_*).
153 * @param fFlags Flags (access permissions, PF_*).
154 * @param offFileData File offset of contents.
155 * @param cbFileData Size of contents in the file.
156 * @param cbMemData Size of contents in memory.
157 * @param Phys Physical address, pass zero if not applicable.
158 *
159 * @return IPRT status code.
160 */
161static int Elf64WriteProgHdr(RTFILE hFile, uint32_t Type, uint32_t fFlags, uint64_t offFileData, uint64_t cbFileData,
162 uint64_t cbMemData, RTGCPHYS Phys)
163{
164 Elf64_Phdr ProgHdr;
165 RT_ZERO(ProgHdr);
166 ProgHdr.p_type = Type;
167 ProgHdr.p_flags = fFlags;
168 ProgHdr.p_offset = offFileData;
169 ProgHdr.p_filesz = cbFileData;
170 ProgHdr.p_memsz = cbMemData;
171 ProgHdr.p_paddr = Phys;
172
173 return RTFileWrite(hFile, &ProgHdr, sizeof(ProgHdr), NULL /* all */);
174}
175
176
177/**
178 * Returns the size of the NOTE section given the name and size of the data.
179 *
180 * @param pszName Name of the note section.
181 * @param cbData Size of the data portion of the note section.
182 *
183 * @return The size of the NOTE section as rounded to the file alignment.
184 */
185static uint64_t Elf64NoteSectionSize(const char *pszName, uint64_t cbData)
186{
187 uint64_t cbNote = sizeof(Elf64_Nhdr);
188
189 size_t cbName = strlen(pszName) + 1;
190 size_t cbNameAlign = RT_ALIGN_Z(cbName, g_NoteAlign);
191
192 cbNote += cbNameAlign;
193 cbNote += RT_ALIGN_64(cbData, g_NoteAlign);
194 return cbNote;
195}
196
197
198/**
199 * Elf function to write 64-bit note header.
200 *
201 * @param hFile The file to write to.
202 * @param Type Type of this section.
203 * @param pszName Name of this section.
204 * @param pvData Opaque pointer to the data, if NULL only computes size.
205 * @param cbData Size of the data.
206 *
207 * @returns IPRT status code.
208 */
209static int Elf64WriteNoteHdr(RTFILE hFile, uint16_t Type, const char *pszName, const void *pvData, uint64_t cbData)
210{
211 AssertReturn(pvData, VERR_INVALID_POINTER);
212 AssertReturn(cbData > 0, VERR_NO_DATA);
213
214 char szNoteName[g_cbNoteName];
215 RT_ZERO(szNoteName);
216 RTStrCopy(szNoteName, sizeof(szNoteName), pszName);
217
218 size_t cbName = strlen(szNoteName) + 1;
219 size_t cbNameAlign = RT_ALIGN_Z(cbName, g_NoteAlign);
220 uint64_t cbDataAlign = RT_ALIGN_64(cbData, g_NoteAlign);
221
222 /*
223 * Yell loudly and bail if we are going to be writing a core file that is not compatible with
224 * both Solaris and the 64-bit ELF spec. which dictates 8-byte alignment. See @bugref{5211#c3}.
225 */
226 if (cbNameAlign - cbName > 3)
227 {
228 LogRel((DBGFLOG_NAME ": Elf64WriteNoteHdr pszName=%s cbName=%u cbNameAlign=%u, cbName aligns to 4 not 8-bytes!\n",
229 pszName, cbName, cbNameAlign));
230 return VERR_INVALID_PARAMETER;
231 }
232
233 if (cbDataAlign - cbData > 3)
234 {
235 LogRel((DBGFLOG_NAME ": Elf64WriteNoteHdr pszName=%s cbData=%u cbDataAlign=%u, cbData aligns to 4 not 8-bytes!\n",
236 pszName, cbData, cbDataAlign));
237 return VERR_INVALID_PARAMETER;
238 }
239
240 static const char s_achPad[7] = { 0, 0, 0, 0, 0, 0, 0 };
241 AssertCompile(sizeof(s_achPad) >= g_NoteAlign - 1);
242
243 Elf64_Nhdr ElfNoteHdr;
244 RT_ZERO(ElfNoteHdr);
245 ElfNoteHdr.n_namesz = (Elf64_Word)cbName - 1; /* Again, a discrepancy between ELF-64 and Solaris,
246 we will follow ELF-64, see @bugref{5211#c3}. */
247 ElfNoteHdr.n_type = Type;
248 ElfNoteHdr.n_descsz = (Elf64_Word)cbDataAlign;
249
250 /*
251 * Write note header.
252 */
253 int rc = RTFileWrite(hFile, &ElfNoteHdr, sizeof(ElfNoteHdr), NULL /* all */);
254 if (RT_SUCCESS(rc))
255 {
256 /*
257 * Write note name.
258 */
259 rc = RTFileWrite(hFile, szNoteName, cbName, NULL /* all */);
260 if (RT_SUCCESS(rc))
261 {
262 /*
263 * Write note name padding if required.
264 */
265 if (cbNameAlign > cbName)
266 rc = RTFileWrite(hFile, s_achPad, cbNameAlign - cbName, NULL);
267
268 if (RT_SUCCESS(rc))
269 {
270 /*
271 * Write note data.
272 */
273 rc = RTFileWrite(hFile, pvData, cbData, NULL /* all */);
274 if (RT_SUCCESS(rc))
275 {
276 /*
277 * Write note data padding if required.
278 */
279 if (cbDataAlign > cbData)
280 rc = RTFileWrite(hFile, s_achPad, cbDataAlign - cbData, NULL /* all*/);
281 }
282 }
283 }
284 }
285
286 if (RT_FAILURE(rc))
287 LogRel((DBGFLOG_NAME ": RTFileWrite failed. rc=%Rrc pszName=%s cbName=%u cbNameAlign=%u cbData=%u cbDataAlign=%u\n",
288 rc, pszName, cbName, cbNameAlign, cbData, cbDataAlign));
289
290 return rc;
291}
292
293
294/**
295 * Count the number of memory ranges that go into the core file.
296 *
297 * We cannot do a page-by-page dump of the entire guest memory as there will be
298 * way too many program header entries. Also we don't want to dump MMIO regions
299 * which means we cannot have a 1:1 mapping between core file offset and memory
300 * offset. Instead we dump the memory in ranges. A memory range is a contiguous
301 * memory area suitable for dumping to a core file.
302 *
303 * @param pVM The cross context VM structure.
304 *
305 * @return Number of memory ranges
306 */
307static uint32_t dbgfR3GetRamRangeCount(PVM pVM)
308{
309 return PGMR3PhysGetRamRangeCount(pVM);
310}
311
312
313/**
314 * Gets the guest-CPU context suitable for dumping into the core file.
315 *
316 * @param pVCpu The cross context virtual CPU structure.
317 * @param pDbgfCpu Where to dump the guest-CPU data.
318 */
319static void dbgfR3GetCoreCpu(PVMCPU pVCpu, PDBGFCORECPU pDbgfCpu)
320{
321#define DBGFCOPYSEL(a_dbgfsel, a_cpumselreg) \
322 do { \
323 (a_dbgfsel).uBase = (a_cpumselreg).u64Base; \
324 (a_dbgfsel).uLimit = (a_cpumselreg).u32Limit; \
325 (a_dbgfsel).uAttr = (a_cpumselreg).Attr.u; \
326 (a_dbgfsel).uSel = (a_cpumselreg).Sel; \
327 } while (0)
328
329 PVM pVM = pVCpu->CTX_SUFF(pVM);
330 PCCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
331 pDbgfCpu->rax = pCtx->rax;
332 pDbgfCpu->rbx = pCtx->rbx;
333 pDbgfCpu->rcx = pCtx->rcx;
334 pDbgfCpu->rdx = pCtx->rdx;
335 pDbgfCpu->rsi = pCtx->rsi;
336 pDbgfCpu->rdi = pCtx->rdi;
337 pDbgfCpu->r8 = pCtx->r8;
338 pDbgfCpu->r9 = pCtx->r9;
339 pDbgfCpu->r10 = pCtx->r10;
340 pDbgfCpu->r11 = pCtx->r11;
341 pDbgfCpu->r12 = pCtx->r12;
342 pDbgfCpu->r13 = pCtx->r13;
343 pDbgfCpu->r14 = pCtx->r14;
344 pDbgfCpu->r15 = pCtx->r15;
345 pDbgfCpu->rip = pCtx->rip;
346 pDbgfCpu->rsp = pCtx->rsp;
347 pDbgfCpu->rbp = pCtx->rbp;
348 pDbgfCpu->rflags = pCtx->rflags.u;
349 DBGFCOPYSEL(pDbgfCpu->cs, pCtx->cs);
350 DBGFCOPYSEL(pDbgfCpu->ds, pCtx->ds);
351 DBGFCOPYSEL(pDbgfCpu->es, pCtx->es);
352 DBGFCOPYSEL(pDbgfCpu->fs, pCtx->fs);
353 DBGFCOPYSEL(pDbgfCpu->gs, pCtx->gs);
354 DBGFCOPYSEL(pDbgfCpu->ss, pCtx->ss);
355 pDbgfCpu->cr0 = pCtx->cr0;
356 pDbgfCpu->cr2 = pCtx->cr2;
357 pDbgfCpu->cr3 = pCtx->cr3;
358 pDbgfCpu->cr4 = pCtx->cr4;
359 AssertCompile(RT_ELEMENTS(pDbgfCpu->dr) == RT_ELEMENTS(pCtx->dr));
360 for (unsigned i = 0; i < RT_ELEMENTS(pDbgfCpu->dr); i++)
361 pDbgfCpu->dr[i] = pCtx->dr[i];
362 pDbgfCpu->gdtr.uAddr = pCtx->gdtr.pGdt;
363 pDbgfCpu->gdtr.cb = pCtx->gdtr.cbGdt;
364 pDbgfCpu->idtr.uAddr = pCtx->idtr.pIdt;
365 pDbgfCpu->idtr.cb = pCtx->idtr.cbIdt;
366 DBGFCOPYSEL(pDbgfCpu->ldtr, pCtx->ldtr);
367 DBGFCOPYSEL(pDbgfCpu->tr, pCtx->tr);
368 pDbgfCpu->sysenter.cs = pCtx->SysEnter.cs;
369 pDbgfCpu->sysenter.eip = pCtx->SysEnter.eip;
370 pDbgfCpu->sysenter.esp = pCtx->SysEnter.esp;
371 pDbgfCpu->msrEFER = pCtx->msrEFER;
372 pDbgfCpu->msrSTAR = pCtx->msrSTAR;
373 pDbgfCpu->msrPAT = pCtx->msrPAT;
374 pDbgfCpu->msrLSTAR = pCtx->msrLSTAR;
375 pDbgfCpu->msrCSTAR = pCtx->msrCSTAR;
376 pDbgfCpu->msrSFMASK = pCtx->msrSFMASK;
377 pDbgfCpu->msrKernelGSBase = pCtx->msrKERNELGSBASE;
378 pDbgfCpu->msrApicBase = APICGetBaseMsrNoCheck(pVCpu);
379 pDbgfCpu->msrTscAux = CPUMGetGuestTscAux(pVCpu);
380 pDbgfCpu->aXcr[0] = pCtx->aXcr[0];
381 pDbgfCpu->aXcr[1] = pCtx->aXcr[1];
382 AssertCompile(sizeof(pDbgfCpu->ext) == sizeof(pCtx->XState));
383 pDbgfCpu->cbExt = pVM->cpum.ro.GuestFeatures.cbMaxExtendedState;
384 if (RT_LIKELY(pDbgfCpu->cbExt))
385 memcpy(&pDbgfCpu->ext, &pCtx->XState, pDbgfCpu->cbExt);
386
387#undef DBGFCOPYSEL
388}
389
390
391/**
392 * Worker function for dbgfR3CoreWrite() which does the writing.
393 *
394 * @returns VBox status code
395 * @param pVM The cross context VM structure.
396 * @param hFile The file to write to. Caller closes this.
397 */
398static int dbgfR3CoreWriteWorker(PVM pVM, RTFILE hFile)
399{
400 /*
401 * Collect core information.
402 */
403 uint32_t const cu32MemRanges = dbgfR3GetRamRangeCount(pVM);
404 uint16_t const cMemRanges = cu32MemRanges < UINT16_MAX - 1 ? cu32MemRanges : UINT16_MAX - 1; /* One PT_NOTE Program header */
405 uint16_t const cProgHdrs = cMemRanges + 1;
406
407 DBGFCOREDESCRIPTOR CoreDescriptor;
408 RT_ZERO(CoreDescriptor);
409 CoreDescriptor.u32Magic = DBGFCORE_MAGIC;
410 CoreDescriptor.u32FmtVersion = DBGFCORE_FMT_VERSION;
411 CoreDescriptor.cbSelf = sizeof(CoreDescriptor);
412 CoreDescriptor.u32VBoxVersion = VBOX_FULL_VERSION;
413 CoreDescriptor.u32VBoxRevision = VMMGetSvnRev();
414 CoreDescriptor.cCpus = pVM->cCpus;
415
416 Log((DBGFLOG_NAME ": CoreDescriptor Version=%u Revision=%u\n", CoreDescriptor.u32VBoxVersion, CoreDescriptor.u32VBoxRevision));
417
418 /*
419 * Compute the file layout (see pg_dbgf_vmcore).
420 */
421 uint64_t const offElfHdr = RTFileTell(hFile);
422 uint64_t const offNoteSection = offElfHdr + sizeof(Elf64_Ehdr);
423 uint64_t const offLoadSections = offNoteSection + sizeof(Elf64_Phdr);
424 uint64_t const cbLoadSections = cMemRanges * sizeof(Elf64_Phdr);
425 uint64_t const offCoreDescriptor = offLoadSections + cbLoadSections;
426 uint64_t const cbCoreDescriptor = Elf64NoteSectionSize(g_pcszCoreVBoxCore, sizeof(CoreDescriptor));
427 uint64_t const offCpuDumps = offCoreDescriptor + cbCoreDescriptor;
428 uint64_t const cbCpuDumps = pVM->cCpus * Elf64NoteSectionSize(g_pcszCoreVBoxCpu, sizeof(DBGFCORECPU));
429 uint64_t const offMemory = offCpuDumps + cbCpuDumps;
430
431 uint64_t const offNoteSectionData = offCoreDescriptor;
432 uint64_t const cbNoteSectionData = cbCoreDescriptor + cbCpuDumps;
433
434 /*
435 * Write ELF header.
436 */
437 int rc = Elf64WriteElfHdr(hFile, cProgHdrs, 0 /* cSecHdrs */);
438 if (RT_FAILURE(rc))
439 {
440 LogRel((DBGFLOG_NAME ": Elf64WriteElfHdr failed. rc=%Rrc\n", rc));
441 return rc;
442 }
443
444 /*
445 * Write PT_NOTE program header.
446 */
447 Assert(RTFileTell(hFile) == offNoteSection);
448 rc = Elf64WriteProgHdr(hFile, PT_NOTE, PF_R,
449 offNoteSectionData, /* file offset to contents */
450 cbNoteSectionData, /* size in core file */
451 cbNoteSectionData, /* size in memory */
452 0); /* physical address */
453 if (RT_FAILURE(rc))
454 {
455 LogRel((DBGFLOG_NAME ": Elf64WritreProgHdr failed for PT_NOTE. rc=%Rrc\n", rc));
456 return rc;
457 }
458
459 /*
460 * Write PT_LOAD program header for each memory range.
461 */
462 Assert(RTFileTell(hFile) == offLoadSections);
463 uint64_t offMemRange = offMemory;
464 for (uint16_t iRange = 0; iRange < cMemRanges; iRange++)
465 {
466 RTGCPHYS GCPhysStart;
467 RTGCPHYS GCPhysEnd;
468 bool fIsMmio;
469 rc = PGMR3PhysGetRange(pVM, iRange, &GCPhysStart, &GCPhysEnd, NULL /* pszDesc */, &fIsMmio);
470 if (RT_FAILURE(rc))
471 {
472 LogRel((DBGFLOG_NAME ": PGMR3PhysGetRange failed for iRange(%u) rc=%Rrc\n", iRange, rc));
473 return rc;
474 }
475
476 uint64_t cbMemRange = GCPhysEnd - GCPhysStart + 1;
477 uint64_t cbFileRange = fIsMmio ? 0 : cbMemRange;
478
479 Log((DBGFLOG_NAME ": PGMR3PhysGetRange iRange=%u GCPhysStart=%#x GCPhysEnd=%#x cbMemRange=%u\n",
480 iRange, GCPhysStart, GCPhysEnd, cbMemRange));
481
482 rc = Elf64WriteProgHdr(hFile, PT_LOAD, PF_R,
483 offMemRange, /* file offset to contents */
484 cbFileRange, /* size in core file */
485 cbMemRange, /* size in memory */
486 GCPhysStart); /* physical address */
487 if (RT_FAILURE(rc))
488 {
489 LogRel((DBGFLOG_NAME ": Elf64WriteProgHdr failed for memory range(%u) cbFileRange=%u cbMemRange=%u rc=%Rrc\n",
490 iRange, cbFileRange, cbMemRange, rc));
491 return rc;
492 }
493
494 offMemRange += cbFileRange;
495 }
496
497 /*
498 * Write the Core descriptor note header and data.
499 */
500 Assert(RTFileTell(hFile) == offCoreDescriptor);
501 rc = Elf64WriteNoteHdr(hFile, NT_VBOXCORE, g_pcszCoreVBoxCore, &CoreDescriptor, sizeof(CoreDescriptor));
502 if (RT_FAILURE(rc))
503 {
504 LogRel((DBGFLOG_NAME ": Elf64WriteNoteHdr failed for Note '%s' rc=%Rrc\n", g_pcszCoreVBoxCore, rc));
505 return rc;
506 }
507
508 /*
509 * Write the CPU context note headers and data.
510 * We allocate the DBGFCORECPU struct. rather than using the stack as it can be pretty large due to X86XSAVEAREA.
511 */
512 Assert(RTFileTell(hFile) == offCpuDumps);
513 PDBGFCORECPU pDbgfCoreCpu = (PDBGFCORECPU)RTMemAlloc(sizeof(*pDbgfCoreCpu));
514 if (RT_UNLIKELY(!pDbgfCoreCpu))
515 {
516 LogRel((DBGFLOG_NAME ": Failed to alloc %u bytes for DBGFCORECPU\n", sizeof(*pDbgfCoreCpu)));
517 return VERR_NO_MEMORY;
518 }
519
520 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
521 {
522 PVMCPU pVCpu = pVM->apCpusR3[idCpu];
523 RT_BZERO(pDbgfCoreCpu, sizeof(*pDbgfCoreCpu));
524 dbgfR3GetCoreCpu(pVCpu, pDbgfCoreCpu);
525
526 rc = Elf64WriteNoteHdr(hFile, NT_VBOXCPU, g_pcszCoreVBoxCpu, pDbgfCoreCpu, sizeof(*pDbgfCoreCpu));
527 if (RT_FAILURE(rc))
528 {
529 LogRel((DBGFLOG_NAME ": Elf64WriteNoteHdr failed for vCPU[%u] rc=%Rrc\n", idCpu, rc));
530 RTMemFree(pDbgfCoreCpu);
531 return rc;
532 }
533 }
534 RTMemFree(pDbgfCoreCpu);
535 pDbgfCoreCpu = NULL;
536
537 /*
538 * Write memory ranges.
539 */
540 Assert(RTFileTell(hFile) == offMemory);
541 for (uint16_t iRange = 0; iRange < cMemRanges; iRange++)
542 {
543 RTGCPHYS GCPhysStart;
544 RTGCPHYS GCPhysEnd;
545 bool fIsMmio;
546 rc = PGMR3PhysGetRange(pVM, iRange, &GCPhysStart, &GCPhysEnd, NULL /* pszDesc */, &fIsMmio);
547 if (RT_FAILURE(rc))
548 {
549 LogRel((DBGFLOG_NAME ": PGMR3PhysGetRange(2) failed for iRange(%u) rc=%Rrc\n", iRange, rc));
550 return rc;
551 }
552
553 if (fIsMmio)
554 continue;
555
556 /*
557 * Write page-by-page of this memory range.
558 *
559 * The read function may fail on MMIO ranges, we write these as zero
560 * pages for now (would be nice to have the VGA bits there though).
561 */
562 uint64_t cbMemRange = GCPhysEnd - GCPhysStart + 1;
563 uint64_t cPages = cbMemRange >> GUEST_PAGE_SHIFT;
564 for (uint64_t iPage = 0; iPage < cPages; iPage++)
565 {
566 uint8_t abPage[GUEST_PAGE_SIZE];
567 rc = PGMPhysSimpleReadGCPhys(pVM, abPage, GCPhysStart + (iPage << GUEST_PAGE_SHIFT), sizeof(abPage));
568 if (RT_FAILURE(rc))
569 {
570 if (rc != VERR_PGM_PHYS_PAGE_RESERVED)
571 LogRel((DBGFLOG_NAME ": PGMPhysRead failed for iRange=%u iPage=%u. rc=%Rrc. Ignoring...\n", iRange, iPage, rc));
572 RT_ZERO(abPage);
573 }
574
575 rc = RTFileWrite(hFile, abPage, sizeof(abPage), NULL /* all */);
576 if (RT_FAILURE(rc))
577 {
578 LogRel((DBGFLOG_NAME ": RTFileWrite failed. iRange=%u iPage=%u rc=%Rrc\n", iRange, iPage, rc));
579 return rc;
580 }
581 }
582 }
583
584 return rc;
585}
586
587
588/**
589 * EMT Rendezvous worker function for DBGFR3CoreWrite().
590 *
591 * @param pVM The cross context VM structure.
592 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
593 * @param pvData Opaque data.
594 *
595 * @return VBox status code.
596 */
597static DECLCALLBACK(VBOXSTRICTRC) dbgfR3CoreWriteRendezvous(PVM pVM, PVMCPU pVCpu, void *pvData)
598{
599 /*
600 * Validate input.
601 */
602 AssertReturn(pVM, VERR_INVALID_VM_HANDLE);
603 AssertReturn(pVCpu, VERR_INVALID_VMCPU_HANDLE);
604 AssertReturn(pvData, VERR_INVALID_POINTER);
605
606 PDBGFCOREDATA pDbgfData = (PDBGFCOREDATA)pvData;
607
608 /*
609 * Create the core file.
610 */
611 uint32_t fFlags = (pDbgfData->fReplaceFile ? RTFILE_O_CREATE_REPLACE : RTFILE_O_CREATE)
612 | RTFILE_O_WRITE
613 | RTFILE_O_DENY_ALL
614 | (0600 << RTFILE_O_CREATE_MODE_SHIFT);
615 RTFILE hFile;
616 int rc = RTFileOpen(&hFile, pDbgfData->pszFilename, fFlags);
617 if (RT_SUCCESS(rc))
618 {
619 rc = dbgfR3CoreWriteWorker(pVM, hFile);
620 RTFileClose(hFile);
621 }
622 else
623 LogRel((DBGFLOG_NAME ": RTFileOpen failed for '%s' rc=%Rrc\n", pDbgfData->pszFilename, rc));
624 return rc;
625}
626
627
628/**
629 * Write core dump of the guest.
630 *
631 * @returns VBox status code.
632 * @param pUVM The user mode VM handle.
633 * @param pszFilename The name of the file to which the guest core
634 * dump should be written.
635 * @param fReplaceFile Whether to replace the file or not.
636 *
637 * @remarks The VM may need to be suspended before calling this function in
638 * order to truly stop all device threads and drivers. This function
639 * only synchronizes EMTs.
640 */
641VMMR3DECL(int) DBGFR3CoreWrite(PUVM pUVM, const char *pszFilename, bool fReplaceFile)
642{
643 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
644 PVM pVM = pUVM->pVM;
645 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
646 AssertReturn(pszFilename, VERR_INVALID_HANDLE);
647
648 /*
649 * Pass the core write request down to EMT rendezvous which makes sure
650 * other EMTs, if any, are not running. IO threads could still be running
651 * but we don't care about them.
652 */
653 DBGFCOREDATA CoreData;
654 RT_ZERO(CoreData);
655 CoreData.pszFilename = pszFilename;
656 CoreData.fReplaceFile = fReplaceFile;
657
658 int rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ONCE, dbgfR3CoreWriteRendezvous, &CoreData);
659 if (RT_SUCCESS(rc))
660 LogRel((DBGFLOG_NAME ": Successfully wrote guest core dump '%s'\n", pszFilename));
661 else
662 LogRel((DBGFLOG_NAME ": Failed to write guest core dump '%s'. rc=%Rrc\n", pszFilename, rc));
663 return rc;
664}
665
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette