VirtualBox

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

Last change on this file since 32215 was 32215, checked in by vboxsync, 14 years ago

VMM/DBGFCoreWrite: build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.8 KB
Line 
1/* $Id: DBGFCoreWrite.cpp 32215 2010-09-02 15:27:11Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, Guest Core Dump.
4 */
5
6/*
7 * Copyright (C) 2010 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
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGF
23#include <iprt/param.h>
24#include <iprt/file.h>
25#include <VBox/dbgf.h>
26#include "DBGFInternal.h"
27#include <VBox/vm.h>
28#include <VBox/pgm.h>
29#include <VBox/err.h>
30#include <VBox/log.h>
31#include <VBox/mm.h>
32
33#include "../Runtime/include/internal/ldrELF64.h"
34
35/*******************************************************************************
36* Defined Constants And Macros *
37*******************************************************************************/
38#ifdef DEBUG_ramshankar
39# undef Log
40# define Log LogRel
41#endif
42#define DBGFLOG_NAME "DGBFCoreWrite"
43
44/**
45 * DBGFCOREDATA: Core data.
46 */
47typedef struct
48{
49 const char *pszDumpPath; /* File path to dump the core into. */
50} DBGFCOREDATA, *PDBGFCOREDATA;
51
52
53/*
54 * VBox VMCore Format:
55 * [ ELF 64 Header] -- Only 1
56 *
57 * [ PT_NOTE ] -- Only 1
58 * - Offset into list of Notes (Note Hdr + data) of VBox CPUs.
59 * - (Any Additional custom Note sections)
60 *
61 * [ PT_LOAD ] -- One for each contiguous memory chunk
62 * - Memory offset
63 * - File offset
64 *
65 * Per-CPU register dump
66 * - CPU 1 Note Hdr + Data
67 * - CPU 2 Note Hdr + Data
68 * ...
69 * (Additional custom notes Hdr+data)
70 * - VBox 1 Note Hdr + Data
71 * - VBox 2 Note Hdr + Data
72 * ...
73 * Memory dump
74 *
75 */
76
77/**
78 * ELF function to write 64-bit ELF header.
79 *
80 * @param hFile The file to write to.
81 * @param cProgHdrs Number of program headers.
82 * @param cSecHdrs Number of section headers.
83 * @param pcbElfHdr Where to store the size of written header to file,
84 * can be NULL.
85 *
86 * @return IPRT status code.
87 */
88static int Elf64WriteElfHdr(RTFILE hFile, uint16_t cProgHdrs, uint16_t cSecHdrs, uint64_t *pcbElfHdr)
89{
90AssertCompile(sizeof(uint32_t) == 4);
91
92 Elf64_Ehdr ElfHdr;
93 RT_ZERO(ElfHdr);
94 ElfHdr.e_ident[EI_MAG0] = ELFMAG0;
95 ElfHdr.e_ident[EI_MAG1] = ELFMAG1;
96 ElfHdr.e_ident[EI_MAG2] = ELFMAG2;
97 ElfHdr.e_ident[EI_MAG3] = ELFMAG3;
98 ElfHdr.e_ident[EI_DATA] = ELFDATA2LSB;
99 ElfHdr.e_type = ET_CORE;
100 ElfHdr.e_version = EV_CURRENT;
101 ElfHdr.e_ident[EI_CLASS] = ELFCLASS64;
102 /* 32-bit VMs will produce cores with e_machine EM_386. */
103#ifdef RT_ARCH_AMD64
104 ElfHdr.e_machine = EM_X86_64;
105#else
106 ElfHdr.e_machine = EM_386;
107#endif
108 ElfHdr.e_phnum = cProgHdrs;
109 ElfHdr.e_shnum = cSecHdrs;
110 ElfHdr.e_ehsize = sizeof(ElfHdr);
111 ElfHdr.e_phoff = sizeof(ElfHdr);
112 ElfHdr.e_phentsize = sizeof(Elf64_Phdr);
113 ElfHdr.e_shentsize = sizeof(Elf64_Shdr);
114
115 int rc = RTFileWrite(hFile, &ElfHdr, sizeof(ElfHdr), NULL /* full write */);
116 if (RT_SUCCESS(rc) && pcbElfHdr)
117 *pcbElfHdr = sizeof(ElfHdr);
118 return rc;
119}
120
121
122/**
123 * ELF function to write 64-bit program header.
124 *
125 * @param hFile The file to write to.
126 * @param Type Type of program header (PT_*).
127 * @param fFlags Flags (access permissions, PF_*).
128 * @param offFileData File offset of contents.
129 * @param cbFileData Size of contents in the file.
130 * @param cbMemData Size of contents in memory.
131 * @param Phys Physical address, pass zero if not applicable.
132 * @param pcbProgHdr Where to store the size of written header to file,
133 * can be NULL.
134 *
135 * @return IPRT status code.
136 */
137static int Elf64WriteProgHdr(RTFILE hFile, uint32_t Type, uint32_t fFlags, uint64_t offFileData, uint64_t cbFileData, uint64_t cbMemData,
138 RTGCPHYS Phys, uint64_t *pcbProgHdr)
139{
140 Elf64_Phdr ProgHdr;
141 RT_ZERO(ProgHdr);
142 ProgHdr.p_type = Type;
143 ProgHdr.p_flags = fFlags;
144 ProgHdr.p_offset = offFileData;
145 ProgHdr.p_filesz = cbFileData;
146 ProgHdr.p_memsz = cbMemData;
147 ProgHdr.p_paddr = Phys;
148
149 int rc = RTFileWrite(hFile, &ProgHdr, sizeof(ProgHdr), NULL /* full write */);
150 if (RT_SUCCESS(rc) && pcbProgHdr)
151 *pcbProgHdr = sizeof(ProgHdr);
152 return rc;
153}
154
155
156/**
157 * Elf function to write 64-bit note header.
158 *
159 * @param hFile The file to write to.
160 * @param Type Type of this section.
161 * @param pszName Name of this section, will be limited to 8 bytes.
162 * @param pcv Opaque pointer to the data, if NULL only computes size.
163 * @param cb Size of the data.
164 * @param pcbNoteHdr Where to store the size of written header to file,
165 * can be NULL.
166 *
167 * @return IPRT status code.
168 */
169static int Elf64WriteNoteHeader(RTFILE hFile, uint16_t Type, const char *pszName, const void *pcv, uint64_t cb, uint64_t *pcbNoteHdr)
170{
171 AssertReturn(pcv, VERR_INVALID_POINTER);
172 AssertReturn(cb > 0, VERR_NO_DATA);
173
174 typedef struct
175 {
176 Elf64_Nhdr Hdr; /* 64-bit NOTE Header */
177 char achName[8]; /* Name of NOTE section */
178 } ELFNOTEHDR;
179
180 ELFNOTEHDR ElfNoteHdr;
181 RT_ZERO(ElfNoteHdr);
182 RTStrCopy(ElfNoteHdr.achName, sizeof(ElfNoteHdr.achName) - 1, pszName);
183 ElfNoteHdr.Hdr.n_namesz = strlen(ElfNoteHdr.achName) + 1;
184 ElfNoteHdr.Hdr.n_type = Type;
185
186 static const char s_achPad[3] = { 0, 0, 0 };
187 uint64_t cbAlign = RT_ALIGN_64(cb, 4);
188 ElfNoteHdr.Hdr.n_descsz = cbAlign;
189
190 /*
191 * Write note header and description.
192 */
193 int rc = RTFileWrite(hFile, &ElfNoteHdr, sizeof(ElfNoteHdr), NULL /* full write */);
194 if (RT_SUCCESS(rc))
195 {
196 rc = RTFileWrite(hFile, pcv, cb, NULL /* full write */);
197 if (RT_SUCCESS(rc))
198 {
199 if (cbAlign > cb)
200 rc = RTFileWrite(hFile, s_achPad, cbAlign - cb, NULL /* full write*/);
201 }
202 }
203
204 if (RT_FAILURE(rc))
205 LogRel((DBGFLOG_NAME ":RTFileWrite failed. rc=%Rrc pszName=%s cb=%u cbAlign=%u\n", rc, pszName, cb, cbAlign));
206
207 return rc;
208}
209
210
211/**
212 * Count the number of memory ranges that go into the core file.
213 *
214 * We cannot do a page-by-page dump of the entire guest memory as there will be
215 * way too many program header entries. Also we don't want to dump MMIO regions
216 * which means we cannot have a 1:1 mapping between core file offset and memory
217 * offset. Instead we dump the memory in ranges. A memory range is a contiguous
218 * memory area suitable for dumping to a core file.
219 *
220 * @param pVM The VM handle.
221 *
222 * @return Number of memory ranges
223 */
224static uint32_t dbgfR3GetRamRangeCount(PVM pVM)
225{
226 return PGMR3PhysGetRamRangeCount(pVM);
227}
228
229
230/**
231 * EMT Rendezvous worker function for DBGFR3CoreWrite.
232 *
233 * @param pVM The VM handle.
234 * @param pVCpu The handle of the calling VCPU.
235 * @param pvData Opaque data.
236 *
237 * @return VBox status code.
238 * @remarks The VM must be suspended before calling this function.
239 */
240static DECLCALLBACK(int) dbgfR3CoreWrite(PVM pVM, PVMCPU pVCpu, void *pvData)
241{
242 /*
243 * Validate input.
244 */
245 AssertReturn(pVM, VERR_INVALID_VM_HANDLE);
246 AssertReturn(pVCpu, VERR_INVALID_VMCPU_HANDLE);
247 AssertReturn(pvData, VERR_INVALID_POINTER);
248
249 PDBGFCOREDATA pDbgfData = (PDBGFCOREDATA)pvData;
250
251 /*
252 * Collect core information.
253 */
254 uint32_t u32MemRanges = dbgfR3GetRamRangeCount(pVM);
255 uint16_t cMemRanges = u32MemRanges < UINT16_MAX - 1 ? u32MemRanges : UINT16_MAX - 1; /* One PT_NOTE Program header */
256 uint16_t cProgHdrs = cMemRanges + 1;
257
258 /*
259 * Compute size of the note section.
260 */
261 uint64_t cbNoteSection = pVM->cCpus * sizeof(CPUMCTX);
262 uint64_t off = 0;
263
264 /*
265 * Create the core file.
266 */
267 RTFILE hFile = NIL_RTFILE;
268 int rc = RTFileOpen(&hFile, pDbgfData->pszDumpPath, RTFILE_O_CREATE_REPLACE | RTFILE_O_READWRITE);
269 if (RT_SUCCESS(rc))
270 {
271 /*
272 * Write ELF header.
273 */
274 uint64_t cbElfHdr = 0;
275 rc = Elf64WriteElfHdr(hFile, cProgHdrs, 0 /* cSecHdrs */, &cbElfHdr);
276 off += cbElfHdr;
277 if (RT_SUCCESS(rc))
278 {
279 /*
280 * Write PT_NOTE program header.
281 */
282 uint64_t cbProgHdr = 0;
283 rc = Elf64WriteProgHdr(hFile, PT_NOTE, PF_R,
284 cbElfHdr + cProgHdrs * sizeof(Elf64_Phdr), /* file offset to contents */
285 cbNoteSection, /* size in core file */
286 cbNoteSection, /* size in memory */
287 0, /* physical address */
288 &cbProgHdr);
289 Assert(cbProgHdr == sizeof(Elf64_Phdr));
290 off += cbProgHdr;
291 if (RT_SUCCESS(rc))
292 {
293 /*
294 * Write PT_LOAD program header for each memory range.
295 */
296 uint64_t offMemRange = off + cbNoteSection;
297 for (uint16_t iRange = 0; iRange < cMemRanges; iRange++)
298 {
299 RTGCPHYS GCPhysStart;
300 RTGCPHYS GCPhysEnd;
301
302 bool fIsMmio;
303 rc = PGMR3PhysGetRange(pVM, iRange, &GCPhysStart, &GCPhysEnd, NULL /* pszDesc */, &fIsMmio);
304 if (RT_FAILURE(rc))
305 {
306 LogRel((DBGFLOG_NAME ": PGMR3PhysGetRange failed for iRange(%u) rc=%Rrc\n", iRange, rc));
307 break;
308 }
309
310 uint64_t cbMemRange = GCPhysEnd - GCPhysStart + 1;
311 uint64_t cbFileRange = fIsMmio ? 0 : cbMemRange;
312
313 LogRel((DBGFLOG_NAME ": PGMR3PhysGetRange iRange=%u GCPhysStart=%#x GCPhysEnd=%#x cbMemRange=%u\n",
314 iRange, GCPhysStart, GCPhysEnd, cbMemRange));
315
316 rc = Elf64WriteProgHdr(hFile, PT_LOAD, PF_R,
317 offMemRange, /* file offset to contents */
318 cbFileRange, /* size in core file */
319 cbMemRange, /* size in memory */
320 GCPhysStart, /* physical address */
321 &cbProgHdr);
322 Assert(cbProgHdr == sizeof(Elf64_Phdr));
323 if (RT_FAILURE(rc))
324 {
325 LogRel((DBGFLOG_NAME ":Elf64WriteProgHdr failed for memory range(%u) cbFileRange=%u cbMemRange=%u rc=%Rrc\n", iRange,
326 cbFileRange, cbMemRange, rc));
327 break;
328 }
329
330 offMemRange += cbFileRange;
331 }
332
333 /*
334 * Write the CPU context note headers and data.
335 */
336 if (RT_SUCCESS(rc))
337 {
338 for (uint32_t iCpu = 0; iCpu < pVM->cCpus; iCpu++)
339 {
340 /** @todo -XXX- cpus */
341 }
342 }
343 }
344
345 }
346
347 RTFileClose(hFile);
348 }
349
350 return rc;
351}
352
353
354/**
355 * Write core dump of the guest.
356 *
357 * @return VBox status code.
358 * @param pVM The VM handle.
359 * @param idCpu The target CPU ID.
360 * @param pszDumpPath The path of the file to dump into, cannot be
361 * NULL.
362 *
363 * @remarks The VM must be suspended before calling this function.
364 */
365VMMR3DECL(int) DBGFR3CoreWrite(PVM pVM, VMCPUID idCpu, const char *pszDumpPath)
366{
367 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
368 AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID);
369 AssertReturn(pszDumpPath, VERR_INVALID_HANDLE);
370
371 /*
372 * Pass the core write request down to EMT rendezvous which makes sure
373 * other EMTs, if any, are not running.
374 */
375 DBGFCOREDATA CoreData;
376 RT_ZERO(CoreData);
377 CoreData.pszDumpPath = pszDumpPath;
378
379 return VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ONCE, dbgfR3CoreWrite, &CoreData);
380}
381
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