/* $Id: VDDbgIoLog.cpp 44529 2013-02-04 15:54:15Z vboxsync $ */ /** @file * * VD Debug library - I/O logger. */ /* * Copyright (C) 2011-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /******************************************************************************* * Header Files * *******************************************************************************/ #define LOGGROUP LOGGROUP_DEFAULT #include #include #include #include #include #include #include #include #include /******************************************************************************* * Structures in a I/O log file, little endian * *******************************************************************************/ /** * I/O log header. */ #pragma pack(1) typedef struct IoLogHeader { /** Magic string */ char szMagic[8]; /** Flags for the log file. */ uint32_t fFlags; /** Id counter. */ uint64_t u64Id; } IoLogHeader; #pragma pack() #define VDIOLOG_MAGIC "VDIOLOG" /** Event type - I/O request start. */ #define VDIOLOG_EVENT_START 0x01 /** Event type - I/O request complete. */ #define VDIOLOG_EVENT_COMPLETE 0x02 /** * I/O log entry marking the start of a new I/O transaction. */ #pragma pack(1) typedef struct IoLogEntryStart { /** Event type. */ uint32_t u32Type; /** Transfer type. */ uint32_t u32ReqType; /** Flag whether this is a sync or async request. */ uint8_t u8AsyncIo; /** Id of the entry. */ uint64_t u64Id; /** Type dependent data. */ union { /** I/O. */ struct { /** Start offset. */ uint64_t u64Off; /** Size of the request. */ uint64_t u64IoSize; } Io; /** Discard */ struct { /** Number of ranges to discard. */ uint32_t cRanges; } Discard; }; } IoLogEntryStart; #pragma pack() /** * I/O log entry markign the completion of an I/O transaction. */ #pragma pack(1) typedef struct IoLogEntryComplete { /** Event type. */ uint32_t u32Type; /** Id of the matching start entry. */ uint64_t u64Id; /** Status code the request completed with */ int32_t i32Rc; /** Number of milliseconds the request needed to complete. */ uint64_t msDuration; /** Number of bytes of data following this entry. */ uint64_t u64IoBuffer; } IoLogEntryComplete; #pragma pack() #pragma pack(1) typedef struct IoLogEntryDiscard { /** Start offset. */ uint64_t u64Off; /** Number of bytes to discard. */ uint32_t u32Discard; } IoLogEntryDiscard; #pragma pack() /******************************************************************************* * Constants And Macros, Structures and Typedefs * *******************************************************************************/ /** * I/O logger instance data. */ typedef struct VDIOLOGGERINT { /** File handle. */ RTFILE hFile; /** Current offset to append new entries to. */ uint64_t offWriteNext; /** Offset to read the next entry from. */ uint64_t offReadNext; /** Flags given during creation. */ uint32_t fFlags; /** Id for the next entry. */ uint64_t idNext; /** Memory cache for the I/O log entries. */ RTMEMCACHE hMemCacheIoLogEntries; /** Mutex section protecting the logger. */ RTSEMFASTMUTEX hMtx; /** Cached event type of the next event. */ uint32_t u32EventTypeNext; /** Cached request type of the next request. */ VDDBGIOLOGREQ enmReqTypeNext; } VDIOLOGGERINT; /** Pointer to the internal I/O logger instance data. */ typedef VDIOLOGGERINT *PVDIOLOGGERINT; /** * I/O log entry data. */ typedef struct VDIOLOGENTINT { /** Id of the start entry. */ uint64_t idStart; /** Timestamnp when the request started. */ uint64_t tsStart; /** Size of the buffer to write on success. */ size_t cbIo; } VDIOLOGENTINT; /** Pointer to the internal I/O log entry data. */ typedef VDIOLOGENTINT *PVDIOLOGENTINT; /******************************************************************************* * Internal Functions * *******************************************************************************/ /** * Creates a new empty I/O logger. * * @returns VBox status code. * @param ppIoLogger Where to store the new I/O logger handle. */ static int vddbgIoLoggerCreate(PVDIOLOGGERINT *ppIoLogger) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = NULL; pIoLogger = (PVDIOLOGGERINT)RTMemAllocZ(sizeof(VDIOLOGGERINT)); if (pIoLogger) { rc = RTSemFastMutexCreate(&pIoLogger->hMtx); if (RT_SUCCESS(rc)) { rc = RTMemCacheCreate(&pIoLogger->hMemCacheIoLogEntries, sizeof(VDIOLOGENTINT), 0, UINT32_MAX, NULL, NULL, NULL, 0); if (RT_SUCCESS(rc)) { *ppIoLogger = pIoLogger; return rc; } } RTMemFree(pIoLogger); } else rc = VERR_NO_MEMORY; return rc; } /** * Update the header of the I/O logger to the current state. * * @returns VBox status code. * @param pIoLogger The I/O logger to update. */ static int vddbgIoLoggerHeaderUpdate(PVDIOLOGGERINT pIoLogger) { int rc = VINF_SUCCESS; IoLogHeader Hdr; memcpy(Hdr.szMagic, VDIOLOG_MAGIC, sizeof(Hdr.szMagic)); Hdr.fFlags = RT_H2LE_U32(pIoLogger->fFlags); Hdr.u64Id = RT_H2LE_U64(pIoLogger->idNext); rc = RTFileWriteAt(pIoLogger->hFile, 0, &Hdr, sizeof(Hdr), NULL); return rc; } /** * Writes data from the given S/G buffer into the I/O log. * * @returns VBox status code. * @param pIoLogger The I/O logger to use. * @param off The start offset in the log to write to. * @param pSgBuf The S/G buffer to write. * @param cbSgBuf How much data to write. */ static int vddbgIoLogWriteSgBuf(PVDIOLOGGERINT pIoLogger, uint64_t off, PCRTSGBUF pSgBuf, size_t cbSgBuf) { int rc = VINF_SUCCESS; RTSGBUF SgBuf; RTSgBufClone(&SgBuf, pSgBuf); while (cbSgBuf) { void *pvSeg; size_t cbSeg = cbSgBuf; pvSeg = RTSgBufGetNextSegment(&SgBuf, &cbSeg); AssertPtrBreakStmt(pvSeg, rc = VERR_INTERNAL_ERROR); rc = RTFileWriteAt(pIoLogger->hFile, off, pvSeg, cbSeg, NULL); if (RT_FAILURE(rc)) break; cbSgBuf -= cbSeg; off += cbSeg; } return rc; } VBOXDDU_DECL(int) VDDbgIoLogCreate(PVDIOLOGGER phIoLogger, const char *pszFilename, uint32_t fFlags) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = NULL; AssertPtrReturn(phIoLogger, VERR_INVALID_POINTER); AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); AssertReturn(!(fFlags & ~VDDBG_IOLOG_VALID_MASK), VERR_INVALID_PARAMETER); rc = vddbgIoLoggerCreate(&pIoLogger); if (RT_SUCCESS(rc)) { pIoLogger->fFlags = fFlags; pIoLogger->hFile = NIL_RTFILE; /* Create new log. */ rc = RTFileOpen(&pIoLogger->hFile, pszFilename, RTFILE_O_DENY_NONE | RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_READ); if (RT_SUCCESS(rc)) { rc = vddbgIoLoggerHeaderUpdate(pIoLogger); if (RT_SUCCESS(rc)) { pIoLogger->offWriteNext = sizeof(IoLogHeader); pIoLogger->offReadNext = sizeof(IoLogHeader); } } if (RT_SUCCESS(rc)) *phIoLogger = pIoLogger; else { if (pIoLogger->hFile != NIL_RTFILE) RTFileClose(pIoLogger->hFile); RTMemFree(pIoLogger); } } return rc; } VBOXDDU_DECL(int) VDDbgIoLogOpen(PVDIOLOGGER phIoLogger, const char *pszFilename) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = NULL; AssertPtrReturn(phIoLogger, VERR_INVALID_POINTER); AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); rc = vddbgIoLoggerCreate(&pIoLogger); if (RT_SUCCESS(rc)) { /* open existing log. */ rc = RTFileOpen(&pIoLogger->hFile, pszFilename, RTFILE_O_DENY_NONE | RTFILE_O_OPEN | RTFILE_O_WRITE | RTFILE_O_READ); if (RT_SUCCESS(rc)) { IoLogHeader Hdr; uint64_t cbLog; rc = RTFileGetSize(pIoLogger->hFile, &cbLog); /* Read the header. */ if (RT_SUCCESS(rc)) rc = RTFileRead(pIoLogger->hFile, &Hdr, sizeof(Hdr), NULL); if ( RT_SUCCESS(rc) && !memcmp(Hdr.szMagic, VDIOLOG_MAGIC, sizeof(Hdr.szMagic))) { pIoLogger->fFlags = RT_LE2H_U32(Hdr.fFlags); pIoLogger->offWriteNext = cbLog; pIoLogger->offReadNext = sizeof(Hdr); pIoLogger->idNext = RT_LE2H_U64(Hdr.u64Id); *phIoLogger = pIoLogger; } else if (RT_SUCCESS(rc)) rc = VERR_INVALID_PARAMETER; } } return rc; } VBOXDDU_DECL(void) VDDbgIoLogDestroy(VDIOLOGGER hIoLogger) { PVDIOLOGGERINT pIoLogger = hIoLogger; AssertPtrReturnVoid(pIoLogger); vddbgIoLoggerHeaderUpdate(pIoLogger); RTFileFlush(pIoLogger->hFile); RTFileClose(pIoLogger->hFile); RTMemCacheDestroy(pIoLogger->hMemCacheIoLogEntries); RTSemFastMutexDestroy(pIoLogger->hMtx); RTMemFree(pIoLogger); } VBOXDDU_DECL(int) VDDbgIoLogCommit(VDIOLOGGER hIoLogger) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = hIoLogger; AssertPtrReturn(pIoLogger, VERR_INVALID_HANDLE); rc = vddbgIoLoggerHeaderUpdate(pIoLogger); if (RT_SUCCESS(rc)) rc = RTFileFlush(pIoLogger->hFile); return rc; } VBOXDDU_DECL(uint32_t) VDDbgIoLogGetFlags(VDIOLOGGER hIoLogger) { PVDIOLOGGERINT pIoLogger = hIoLogger; AssertPtrReturn(pIoLogger, 0); return pIoLogger->fFlags; } VBOXDDU_DECL(int) VDDbgIoLogStart(VDIOLOGGER hIoLogger, bool fAsync, VDDBGIOLOGREQ enmTxDir, uint64_t off, size_t cbIo, PCRTSGBUF pSgBuf, PVDIOLOGENT phIoLogEntry) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = hIoLogger; PVDIOLOGENTINT pIoLogEntry = NULL; AssertPtrReturn(pIoLogger, VERR_INVALID_HANDLE); AssertPtrReturn(phIoLogEntry, VERR_INVALID_POINTER); AssertReturn(enmTxDir > VDDBGIOLOGREQ_INVALID && enmTxDir <= VDDBGIOLOGREQ_FLUSH, VERR_INVALID_PARAMETER); rc = RTSemFastMutexRequest(pIoLogger->hMtx); AssertRCReturn(rc, rc); pIoLogEntry = (PVDIOLOGENTINT)RTMemCacheAlloc(pIoLogger->hMemCacheIoLogEntries); if (pIoLogEntry) { IoLogEntryStart Entry; pIoLogEntry->idStart = pIoLogger->idNext++; Entry.u32Type = VDIOLOG_EVENT_START; Entry.u8AsyncIo = fAsync ? 1 : 0; Entry.u32ReqType = enmTxDir; Entry.u64Id = RT_H2LE_U64(pIoLogEntry->idStart); Entry.Io.u64Off = RT_H2LE_U64(off); Entry.Io.u64IoSize = RT_H2LE_U64(cbIo); /* Write new entry. */ rc = RTFileWriteAt(pIoLogger->hFile, pIoLogger->offWriteNext, &Entry, sizeof(Entry), NULL); if (RT_SUCCESS(rc)) { pIoLogger->offWriteNext += sizeof(Entry); if ( enmTxDir == VDDBGIOLOGREQ_WRITE && (pIoLogger->fFlags & VDDBG_IOLOG_LOG_DATA_WRITTEN)) { /* Write data. */ rc = vddbgIoLogWriteSgBuf(pIoLogger, pIoLogger->offWriteNext, pSgBuf, cbIo); if (RT_FAILURE(rc)) { pIoLogger->offWriteNext -= sizeof(Entry); rc = RTFileSetSize(pIoLogger->hFile, pIoLogger->offWriteNext); } else pIoLogger->offWriteNext += cbIo; } } if (RT_SUCCESS(rc)) { pIoLogEntry->tsStart = RTTimeProgramMilliTS(); if ( enmTxDir == VDDBGIOLOGREQ_READ && (pIoLogger->fFlags & VDDBG_IOLOG_LOG_DATA_READ)) pIoLogEntry->cbIo = cbIo; else pIoLogEntry->cbIo = 0; *phIoLogEntry = pIoLogEntry; } else { pIoLogger->idNext--; RTMemCacheFree(pIoLogger->hMemCacheIoLogEntries, pIoLogEntry); } } else rc = VERR_NO_MEMORY; RTSemFastMutexRelease(pIoLogger->hMtx); return rc; } VBOXDDU_DECL(int) VDDbgIoLogStartDiscard(VDIOLOGGER hIoLogger, bool fAsync, PCRTRANGE paRanges, unsigned cRanges, PVDIOLOGENT phIoLogEntry) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = hIoLogger; PVDIOLOGENTINT pIoLogEntry = NULL; AssertPtrReturn(pIoLogger, VERR_INVALID_HANDLE); AssertPtrReturn(phIoLogEntry, VERR_INVALID_POINTER); rc = RTSemFastMutexRequest(pIoLogger->hMtx); AssertRCReturn(rc, rc); pIoLogEntry = (PVDIOLOGENTINT)RTMemCacheAlloc(pIoLogger->hMemCacheIoLogEntries); if (pIoLogEntry) { IoLogEntryStart Entry; pIoLogEntry->idStart = pIoLogger->idNext++; Entry.u32Type = VDIOLOG_EVENT_START; Entry.u8AsyncIo = fAsync ? 1 : 0; Entry.u32ReqType = VDDBGIOLOGREQ_DISCARD; Entry.u64Id = RT_H2LE_U64(pIoLogEntry->idStart); Entry.Discard.cRanges = RT_H2LE_U32(cRanges); /* Write new entry. */ rc = RTFileWriteAt(pIoLogger->hFile, pIoLogger->offWriteNext, &Entry, sizeof(Entry), NULL); if (RT_SUCCESS(rc)) { pIoLogger->offWriteNext += sizeof(Entry); IoLogEntryDiscard DiscardRange; for (unsigned i = 0; i < cRanges; i++) { DiscardRange.u64Off = RT_H2LE_U64(paRanges[i].offStart); DiscardRange.u32Discard = RT_H2LE_U32(paRanges[i].cbRange); rc = RTFileWriteAt(pIoLogger->hFile, pIoLogger->offWriteNext + i*sizeof(DiscardRange), &DiscardRange, sizeof(DiscardRange), NULL); if (RT_FAILURE(rc)) break; } if (RT_FAILURE(rc)) { pIoLogger->offWriteNext -= sizeof(Entry); rc = RTFileSetSize(pIoLogger->hFile, pIoLogger->offWriteNext); } else pIoLogger->offWriteNext += cRanges * sizeof(DiscardRange); } if (RT_SUCCESS(rc)) { pIoLogEntry->tsStart = RTTimeProgramMilliTS(); pIoLogEntry->cbIo = 0; *phIoLogEntry = pIoLogEntry; } else { pIoLogger->idNext--; RTMemCacheFree(pIoLogger->hMemCacheIoLogEntries, pIoLogEntry); } } else rc = VERR_NO_MEMORY; RTSemFastMutexRelease(pIoLogger->hMtx); return rc; } VBOXDDU_DECL(int) VDDbgIoLogComplete(VDIOLOGGER hIoLogger, VDIOLOGENT hIoLogEntry, int rcReq, PCRTSGBUF pSgBuf) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = hIoLogger; PVDIOLOGENTINT pIoLogEntry = hIoLogEntry; AssertPtrReturn(pIoLogger, VERR_INVALID_HANDLE); AssertPtrReturn(pIoLogEntry, VERR_INVALID_HANDLE); rc = RTSemFastMutexRequest(pIoLogger->hMtx); AssertRCReturn(rc, rc); IoLogEntryComplete Entry; Entry.u32Type = VDIOLOG_EVENT_COMPLETE; Entry.u64Id = RT_H2LE_U64(pIoLogEntry->idStart); Entry.msDuration = RTTimeProgramMilliTS() - RT_H2LE_U64(pIoLogEntry->tsStart); Entry.i32Rc = (int32_t)RT_H2LE_U32((uint32_t)rcReq); Entry.u64IoBuffer = RT_H2LE_U64(pIoLogEntry->cbIo); /* Write new entry. */ rc = RTFileWriteAt(pIoLogger->hFile, pIoLogger->offWriteNext, &Entry, sizeof(Entry), NULL); if (RT_SUCCESS(rc)) { pIoLogger->offWriteNext += sizeof(Entry); if (pIoLogEntry->cbIo) { rc = vddbgIoLogWriteSgBuf(pIoLogger, pIoLogger->offWriteNext, pSgBuf, pIoLogEntry->cbIo); if (RT_SUCCESS(rc)) pIoLogger->offWriteNext += pIoLogEntry->cbIo; else { pIoLogger->offWriteNext -= sizeof(Entry); rc = RTFileSetSize(pIoLogger->hFile, pIoLogger->offWriteNext); } } } RTMemCacheFree(pIoLogger->hMemCacheIoLogEntries, pIoLogEntry); RTSemFastMutexRelease(pIoLogger->hMtx); return rc; } VBOXDDU_DECL(int) VDDbgIoLogEventTypeGetNext(VDIOLOGGER hIoLogger, VDIOLOGEVENT *penmEvent) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = hIoLogger; AssertPtrReturn(pIoLogger, VERR_INVALID_HANDLE); AssertPtrReturn(penmEvent, VERR_INVALID_POINTER); rc = RTSemFastMutexRequest(pIoLogger->hMtx); AssertRCReturn(rc, rc); if (pIoLogger->offReadNext == pIoLogger->offWriteNext) { *penmEvent = VDIOLOGEVENT_END; RTSemFastMutexRelease(pIoLogger->hMtx); return VINF_SUCCESS; } if (!pIoLogger->u32EventTypeNext) { uint32_t abBuf[2]; rc = RTFileReadAt(pIoLogger->hFile, pIoLogger->offReadNext, &abBuf, sizeof(abBuf), NULL); if (RT_SUCCESS(rc)) { pIoLogger->u32EventTypeNext = abBuf[0]; pIoLogger->enmReqTypeNext = (VDDBGIOLOGREQ)abBuf[1]; } } if (RT_SUCCESS(rc)) { Assert(pIoLogger->u32EventTypeNext != VDIOLOGEVENT_INVALID); switch (pIoLogger->u32EventTypeNext) { case VDIOLOG_EVENT_START: *penmEvent = VDIOLOGEVENT_START; break; case VDIOLOG_EVENT_COMPLETE: *penmEvent = VDIOLOGEVENT_COMPLETE; break; default: AssertMsgFailed(("Invalid event type %d\n", pIoLogger->u32EventTypeNext)); } } RTSemFastMutexRelease(pIoLogger->hMtx); return rc; } VBOXDDU_DECL(int) VDDbgIoLogReqTypeGetNext(VDIOLOGGER hIoLogger, PVDDBGIOLOGREQ penmReq) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = hIoLogger; AssertPtrReturn(pIoLogger, VERR_INVALID_HANDLE); AssertPtrReturn(penmReq, VERR_INVALID_POINTER); rc = RTSemFastMutexRequest(pIoLogger->hMtx); AssertRCReturn(rc, rc); if (pIoLogger->offReadNext == pIoLogger->offWriteNext) { *penmReq = VDDBGIOLOGREQ_INVALID; RTSemFastMutexRelease(pIoLogger->hMtx); return VERR_INVALID_STATE; } if (RT_SUCCESS(rc)) { Assert(pIoLogger->enmReqTypeNext != VDDBGIOLOGREQ_INVALID); *penmReq = pIoLogger->enmReqTypeNext; } RTSemFastMutexRelease(pIoLogger->hMtx); return rc; } VBOXDDU_DECL(int) VDDbgIoLogEventGetStart(VDIOLOGGER hIoLogger, uint64_t *pidEvent, bool *pfAsync, uint64_t *poff, size_t *pcbIo, size_t cbBuf, void *pvBuf) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = hIoLogger; AssertPtrReturn(pIoLogger, VERR_INVALID_HANDLE); AssertPtrReturn(pidEvent, VERR_INVALID_POINTER); AssertPtrReturn(pfAsync, VERR_INVALID_POINTER); AssertPtrReturn(poff, VERR_INVALID_POINTER); AssertPtrReturn(pcbIo, VERR_INVALID_POINTER); rc = RTSemFastMutexRequest(pIoLogger->hMtx); AssertRCReturn(rc, rc); if (pIoLogger->u32EventTypeNext == VDIOLOG_EVENT_START) { IoLogEntryStart Entry; rc = RTFileReadAt(pIoLogger->hFile, pIoLogger->offReadNext, &Entry, sizeof(Entry), NULL); if (RT_SUCCESS(rc)) { *pfAsync = (bool)Entry.u8AsyncIo; *pidEvent = RT_LE2H_U64(Entry.u64Id); *poff = RT_LE2H_U64(Entry.Io.u64Off); *pcbIo = RT_LE2H_U64(Entry.Io.u64IoSize); if ( pIoLogger->enmReqTypeNext == VDDBGIOLOGREQ_WRITE && (pIoLogger->fFlags & VDDBG_IOLOG_LOG_DATA_WRITTEN)) { /* Read data. */ if (cbBuf < *pcbIo) rc = VERR_BUFFER_OVERFLOW; else rc = RTFileReadAt(pIoLogger->hFile, pIoLogger->offReadNext + sizeof(Entry), pvBuf, *pcbIo, NULL); if (rc != VERR_BUFFER_OVERFLOW) pIoLogger->offReadNext += *pcbIo + sizeof(Entry); } else pIoLogger->offReadNext += sizeof(Entry); } } else rc = VERR_INVALID_STATE; if (RT_SUCCESS(rc)) pIoLogger->u32EventTypeNext = 0; RTSemFastMutexRelease(pIoLogger->hMtx); return rc; } VBOXDDU_DECL(int) VDDbgIoLogEventGetStartDiscard(VDIOLOGGER hIoLogger, uint64_t *pidEvent, bool *pfAsync, PRTRANGE *ppaRanges, unsigned *pcRanges) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = hIoLogger; AssertPtrReturn(pIoLogger, VERR_INVALID_HANDLE); AssertPtrReturn(pidEvent, VERR_INVALID_POINTER); AssertPtrReturn(pfAsync, VERR_INVALID_POINTER); rc = RTSemFastMutexRequest(pIoLogger->hMtx); AssertRCReturn(rc, rc); if ( pIoLogger->u32EventTypeNext == VDIOLOG_EVENT_START && pIoLogger->enmReqTypeNext == VDDBGIOLOGREQ_DISCARD) { IoLogEntryStart Entry; rc = RTFileReadAt(pIoLogger->hFile, pIoLogger->offReadNext, &Entry, sizeof(Entry), NULL); if (RT_SUCCESS(rc)) { PRTRANGE paRanges = NULL; IoLogEntryDiscard DiscardRange; pIoLogger->offReadNext += sizeof(Entry); *pfAsync = (bool)Entry.u8AsyncIo; *pidEvent = RT_LE2H_U64(Entry.u64Id); *pcRanges = RT_LE2H_U32(Entry.Discard.cRanges); paRanges = (PRTRANGE)RTMemAllocZ(*pcRanges * sizeof(RTRANGE)); if (paRanges) { for (unsigned i = 0; i < *pcRanges; i++) { rc = RTFileReadAt(pIoLogger->hFile, pIoLogger->offReadNext + i*sizeof(DiscardRange), &DiscardRange, sizeof(DiscardRange), NULL); if (RT_FAILURE(rc)) break; paRanges[i].offStart = RT_LE2H_U64(DiscardRange.u64Off); paRanges[i].cbRange = RT_LE2H_U32(DiscardRange.u32Discard); } if (RT_SUCCESS(rc)) { pIoLogger->offReadNext += *pcRanges * sizeof(DiscardRange); *ppaRanges = paRanges; } else pIoLogger->offReadNext -= sizeof(Entry); } else rc = VERR_NO_MEMORY; } } else rc = VERR_INVALID_STATE; if (RT_SUCCESS(rc)) pIoLogger->u32EventTypeNext = 0; RTSemFastMutexRelease(pIoLogger->hMtx); return rc; } VBOXDDU_DECL(int) VDDbgIoLogEventGetComplete(VDIOLOGGER hIoLogger, uint64_t *pidEvent, int *pRc, uint64_t *pmsDuration, size_t *pcbIo, size_t cbBuf, void *pvBuf) { int rc = VINF_SUCCESS; PVDIOLOGGERINT pIoLogger = hIoLogger; AssertPtrReturn(pIoLogger, VERR_INVALID_HANDLE); AssertPtrReturn(pidEvent, VERR_INVALID_POINTER); AssertPtrReturn(pmsDuration, VERR_INVALID_POINTER); AssertPtrReturn(pcbIo, VERR_INVALID_POINTER); rc = RTSemFastMutexRequest(pIoLogger->hMtx); AssertRCReturn(rc, rc); if (pIoLogger->u32EventTypeNext == VDIOLOG_EVENT_COMPLETE) { IoLogEntryComplete Entry; rc = RTFileReadAt(pIoLogger->hFile, pIoLogger->offReadNext, &Entry, sizeof(Entry), NULL); if (RT_SUCCESS(rc)) { *pidEvent = RT_LE2H_U64(Entry.u64Id); *pRc = (int)RT_LE2H_U32((int32_t)Entry.i32Rc); *pmsDuration = RT_LE2H_U64(Entry.msDuration); *pcbIo = RT_LE2H_U64(Entry.u64IoBuffer); if (*pcbIo) { /* Read data. */ if (cbBuf < *pcbIo) rc = VERR_BUFFER_OVERFLOW; else rc = RTFileReadAt(pIoLogger->hFile, pIoLogger->offReadNext + sizeof(Entry), pvBuf, *pcbIo, NULL); if (rc != VERR_BUFFER_OVERFLOW) pIoLogger->offReadNext += *pcbIo + sizeof(Entry); } else pIoLogger->offReadNext += sizeof(Entry); } } else rc = VERR_INVALID_STATE; if (RT_SUCCESS(rc)) pIoLogger->u32EventTypeNext = 0; RTSemFastMutexRelease(pIoLogger->hMtx); return rc; }