/* $Id: tracelogreader.cpp $ */ /** @file * IPRT - Trace log reader. */ /* * Copyright (C) 2018-2019 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. * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the * VirtualBox OSE distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include "internal/iprt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "internal/magics.h" /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** The trace log is malformed. */ #define VERR_TRACELOG_READER_MALFORMED_LOG (-25700) /** The trace log version is not supported. */ #define VERR_TRACELOG_READER_LOG_UNSUPPORTED (-25701) /** The trace log reader iterator reached the end of the event list. */ #define VERR_TRACELOG_READER_ITERATOR_END (-25702) /** Pointer to a trace log reader instance. */ typedef struct RTTRACELOGRDRINT *PRTTRACELOGRDRINT; /** * State enums the trace log reader can be in. */ typedef enum RTTRACELOGRDRSTATE { /** Invalid state. */ RTTRACELOGRDRSTATE_INVALID = 0, /** The header is currently being received. */ RTTRACELOGRDRSTATE_RECV_HDR, /** The header description is being received (if available). */ RTTRACELOGRDRSTATE_RECV_HDR_DESC, /** the magic is being received to decide what to do next. */ RTTRACELOGRDRSTATE_RECV_MAGIC, /** The event descriptor is being received. */ RTTRACELOGRDRSTATE_RECV_EVT_DESC, /** The event descriptor ID is being received. */ RTTRACELOGRDRSTATE_RECV_EVT_DESC_ID, /** The event descriptor description is being received. */ RTTRACELOGRDRSTATE_RECV_EVT_DESC_DESC, /** The event item descriptor is being received. */ RTTRACELOGRDRSTATE_RECV_EVT_ITEM_DESC, /** The event item descriptor name is being received. */ RTTRACELOGRDRSTATE_RECV_EVT_ITEM_DESC_NAME, /** The event item descriptor description is being received. */ RTTRACELOGRDRSTATE_RECV_EVT_ITEM_DESC_DESC, /** The event marker is being received. */ RTTRACELOGRDRSTATE_RECV_EVT_MARKER, /** The event data is being received. */ RTTRACELOGRDRSTATE_RECV_EVT_DATA, /** 32bit hack. */ RTTRACELOGRDRSTATE_32BIT_HACK = 0x7fffffff } RTTRACELOGRDRSTATE; /** Pointer to internal trace log reader event descriptor. */ typedef struct RTTRACELOGRDREVTDESC *PRTTRACELOGRDREVTDESC; /** Pointer to const internal trace log reader event descriptor. */ typedef const RTTRACELOGRDREVTDESC *PCRTTRACELOGRDREVTDESC; /** * Trace log reader event. */ typedef struct RTTRACELOGRDREVTINT { /** List node for the global list of events. */ RTLISTANCHOR NdGlob; /** The trace log reader instance the event belongs to. */ PRTTRACELOGRDRINT pRdr; /** Trace log sequence number. */ uint64_t u64SeqNo; /** Marker time stamp. */ uint64_t u64Ts; /** Pointer to the event descriptor, describing the data layout. */ PCRTTRACELOGRDREVTDESC pEvtDesc; /** Parent group ID if assigned. */ RTTRACELOGEVTGRPID idGrpParent; /** Group ID this event belongs to. */ RTTRACELOGEVTGRPID idGrp; /** Pointer to the array holding the non static raw data size values. */ size_t *pacbRawData; /** Overall event data size in bytes, including non static data. */ size_t cbEvtData; /** Event data, variable in size. */ uint8_t abEvtData[1]; } RTTRACELOGRDREVTINT; /** Pointer to a trace log reader event. */ typedef RTTRACELOGRDREVTINT *PRTTRACELOGRDREVTINT; /** Pointer to a const trace log reader event. */ typedef const RTTRACELOGRDREVTINT *PCRTTRACELOGRDREVTINT; /** * Trace log reader internal event descriptor. */ typedef struct RTTRACELOGRDREVTDESC { /** Overall size of the event data not counting variable raw data items. */ size_t cbEvtData; /** Number of non static raw binary items in the descriptor. */ uint32_t cRawDataNonStatic; /** Current event item descriptor to work on. */ uint32_t idxEvtItemCur; /** Size of the name of the current item to work on. */ size_t cbStrItemName; /** Size of the description of the current item to work on. */ size_t cbStrItemDesc; /** Size of the ID in bytes including the terminator. */ size_t cbStrId; /** Size of the description in bytes including the terminator. */ size_t cbStrDesc; /** Embedded event descriptor. */ RTTRACELOGEVTDESC EvtDesc; /** Array of event item descriptors, variable in size. */ RTTRACELOGEVTITEMDESC aEvtItemDesc[1]; } RTTRACELOGRDREVTDESC; /** * Trace log reader instance data. */ typedef struct RTTRACELOGRDRINT { /** Magic for identification. */ uint32_t u32Magic; /** Stream out callback. */ PFNRTTRACELOGRDRSTREAM pfnStreamIn; /** Stream close callback .*/ PFNRTTRACELOGSTREAMCLOSE pfnStreamClose; /** Opaque user data passed to the stream callback. */ void *pvUser; /** Mutex protecting the structure. */ RTSEMMUTEX hMtx; /** Current state the reader is in. */ RTTRACELOGRDRSTATE enmState; /** Flag whether to convert all inputs to the host endianess. */ bool fConvEndianess; /** String cache for descriptions and IDs. */ RTSTRCACHE hStrCache; /** Size of the description in characters. */ size_t cchDesc; /** Pointer to the description if set. */ const char *pszDesc; /** List of received events. */ RTLISTANCHOR LstEvts; /** Number of event descriptors known. */ uint32_t cEvtDescsCur; /** Maximum number of event descriptors currently fitting into the array. */ uint32_t cEvtDescsMax; /** Pointer to the array of event descriptor pointers. */ PRTTRACELOGRDREVTDESC *papEvtDescs; /** Current event descriptor being initialised. */ PRTTRACELOGRDREVTDESC pEvtDescCur; /** The current event being received. */ PRTTRACELOGRDREVTINT pEvtCur; /** Last seen sequence number. */ uint64_t u64SeqNoLast; /** Size of the scratch buffer holding the received data. */ size_t cbScratch; /** Pointer to the scratch buffer. */ uint8_t *pbScratch; /** Current offset into the scratch buffer to write fetched data to. */ uint32_t offScratch; /** Number of bytes left to receive until processing the data. */ size_t cbRecvLeft; /** Starting timestamp fetched from the header. */ uint64_t u64TsStart; /** Size of the pointer type in the trace log. */ size_t cbTypePtr; /** Size of the size_t type in the trace log. */ size_t cbTypeSize; } RTTRACELOGRDRINT; /** * Internal reader iterator instance data. */ typedef struct RTTRACELOGRDRITINT { /** The reader instance this iterator belongs to. */ PRTTRACELOGRDRINT pRdr; /** The current event. */ PRTTRACELOGRDREVTINT pEvt; } RTTRACELOGRDRITINT; /** Pointer to an internal reader iterator instance. */ typedef RTTRACELOGRDRITINT *PRTTRACELOGRDRITINT; /** * Trace log handler state callback. * * @returns IPRT status code. * @param pThis The trace log reader instance. * @param penmEvt Where to store the event indicator if a user visible event happened. * @param pfContinuePoll Where to store the flag whether to continue polling. */ typedef DECLCALLBACK(int) FNRTTRACELOGRDRSTATEHANDLER(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); /** Pointer to a trace log reader state handler. */ typedef FNRTTRACELOGRDRSTATEHANDLER *PFNRTTRACELOGRDRSTATEHANDLER; /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ static DECLCALLBACK(int) rtTraceLogRdrHdrRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); static DECLCALLBACK(int) rtTraceLogRdrHdrDescRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); static DECLCALLBACK(int) rtTraceLogRdrMagicRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); static DECLCALLBACK(int) rtTraceLogRdrEvtDescRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); static DECLCALLBACK(int) rtTraceLogRdrEvtDescIdRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); static DECLCALLBACK(int) rtTraceLogRdrEvtDescDescriptionRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); static DECLCALLBACK(int) rtTraceLogRdrEvtItemDescRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); static DECLCALLBACK(int) rtTraceLogRdrEvtItemDescNameRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); static DECLCALLBACK(int) rtTraceLogRdrEvtItemDescDescriptionRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); static DECLCALLBACK(int) rtTraceLogRdrEvtMarkerRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); static DECLCALLBACK(int) rtTraceLogRdrEvtDataRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll); /** * State handlers. */ static PFNRTTRACELOGRDRSTATEHANDLER g_apfnStateHandlers[] = { NULL, rtTraceLogRdrHdrRecvd, rtTraceLogRdrHdrDescRecvd, rtTraceLogRdrMagicRecvd, rtTraceLogRdrEvtDescRecvd, rtTraceLogRdrEvtDescIdRecvd, rtTraceLogRdrEvtDescDescriptionRecvd, rtTraceLogRdrEvtItemDescRecvd, rtTraceLogRdrEvtItemDescNameRecvd, rtTraceLogRdrEvtItemDescDescriptionRecvd, rtTraceLogRdrEvtMarkerRecvd, rtTraceLogRdrEvtDataRecvd, NULL }; /** * Wrapper around the stream in callback. * * @returns IPRT status code returned by the stream callback. * @param pThis The trace log reader instance. * @param pvBuf The data to stream. * @param cbBuf Number of bytes to read in. * @param pcbRead Where to store the amount of data read. * @param cMsTimeout How long to wait for something to arrive. */ DECLINLINE(int) rtTraceLogRdrStreamRead(PRTTRACELOGRDRINT pThis, void *pvBuf, size_t cbBuf, size_t *pcbRead, RTMSINTERVAL cMsTimeout) { return pThis->pfnStreamIn(pThis->pvUser, pvBuf, cbBuf, pcbRead, cMsTimeout); } /** * Converts the header endianess to the host endianess. * * @returns nothing. * @param pHdr The trace log header to convert. */ static void rtTraceLogRdrHdrEndianessConv(PTRACELOGHDR pHdr) { pHdr->u32Endianess = RT_BSWAP_U32(pHdr->u32Endianess); pHdr->u32Version = RT_BSWAP_U32(pHdr->u32Version); pHdr->fFlags = RT_BSWAP_U32(pHdr->fFlags); pHdr->cbStrDesc = RT_BSWAP_U32(pHdr->cbStrDesc); pHdr->u64TsStart = RT_BSWAP_U64(pHdr->u64TsStart); } /** * Converts the event descriptor endianess to the host endianess. * * @returns nothing. * @param pEvtDesc The trace log event descriptor to convert. */ static void rtTraceLogRdrEvtDescEndianessConv(PTRACELOGEVTDESC pEvtDesc) { pEvtDesc->u32Id = RT_BSWAP_U32(pEvtDesc->u32Id); pEvtDesc->u32Severity = RT_BSWAP_U32(pEvtDesc->u32Severity); pEvtDesc->cbStrId = RT_BSWAP_U32(pEvtDesc->cbStrId); pEvtDesc->cbStrDesc = RT_BSWAP_U32(pEvtDesc->cbStrDesc); pEvtDesc->cEvtItems = RT_BSWAP_U32(pEvtDesc->cEvtItems); } /** * Converts the event item descriptor endianess to host endianess. * * @returns nothing. * @param pEvtItemDesc The trace log event item descriptor to convert. */ static void rtTraceLogRdrEvtItemDescEndianessConv(PTRACELOGEVTITEMDESC pEvtItemDesc) { pEvtItemDesc->cbStrName = RT_BSWAP_U32(pEvtItemDesc->cbStrName); pEvtItemDesc->cbStrDesc = RT_BSWAP_U32(pEvtItemDesc->cbStrDesc); pEvtItemDesc->u32Type = RT_BSWAP_U32(pEvtItemDesc->u32Type); pEvtItemDesc->cbRawData = RT_BSWAP_U32(pEvtItemDesc->cbRawData); } /** * Converts the event marker endianess to host endianess. * * @returns nothing. * @param pEvt The trace log event marker to convert. */ static void rtTraceLogRdrEvtEndianessConv(PTRACELOGEVT pEvt) { pEvt->u64SeqNo = RT_BSWAP_U64(pEvt->u64SeqNo); pEvt->u64Ts = RT_BSWAP_U64(pEvt->u64Ts); pEvt->u64EvtGrpId = RT_BSWAP_U64(pEvt->u64EvtGrpId); pEvt->u64EvtParentGrpId = RT_BSWAP_U64(pEvt->u64EvtParentGrpId); pEvt->fFlags = RT_BSWAP_U32(pEvt->fFlags); pEvt->u32EvtDescId = RT_BSWAP_U32(pEvt->u32EvtDescId); pEvt->cbEvtData = RT_BSWAP_U32(pEvt->cbEvtData); pEvt->cRawEvtDataSz = RT_BSWAP_U32(pEvt->cRawEvtDataSz); } /** * Converts severity field from stream to API value. * * @returns API severity enum, RTTRACELOGEVTSEVERITY_INVALID if the supplied stream value * is invalid. * @param u32Severity The severity value from the stream. */ static RTTRACELOGEVTSEVERITY rtTraceLogRdrConvSeverity(uint32_t u32Severity) { RTTRACELOGEVTSEVERITY enmSeverity = RTTRACELOGEVTSEVERITY_INVALID; switch (u32Severity) { case TRACELOG_EVTDESC_SEVERITY_INFO: enmSeverity = RTTRACELOGEVTSEVERITY_INFO; break; case TRACELOG_EVTDESC_SEVERITY_WARNING: enmSeverity = RTTRACELOGEVTSEVERITY_WARNING; break; case TRACELOG_EVTDESC_SEVERITY_ERROR: enmSeverity = RTTRACELOGEVTSEVERITY_ERROR; break; case TRACELOG_EVTDESC_SEVERITY_FATAL: enmSeverity = RTTRACELOGEVTSEVERITY_FATAL; break; case TRACELOG_EVTDESC_SEVERITY_DEBUG: enmSeverity = RTTRACELOGEVTSEVERITY_DEBUG; break; default: enmSeverity = RTTRACELOGEVTSEVERITY_INVALID; } return enmSeverity; } /** * Converts type field from stream to API value. * * @returns API type enum, RTTRACELOGTYPE_INVALID if the supplied stream value * is invalid. * @param u32Type The type value from the stream. */ static RTTRACELOGTYPE rtTraceLogRdrConvType(uint32_t u32Type) { RTTRACELOGTYPE enmType = RTTRACELOGTYPE_INVALID; switch (u32Type) { case TRACELOG_EVTITEMDESC_TYPE_BOOL: enmType = RTTRACELOGTYPE_BOOL; break; case TRACELOG_EVTITEMDESC_TYPE_UINT8: enmType = RTTRACELOGTYPE_UINT8; break; case TRACELOG_EVTITEMDESC_TYPE_INT8: enmType = RTTRACELOGTYPE_INT8; break; case TRACELOG_EVTITEMDESC_TYPE_UINT16: enmType = RTTRACELOGTYPE_UINT16; break; case TRACELOG_EVTITEMDESC_TYPE_INT16: enmType = RTTRACELOGTYPE_INT16; break; case TRACELOG_EVTITEMDESC_TYPE_UINT32: enmType = RTTRACELOGTYPE_UINT32; break; case TRACELOG_EVTITEMDESC_TYPE_INT32: enmType = RTTRACELOGTYPE_INT32; break; case TRACELOG_EVTITEMDESC_TYPE_UINT64: enmType = RTTRACELOGTYPE_UINT64; break; case TRACELOG_EVTITEMDESC_TYPE_INT64: enmType = RTTRACELOGTYPE_INT64; break; case TRACELOG_EVTITEMDESC_TYPE_FLOAT32: enmType = RTTRACELOGTYPE_FLOAT32; break; case TRACELOG_EVTITEMDESC_TYPE_FLOAT64: enmType = RTTRACELOGTYPE_FLOAT64; break; case TRACELOG_EVTITEMDESC_TYPE_RAWDATA: enmType = RTTRACELOGTYPE_RAWDATA; break; case TRACELOG_EVTITEMDESC_TYPE_POINTER: enmType = RTTRACELOGTYPE_POINTER; break; case TRACELOG_EVTITEMDESC_TYPE_SIZE: enmType = RTTRACELOGTYPE_SIZE; break; default: enmType = RTTRACELOGTYPE_INVALID; } return enmType; } /** * Converts the type enum to the size of the the event item data in bytes. * * @returns Event item data size in bytes. * @param pThis The trace log reader instance. * @param pEvtItemDesc The event item descriptor. */ static size_t rtTraceLogRdrGetEvtItemDataSz(PRTTRACELOGRDRINT pThis, PCRTTRACELOGEVTITEMDESC pEvtItemDesc) { size_t cb = 0; switch (pEvtItemDesc->enmType) { case RTTRACELOGTYPE_BOOL: case RTTRACELOGTYPE_UINT8: case RTTRACELOGTYPE_INT8: { cb = 1; break; } case RTTRACELOGTYPE_UINT16: case RTTRACELOGTYPE_INT16: { cb = 2; break; } case RTTRACELOGTYPE_UINT32: case RTTRACELOGTYPE_INT32: case RTTRACELOGTYPE_FLOAT32: { cb = 4; break; } case RTTRACELOGTYPE_UINT64: case RTTRACELOGTYPE_INT64: case RTTRACELOGTYPE_FLOAT64: { cb = 8; break; } case RTTRACELOGTYPE_RAWDATA: { cb = pEvtItemDesc->cbRawData; break; } case RTTRACELOGTYPE_POINTER: { cb = pThis->cbTypePtr; break; } case RTTRACELOGTYPE_SIZE: { cb = pThis->cbTypeSize; break; } default: AssertMsgFailed(("Invalid type %d\n", pEvtItemDesc->enmType)); } return cb; } /** * Calculates the overall event data size from the items in the event descriptor. * * @returns nothing. * @param pThis The trace log reader instance. * @param pEvtDesc The event descriptor. */ static void rtTraceLogRdrEvtCalcEvtDataSz(PRTTRACELOGRDRINT pThis, PRTTRACELOGRDREVTDESC pEvtDesc) { pEvtDesc->cbEvtData = 0; pEvtDesc->cRawDataNonStatic = 0; for (unsigned i = 0; i < pEvtDesc->EvtDesc.cEvtItems; i++) { PCRTTRACELOGEVTITEMDESC pEvtItemDesc = &pEvtDesc->aEvtItemDesc[i]; pEvtDesc->cbEvtData += rtTraceLogRdrGetEvtItemDataSz(pThis, pEvtItemDesc); if ( pEvtItemDesc->enmType == RTTRACELOGTYPE_RAWDATA && pEvtItemDesc->cbRawData == 0) pEvtDesc->cRawDataNonStatic++; } } /** * Ensures that the scratch buffer can hold at least the given amount of data. * * @returns IPRT status code. * @param pThis The trace log reader instance. * @param cbScratch New size of the scratch buffer in bytes. */ static int rtTraceLogRdrScratchEnsureSz(PRTTRACELOGRDRINT pThis, size_t cbScratch) { int rc = VINF_SUCCESS; if (pThis->cbScratch < cbScratch) { cbScratch = RT_ALIGN_Z(cbScratch, 64); uint8_t *pbScratchNew = (uint8_t *)RTMemRealloc(pThis->pbScratch, cbScratch); if (RT_LIKELY(pbScratchNew)) { pThis->cbScratch = cbScratch; pThis->pbScratch = pbScratchNew; } else rc = VERR_NO_MEMORY; } return rc; } /** * Advances to the next state resetting the scratch/receive buffers to the given state. * * @returns IPRT status. * @param pThis The trace log reader instance. * @param enmState The next state. * @param cbRecv How much to receive before processing the new data. * @param offScratch Offset to set the receive buffer to (used * when the magic was received which should still be saved). */ static int rtTraceLogRdrStateAdvanceEx(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRSTATE enmState, size_t cbRecv, uint32_t offScratch) { Assert(cbRecv >= offScratch); pThis->enmState = enmState; pThis->cbRecvLeft = cbRecv - offScratch; pThis->offScratch = offScratch; int rc = rtTraceLogRdrScratchEnsureSz(pThis, cbRecv); /* Zero out scratch buffer (don't care whether growing it failed, the old buffer is still there). */ memset(pThis->pbScratch + offScratch, 0, pThis->cbScratch - offScratch); return rc; } /** * Advances to the next state resetting the scratch/receive buffers. * * @returns IPRT status. * @param pThis The trace log reader instance. * @param enmState The next state. * @param cbRecv How much to receive before processing the new data. */ static int rtTraceLogRdrStateAdvance(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRSTATE enmState, size_t cbRecv) { return rtTraceLogRdrStateAdvanceEx(pThis, enmState, cbRecv, 0); } /** * Marks a received event descriptor as completed and adds it to the array of known descriptors. * * @returns IPRT status code. * @param pThis The trace log reader instance. * @param pEvtDesc The event descriptor which completed. */ static int rtTraceLogRdrEvtDescComplete(PRTTRACELOGRDRINT pThis, PRTTRACELOGRDREVTDESC pEvtDesc) { int rc = VINF_SUCCESS; rtTraceLogRdrEvtCalcEvtDataSz(pThis, pEvtDesc); /* Insert into array of known event descriptors. */ if (pThis->cEvtDescsCur == pThis->cEvtDescsMax) { uint32_t cEvtDescsNew = pThis->cEvtDescsMax + 10; size_t cbNew = cEvtDescsNew * sizeof(PRTTRACELOGRDREVTDESC *); PRTTRACELOGRDREVTDESC *papEvtDescsNew = (PRTTRACELOGRDREVTDESC *)RTMemRealloc(pThis->papEvtDescs, cbNew); if (RT_LIKELY(papEvtDescsNew)) { pThis->papEvtDescs = papEvtDescsNew; pThis->cEvtDescsMax = cEvtDescsNew; } else rc = VERR_NO_MEMORY; } if (RT_SUCCESS(rc)) { pThis->papEvtDescs[pThis->cEvtDescsCur++] = pEvtDesc; pThis->pEvtDescCur = NULL; rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_MAGIC, TRACELOG_MAGIC_SZ); } return rc; } /** * Decides which state to enter next after one event item descriptor was completed successfully. * * @returns IPRT status code. * @param pThis The trace log reader instance. * @param penmEvt Where to store the event indicator if a user visible event happened. * @param pfContinuePoll Where to store the flag whether to continue polling. */ static int rtTraceLogRdrEvtItemDescComplete(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { RT_NOREF(penmEvt, pfContinuePoll); int rc = VINF_SUCCESS; PRTTRACELOGRDREVTDESC pEvtDesc = pThis->pEvtDescCur; pEvtDesc->idxEvtItemCur++; /* If this event descriptor is complete add it to the array of known descriptors. */ if (pEvtDesc->idxEvtItemCur == pEvtDesc->EvtDesc.cEvtItems) rc = rtTraceLogRdrEvtDescComplete(pThis, pEvtDesc); else { /* Not done yet. */ rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_EVT_ITEM_DESC, sizeof(TRACELOGEVTITEMDESC)); } return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles a received header.} */ static DECLCALLBACK(int) rtTraceLogRdrHdrRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { RT_NOREF(penmEvt, pfContinuePoll); int rc = VINF_SUCCESS; PTRACELOGHDR pHdr = (PTRACELOGHDR)pThis->pbScratch; /* Verify magic. */ if (!memcmp(&pHdr->szMagic[0], TRACELOG_HDR_MAGIC, sizeof(pHdr->szMagic))) { /* Check endianess. */ if (pHdr->u32Endianess == TRACELOG_HDR_ENDIANESS) pThis->fConvEndianess = false; else if (RT_BSWAP_U32(pHdr->u32Endianess) == TRACELOG_HDR_ENDIANESS) { pThis->fConvEndianess = true; rtTraceLogRdrHdrEndianessConv(pHdr); } else rc = VERR_TRACELOG_READER_MALFORMED_LOG; if (RT_SUCCESS(rc)) { Assert(pHdr->u32Endianess == TRACELOG_HDR_ENDIANESS); /* Enforce strict limits to avoid exhausting memory. */ if ( pHdr->u32Version == TRACELOG_VERSION && pHdr->cbStrDesc < _1K && pHdr->cbTypePtr <= 8 && (pHdr->cbTypeSize == 8 || pHdr->cbTypeSize == 4)) { pThis->u64TsStart = pHdr->u64TsStart; pThis->cbTypePtr = pHdr->cbTypePtr; pThis->cbTypeSize = pHdr->cbTypeSize; pThis->cchDesc = pHdr->cbStrDesc; pThis->cEvtDescsMax = 10; /* Allocate array to hold event descriptors later on. */ pThis->papEvtDescs = (PRTTRACELOGRDREVTDESC *)RTMemAllocZ(pThis->cEvtDescsMax * sizeof(PRTTRACELOGRDREVTDESC)); if (RT_LIKELY(pThis->papEvtDescs)) { /* Switch to the next state. */ if (pHdr->cbStrDesc) rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_HDR_DESC, pHdr->cbStrDesc); else rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_MAGIC, TRACELOG_MAGIC_SZ); if (RT_SUCCESS(rc)) { *penmEvt = RTTRACELOGRDRPOLLEVT_HDR_RECVD; *pfContinuePoll = false; } } else rc = VERR_NO_MEMORY; } else rc = VERR_TRACELOG_READER_LOG_UNSUPPORTED; } } else rc = VERR_TRACELOG_READER_MALFORMED_LOG; return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles a received log description.} */ static DECLCALLBACK(int) rtTraceLogRdrHdrDescRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { RT_NOREF(penmEvt, pfContinuePoll); int rc = VINF_SUCCESS; char *pszDesc = (char *)pThis->pbScratch; RTStrPurgeEncoding(pszDesc); pThis->pszDesc = RTStrCacheEnterN(pThis->hStrCache, pszDesc, pThis->cchDesc); if (pThis->pszDesc) rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_MAGIC, TRACELOG_MAGIC_SZ); else rc = VERR_NO_STR_MEMORY; return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles a received magic.} */ static DECLCALLBACK(int) rtTraceLogRdrMagicRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { RT_NOREF(penmEvt, pfContinuePoll); int rc = VINF_SUCCESS; char *pszMagic = (char *)pThis->pbScratch; if (!memcmp(pszMagic, TRACELOG_EVTDESC_MAGIC, TRACELOG_MAGIC_SZ)) rc = rtTraceLogRdrStateAdvanceEx(pThis, RTTRACELOGRDRSTATE_RECV_EVT_DESC, sizeof(TRACELOGEVTDESC), TRACELOG_MAGIC_SZ); else if (!memcmp(pszMagic, TRACELOG_EVT_MAGIC, TRACELOG_MAGIC_SZ)) rc = rtTraceLogRdrStateAdvanceEx(pThis, RTTRACELOGRDRSTATE_RECV_EVT_MARKER, sizeof(TRACELOGEVT), TRACELOG_MAGIC_SZ); return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles a received event descriptor.} */ static DECLCALLBACK(int) rtTraceLogRdrEvtDescRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { RT_NOREF(penmEvt, pfContinuePoll); int rc = VINF_SUCCESS; PTRACELOGEVTDESC pEvtDesc = (PTRACELOGEVTDESC)pThis->pbScratch; if (pThis->fConvEndianess) rtTraceLogRdrEvtDescEndianessConv(pEvtDesc); if ( !memcmp(&pEvtDesc->szMagic[0], TRACELOG_EVTDESC_MAGIC, sizeof(pEvtDesc->szMagic)) && pEvtDesc->u32Id == pThis->cEvtDescsCur && (pEvtDesc->cbStrId >= 1 && pEvtDesc->cbStrId < 128) && pEvtDesc->cbStrDesc < _1K && pEvtDesc->cEvtItems < 128) { RTTRACELOGEVTSEVERITY enmSeverity = rtTraceLogRdrConvSeverity(pEvtDesc->u32Severity); if (RT_LIKELY(enmSeverity != RTTRACELOGEVTSEVERITY_INVALID)) { /* Allocate new internal event descriptor state. */ size_t cbEvtDesc = RT_UOFFSETOF_DYN(RTTRACELOGRDREVTDESC, aEvtItemDesc[pEvtDesc->cEvtItems]); PRTTRACELOGRDREVTDESC pEvtDescInt = (PRTTRACELOGRDREVTDESC)RTMemAllocZ(cbEvtDesc); if (RT_LIKELY(pEvtDesc)) { pEvtDescInt->cbStrId = pEvtDesc->cbStrId; pEvtDescInt->cbStrDesc = pEvtDesc->cbStrDesc; pEvtDescInt->EvtDesc.enmSeverity = enmSeverity; pEvtDescInt->EvtDesc.cEvtItems = pEvtDesc->cEvtItems; pEvtDescInt->EvtDesc.paEvtItemDesc = &pEvtDescInt->aEvtItemDesc[0]; pThis->pEvtDescCur = pEvtDescInt; rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_EVT_DESC_ID, pEvtDescInt->cbStrId); } else rc = VERR_NO_MEMORY; } else rc = VERR_TRACELOG_READER_MALFORMED_LOG; } else rc = VERR_TRACELOG_READER_MALFORMED_LOG; return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles a received event descriptor ID.} */ static DECLCALLBACK(int) rtTraceLogRdrEvtDescIdRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { RT_NOREF(penmEvt, pfContinuePoll); int rc = VINF_SUCCESS; pThis->pEvtDescCur->EvtDesc.pszId = RTStrCacheEnterN(pThis->hStrCache, (const char *)pThis->pbScratch, pThis->pEvtDescCur->cbStrId); if (RT_LIKELY(pThis->pEvtDescCur->EvtDesc.pszId)) { if (pThis->pEvtDescCur->cbStrDesc) rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_EVT_DESC_DESC, pThis->pEvtDescCur->cbStrDesc); else if (pThis->pEvtDescCur->EvtDesc.cEvtItems > 0) rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_EVT_ITEM_DESC, sizeof(TRACELOGEVTITEMDESC)); else rc = rtTraceLogRdrEvtDescComplete(pThis, pThis->pEvtDescCur); } else rc = VERR_NO_STR_MEMORY; return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles a received event descriptor description.} */ static DECLCALLBACK(int) rtTraceLogRdrEvtDescDescriptionRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { RT_NOREF(penmEvt, pfContinuePoll); int rc = VINF_SUCCESS; pThis->pEvtDescCur->EvtDesc.pszDesc = RTStrCacheEnterN(pThis->hStrCache, (const char *)pThis->pbScratch, pThis->pEvtDescCur->cbStrDesc); if (RT_LIKELY(pThis->pEvtDescCur->EvtDesc.pszDesc)) { if (pThis->pEvtDescCur->EvtDesc.cEvtItems > 0) rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_EVT_ITEM_DESC, sizeof(TRACELOGEVTITEMDESC)); else rc = rtTraceLogRdrEvtDescComplete(pThis, pThis->pEvtDescCur); } else rc = VERR_NO_STR_MEMORY; return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles a received event item descriptor.} */ static DECLCALLBACK(int) rtTraceLogRdrEvtItemDescRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { RT_NOREF(penmEvt, pfContinuePoll); int rc = VINF_SUCCESS; PTRACELOGEVTITEMDESC pEvtItemDesc = (PTRACELOGEVTITEMDESC)pThis->pbScratch; if (pThis->fConvEndianess) rtTraceLogRdrEvtItemDescEndianessConv(pEvtItemDesc); if ( !memcmp(&pEvtItemDesc->szMagic[0], TRACELOG_EVTITEMDESC_MAGIC, sizeof(pEvtItemDesc->szMagic)) && (pEvtItemDesc->cbStrName >= 1 && pEvtItemDesc->cbStrName < 128) && pEvtItemDesc->cbStrDesc < _1K && pEvtItemDesc->cbRawData < _1M) { RTTRACELOGTYPE enmType = rtTraceLogRdrConvType(pEvtItemDesc->u32Type); if (RT_LIKELY(enmType != RTTRACELOGTYPE_INVALID)) { PRTTRACELOGEVTITEMDESC pEvtDesc = &pThis->pEvtDescCur->aEvtItemDesc[pThis->pEvtDescCur->idxEvtItemCur]; pThis->pEvtDescCur->cbStrItemName = pEvtItemDesc->cbStrName; pThis->pEvtDescCur->cbStrItemDesc = pEvtItemDesc->cbStrDesc; pEvtDesc->enmType = enmType; pEvtDesc->cbRawData = pEvtItemDesc->cbRawData; rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_EVT_ITEM_DESC_NAME, pEvtItemDesc->cbStrName); } else rc = VERR_TRACELOG_READER_MALFORMED_LOG; } else rc = VERR_TRACELOG_READER_MALFORMED_LOG; return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles a received event item descriptor name.} */ static DECLCALLBACK(int) rtTraceLogRdrEvtItemDescNameRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { int rc = VINF_SUCCESS; PRTTRACELOGEVTITEMDESC pEvtDesc = &pThis->pEvtDescCur->aEvtItemDesc[pThis->pEvtDescCur->idxEvtItemCur]; pEvtDesc->pszName = RTStrCacheEnterN(pThis->hStrCache, (const char *)pThis->pbScratch, pThis->pEvtDescCur->cbStrItemName); if (RT_LIKELY(pEvtDesc->pszName)) { if (pThis->pEvtDescCur->cbStrItemDesc) rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_EVT_ITEM_DESC_DESC, pThis->pEvtDescCur->cbStrItemDesc); else rc = rtTraceLogRdrEvtItemDescComplete(pThis, penmEvt, pfContinuePoll); } else rc = VERR_NO_STR_MEMORY; return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles a received event item description.} */ static DECLCALLBACK(int) rtTraceLogRdrEvtItemDescDescriptionRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { int rc = VINF_SUCCESS; PRTTRACELOGEVTITEMDESC pEvtDesc = &pThis->pEvtDescCur->aEvtItemDesc[pThis->pEvtDescCur->idxEvtItemCur]; pEvtDesc->pszDesc = RTStrCacheEnterN(pThis->hStrCache, (const char *)pThis->pbScratch, pThis->pEvtDescCur->cbStrItemDesc); if (RT_LIKELY(pEvtDesc->pszDesc)) rc = rtTraceLogRdrEvtItemDescComplete(pThis, penmEvt, pfContinuePoll); else rc = VERR_NO_STR_MEMORY; return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles a received event marker.} */ static DECLCALLBACK(int) rtTraceLogRdrEvtMarkerRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { int rc = VINF_SUCCESS; PTRACELOGEVT pEvtStrm = (PTRACELOGEVT)pThis->pbScratch; if (pThis->fConvEndianess) rtTraceLogRdrEvtEndianessConv(pEvtStrm); if ( (pEvtStrm->u64SeqNo == pThis->u64SeqNoLast + 1) && !(pEvtStrm->fFlags & ~TRACELOG_EVT_F_VALID) && pEvtStrm->u32EvtDescId < pThis->cEvtDescsCur) { PRTTRACELOGRDREVTDESC pEvtDesc = pThis->papEvtDescs[pEvtStrm->u32EvtDescId]; if ( ( !pEvtDesc->cRawDataNonStatic && pEvtStrm->cbEvtData == pEvtDesc->cbEvtData) || ( pEvtDesc->cRawDataNonStatic && pEvtStrm->cbEvtData >= pEvtDesc->cbEvtData && pEvtStrm->cRawEvtDataSz == pEvtDesc->cRawDataNonStatic)) { size_t cbEvt = RT_UOFFSETOF_DYN(RTTRACELOGRDREVTINT, abEvtData[pEvtStrm->cbEvtData]); cbEvt += pEvtDesc->cRawDataNonStatic * sizeof(size_t); PRTTRACELOGRDREVTINT pEvt = (PRTTRACELOGRDREVTINT)RTMemAllocZ(cbEvt); if (RT_LIKELY(pEvt)) { pEvt->pRdr = pThis; pEvt->u64SeqNo = pEvtStrm->u64SeqNo; pEvt->u64Ts = pEvtStrm->u64Ts; pEvt->pEvtDesc = pEvtDesc; pEvt->cbEvtData = pEvtStrm->cbEvtData; pEvt->pacbRawData = pEvtDesc->cRawDataNonStatic ? (size_t *)&pEvt->abEvtData[pEvtStrm->cbEvtData] : NULL; /** @todo Group handling and parenting. */ pThis->pEvtCur = pEvt; size_t cbEvtDataRecv = pEvtStrm->cRawEvtDataSz * pThis->cbTypeSize + pEvtStrm->cbEvtData; if (cbEvtDataRecv) rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_EVT_DATA, cbEvtDataRecv); else { pThis->pEvtCur = NULL; RTSemMutexRequest(pThis->hMtx, RT_INDEFINITE_WAIT); pThis->u64SeqNoLast = pEvt->u64SeqNo; RTListAppend(&pThis->LstEvts, &pEvt->NdGlob); RTSemMutexRelease(pThis->hMtx); *penmEvt = RTTRACELOGRDRPOLLEVT_TRACE_EVENT_RECVD; *pfContinuePoll = false; rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_MAGIC, TRACELOG_MAGIC_SZ); } } else rc = VERR_NO_MEMORY; } else rc = VERR_TRACELOG_READER_MALFORMED_LOG; } else rc = VERR_TRACELOG_READER_MALFORMED_LOG; return rc; } /** * @callback_method_impl{FNRTTRACELOGRDRSTATEHANDLER, Handles received event data.} */ static DECLCALLBACK(int) rtTraceLogRdrEvtDataRecvd(PRTTRACELOGRDRINT pThis, RTTRACELOGRDRPOLLEVT *penmEvt, bool *pfContinuePoll) { RT_NOREF(penmEvt, pfContinuePoll); int rc = VINF_SUCCESS; PRTTRACELOGRDREVTINT pEvt = pThis->pEvtCur; PCRTTRACELOGRDREVTDESC pEvtDesc = pEvt->pEvtDesc; uint8_t *pbData = pThis->pbScratch; size_t cbRawDataNonStatic = 0; /* Retrieve any raw data size indicators first. */ for (unsigned i = 0; i < pEvtDesc->cRawDataNonStatic; i++) { size_t cb = 0; if (pThis->cbTypeSize == 4) { cb = RT_BSWAP_U32(*(uint32_t *)pbData); pbData += 4; } else if (pThis->cbTypeSize == 8) { cb = RT_BSWAP_U64(*(uint64_t *)pbData); pbData += 8; } else AssertMsgFailed(("Invalid size_t size %u\n", pThis->cbTypeSize)); pEvt->pacbRawData[i] = cb; cbRawDataNonStatic += cb; } /* Verify that sizes add up. */ if (pEvt->cbEvtData == pEvtDesc->cbEvtData + cbRawDataNonStatic) { /* Copy the data over. */ memcpy(&pEvt->abEvtData[0], pbData, pEvt->cbEvtData); /* Done add event to global list and generate event. */ pThis->pEvtCur = NULL; RTSemMutexRequest(pThis->hMtx, RT_INDEFINITE_WAIT); pThis->u64SeqNoLast = pEvt->u64SeqNo; RTListAppend(&pThis->LstEvts, &pEvt->NdGlob); RTSemMutexRelease(pThis->hMtx); *penmEvt = RTTRACELOGRDRPOLLEVT_TRACE_EVENT_RECVD; *pfContinuePoll = false; rc = rtTraceLogRdrStateAdvance(pThis, RTTRACELOGRDRSTATE_RECV_MAGIC, TRACELOG_MAGIC_SZ); } else rc = VERR_TRACELOG_READER_MALFORMED_LOG; return rc; } /** * @copydoc FNRTTRACELOGRDRSTREAM */ static DECLCALLBACK(int) rtTraceLogRdrFileStream(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbRead, RTMSINTERVAL cMsTimeout) { RT_NOREF(cMsTimeout); RTFILE hFile = (RTFILE)pvUser; return RTFileRead(hFile, pvBuf, cbBuf, pcbRead); } /** * @copydoc FNRTTRACELOGSTREAMCLOSE */ static DECLCALLBACK(int) rtTraceLogRdrFileStreamClose(void *pvUser) { RTFILE hFile = (RTFILE)pvUser; return RTFileClose(hFile); } /** * Returns the size of the data for the given event item descriptor. * * @returns Size in bytes for the given event item descriptor. * @param pThis The trace log rader instance. * @param pEvtItemDesc The event item descriptor. * @param pacbRawData The raw data size array for he associated event to get the size for non static raw data items. * @param pidxRawData The index into the raw data size array for the next item to use. */ static size_t rtTraceLogRdrEvtItemGetSz(PRTTRACELOGRDRINT pThis, PCRTTRACELOGEVTITEMDESC pEvtItemDesc, size_t *pacbRawData, unsigned *pidxRawData) { size_t cbRet = 0; switch (pEvtItemDesc->enmType) { case RTTRACELOGTYPE_BOOL: cbRet = sizeof(bool); break; case RTTRACELOGTYPE_UINT8: cbRet = sizeof(uint8_t); break; case RTTRACELOGTYPE_INT8: cbRet = sizeof(int8_t); break; case RTTRACELOGTYPE_UINT16: cbRet = sizeof(uint16_t); break; case RTTRACELOGTYPE_INT16: cbRet = sizeof(int16_t); break; case RTTRACELOGTYPE_UINT32: cbRet = sizeof(uint32_t); break; case RTTRACELOGTYPE_INT32: cbRet = sizeof(int32_t); break; case RTTRACELOGTYPE_UINT64: cbRet = sizeof(uint64_t); break; case RTTRACELOGTYPE_INT64: cbRet = sizeof(int64_t); break; case RTTRACELOGTYPE_FLOAT32: cbRet = sizeof(float); break; case RTTRACELOGTYPE_FLOAT64: cbRet = sizeof(double); break; case RTTRACELOGTYPE_RAWDATA: if (pEvtItemDesc->cbRawData == 0) { cbRet = pacbRawData[*pidxRawData]; *pidxRawData++; } else cbRet = pEvtItemDesc->cbRawData; break; case RTTRACELOGTYPE_POINTER: cbRet = pThis->cbTypePtr; break; case RTTRACELOGTYPE_SIZE: cbRet = pThis->cbTypeSize; break; default: AssertMsgFailed(("Invalid type given %d\n", pEvtItemDesc->enmType)); } return cbRet; } /** * Resolves the offset of the field with the given name returning the offset and data type. * * @returns IPRT status code. * @param pEvt The event to fetch the data for. * @param pszName The field to fetch. * @param poffData Where to store the offset to the data on success. * @param pcbEvtData Where to store the size of the size of the event data. * @param ppEvtItemDesc Where to store the event item descriptor. */ static int rtTraceLogRdrEvtResolveData(PRTTRACELOGRDREVTINT pEvt, const char *pszName, uint32_t *poffData, size_t *pcbEvtData, PPCRTTRACELOGEVTITEMDESC ppEvtItemDesc) { PCRTTRACELOGRDREVTDESC pEvtDesc = pEvt->pEvtDesc; uint32_t offData = 0; unsigned idxRawData = 0; for (unsigned i = 0; i < pEvtDesc->EvtDesc.cEvtItems; i++) { PCRTTRACELOGEVTITEMDESC pEvtItemDesc = &pEvtDesc->aEvtItemDesc[i]; if (!RTStrCmp(pszName, pEvtItemDesc->pszName)) { *poffData = offData; *pcbEvtData = rtTraceLogRdrEvtItemGetSz(pEvt->pRdr, pEvtItemDesc, pEvt->pacbRawData, &idxRawData); *ppEvtItemDesc = pEvtItemDesc; return VINF_SUCCESS; } offData += (uint32_t)rtTraceLogRdrEvtItemGetSz(pEvt->pRdr, pEvtItemDesc, pEvt->pacbRawData, &idxRawData); } return VERR_NOT_FOUND; } /** * Fills a value with the given event data. * * @returns IPRT status code. * @param pEvt The event to fetch the data for. * @param offData Offset the data is located in the event. * @param cbData Number of bytes for the data. * @param pEvtItemDesc The event item descriptor. * @param pVal The value to fill. */ static int rtTraceLogRdrEvtFillVal(PRTTRACELOGRDREVTINT pEvt, uint32_t offData, size_t cbData, PCRTTRACELOGEVTITEMDESC pEvtItemDesc, PRTTRACELOGEVTVAL pVal) { PRTTRACELOGRDRINT pThis = pEvt->pRdr; uint8_t *pbData = &pEvt->abEvtData[offData]; pVal->pItemDesc = pEvtItemDesc; switch (pEvtItemDesc->enmType) { case RTTRACELOGTYPE_BOOL: pVal->u.f = *(bool *)pbData; break; case RTTRACELOGTYPE_UINT8: pVal->u.u8 = *pbData; break; case RTTRACELOGTYPE_INT8: pVal->u.i8 = *(int8_t *)pbData; break; case RTTRACELOGTYPE_UINT16: { uint16_t u16Tmp = *(uint16_t *)pbData; if (pThis->fConvEndianess) pVal->u.u16 = RT_BSWAP_U16(u16Tmp); else pVal->u.u16 = u16Tmp; break; } case RTTRACELOGTYPE_INT16: { uint8_t abData[2]; if (pThis->fConvEndianess) { abData[0] = pbData[1]; abData[1] = pbData[0]; } else { abData[0] = pbData[0]; abData[1] = pbData[1]; } pVal->u.i16 = *(int16_t *)&abData[0]; break; } case RTTRACELOGTYPE_UINT32: { uint32_t u32Tmp = *(uint32_t *)pbData; if (pThis->fConvEndianess) pVal->u.u32 = RT_BSWAP_U32(u32Tmp); else pVal->u.u32 = u32Tmp; break; } case RTTRACELOGTYPE_INT32: { uint8_t abData[4]; if (pThis->fConvEndianess) { abData[0] = pbData[3]; abData[1] = pbData[2]; abData[2] = pbData[1]; abData[3] = pbData[0]; } else { abData[0] = pbData[0]; abData[1] = pbData[1]; abData[2] = pbData[2]; abData[3] = pbData[3]; } pVal->u.i32 = *(int32_t *)&abData[0]; break; } case RTTRACELOGTYPE_UINT64: { uint64_t u64Tmp = *(uint64_t *)pbData; if (pThis->fConvEndianess) pVal->u.u64 = RT_BSWAP_U64(u64Tmp); else pVal->u.u64 = u64Tmp; break; } case RTTRACELOGTYPE_INT64: { uint8_t abData[8]; if (pThis->fConvEndianess) { abData[0] = pbData[7]; abData[1] = pbData[6]; abData[2] = pbData[5]; abData[3] = pbData[4]; abData[4] = pbData[3]; abData[5] = pbData[2]; abData[6] = pbData[1]; abData[7] = pbData[0]; } else { abData[0] = pbData[0]; abData[1] = pbData[1]; abData[2] = pbData[2]; abData[3] = pbData[3]; abData[4] = pbData[4]; abData[5] = pbData[5]; abData[6] = pbData[6]; abData[7] = pbData[7]; } pVal->u.i32 = *(int64_t *)&abData[0]; break; } case RTTRACELOGTYPE_FLOAT32: { uint8_t abData[4]; if (pThis->fConvEndianess) { abData[0] = pbData[3]; abData[1] = pbData[2]; abData[2] = pbData[1]; abData[3] = pbData[0]; } else { abData[0] = pbData[0]; abData[1] = pbData[1]; abData[2] = pbData[2]; abData[3] = pbData[3]; } pVal->u.f32 = *(float *)&abData[0]; break; } case RTTRACELOGTYPE_FLOAT64: { uint8_t abData[8]; if (pThis->fConvEndianess) { abData[0] = pbData[7]; abData[1] = pbData[6]; abData[2] = pbData[5]; abData[3] = pbData[4]; abData[4] = pbData[3]; abData[5] = pbData[2]; abData[6] = pbData[1]; abData[7] = pbData[0]; } else { abData[0] = pbData[0]; abData[1] = pbData[1]; abData[2] = pbData[2]; abData[3] = pbData[3]; abData[4] = pbData[4]; abData[5] = pbData[5]; abData[6] = pbData[6]; abData[7] = pbData[7]; } pVal->u.f64 = *(double *)&abData[0]; break; } case RTTRACELOGTYPE_RAWDATA: pVal->u.RawData.pb = pbData; if (pEvtItemDesc->cbRawData == 0) pVal->u.RawData.cb = cbData; else pVal->u.RawData.cb = pEvtItemDesc->cbRawData; break; case RTTRACELOGTYPE_POINTER: { if (pThis->cbTypePtr == 4) { if (pThis->fConvEndianess) pVal->u.uPtr = RT_BSWAP_U32(*(uint32_t *)pbData); else pVal->u.uPtr = *(uint32_t *)pbData; } else if (pThis->cbTypePtr == 8) { if (pThis->fConvEndianess) pVal->u.uPtr = RT_BSWAP_U64(*(uint64_t *)pbData); else pVal->u.uPtr = *(uint64_t *)pbData; } else AssertMsgFailed(("Invalid pointer size %d, should not happen!\n", pThis->cbTypePtr)); break; } case RTTRACELOGTYPE_SIZE: { if (pThis->cbTypeSize == 4) { if (pThis->fConvEndianess) pVal->u.sz = RT_BSWAP_U32(*(uint32_t *)pbData); else pVal->u.sz = *(uint32_t *)pbData; } else if (pThis->cbTypeSize == 8) { if (pThis->fConvEndianess) pVal->u.sz = RT_BSWAP_U64(*(uint64_t *)pbData); else pVal->u.sz = *(uint64_t *)pbData; } else AssertMsgFailed(("Invalid size_t size %d, should not happen!\n", pThis->cbTypeSize)); break; } default: AssertMsgFailed(("Invalid type given %d\n", pEvtItemDesc->enmType)); } return VINF_SUCCESS; } RTDECL(int) RTTraceLogRdrCreate(PRTTRACELOGRDR phTraceLogRdr, PFNRTTRACELOGRDRSTREAM pfnStreamIn, PFNRTTRACELOGSTREAMCLOSE pfnStreamClose, void *pvUser) { AssertPtrReturn(phTraceLogRdr, VERR_INVALID_POINTER); AssertPtrReturn(pfnStreamIn, VERR_INVALID_POINTER); AssertPtrReturn(pfnStreamClose, VERR_INVALID_POINTER); int rc = VINF_SUCCESS; PRTTRACELOGRDRINT pThis = (PRTTRACELOGRDRINT)RTMemAllocZ(sizeof(*pThis)); if (pThis) { rc = RTSemMutexCreate(&pThis->hMtx); if (RT_SUCCESS(rc)) { rc = RTStrCacheCreate(&pThis->hStrCache, "TRACELOGRDR"); if (RT_SUCCESS(rc)) { RTListInit(&pThis->LstEvts); pThis->u32Magic = RTTRACELOGRDR_MAGIC; pThis->pfnStreamIn = pfnStreamIn; pThis->pfnStreamClose = pfnStreamClose; pThis->pvUser = pvUser; pThis->enmState = RTTRACELOGRDRSTATE_RECV_HDR; pThis->fConvEndianess = false; pThis->pszDesc = NULL; pThis->cEvtDescsCur = 0; pThis->cEvtDescsMax = 0; pThis->papEvtDescs = NULL; pThis->pEvtDescCur = NULL; pThis->u64SeqNoLast = 0; pThis->cbScratch = sizeof(TRACELOGHDR); pThis->offScratch = 0; pThis->cbRecvLeft = sizeof(TRACELOGHDR); pThis->pbScratch = (uint8_t *)RTMemAllocZ(pThis->cbScratch); if (RT_LIKELY(pThis->pbScratch)) { *phTraceLogRdr = pThis; return VINF_SUCCESS; } else rc = VERR_NO_MEMORY; RTStrCacheDestroy(pThis->hStrCache); } RTSemMutexDestroy(pThis->hMtx); } RTMemFree(pThis); } else rc = VERR_NO_MEMORY; return rc; } RTDECL(int) RTTraceLogRdrCreateFromFile(PRTTRACELOGRDR phTraceLogRdr, const char *pszFilename) { AssertPtrReturn(phTraceLogRdr, VERR_INVALID_POINTER); AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); RTFILE hFile = NIL_RTFILE; int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); if (RT_SUCCESS(rc)) { rc = RTTraceLogRdrCreate(phTraceLogRdr, rtTraceLogRdrFileStream, rtTraceLogRdrFileStreamClose, hFile); if (RT_FAILURE(rc)) { RTFileClose(hFile); RTFileDelete(pszFilename); } } return rc; } RTDECL(int) RTTraceLogRdrDestroy(RTTRACELOGRDR hTraceLogRdr) { PRTTRACELOGRDRINT pThis = hTraceLogRdr; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); pThis->u32Magic = RTTRACELOGRDR_MAGIC_DEAD; pThis->pfnStreamClose(pThis->pvUser); for (unsigned i = 0; i < pThis->cEvtDescsCur; i++) RTMemFree(pThis->papEvtDescs[i]); if (pThis->papEvtDescs) RTMemFree(pThis->papEvtDescs); RTSemMutexDestroy(pThis->hMtx); RTMemFree(pThis->pbScratch); RTStrCacheDestroy(pThis->hStrCache); RTMemFree(pThis); return VINF_SUCCESS; } RTDECL(int) RTTraceLogRdrEvtPoll(RTTRACELOGRDR hTraceLogRdr, RTTRACELOGRDRPOLLEVT *penmEvt, RTMSINTERVAL cMsTimeout) { PRTTRACELOGRDRINT pThis = hTraceLogRdr; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertPtrReturn(penmEvt, VERR_INVALID_POINTER); int rc = VINF_SUCCESS; bool fContinue = true; while ( RT_SUCCESS(rc) && fContinue) { size_t cbRecvd = 0; rc = rtTraceLogRdrStreamRead(pThis, &pThis->pbScratch[pThis->offScratch], pThis->cbRecvLeft, &cbRecvd, cMsTimeout); if (RT_SUCCESS(rc)) { if (cbRecvd == pThis->cbRecvLeft) { /* Act according to the current state. */ rc = g_apfnStateHandlers[pThis->enmState](pThis, penmEvt, &fContinue); } else pThis->cbRecvLeft -= cbRecvd; } } return rc; } RTDECL(int) RTTraceLogRdrQueryLastEvt(RTTRACELOGRDR hTraceLogRdr, PRTTRACELOGRDREVT phRdrEvt) { PRTTRACELOGRDRINT pThis = hTraceLogRdr; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertPtrReturn(phRdrEvt, VERR_INVALID_POINTER); int rc = VINF_SUCCESS; RTSemMutexRequest(pThis->hMtx, RT_INDEFINITE_WAIT); PRTTRACELOGRDREVTINT pEvt = RTListGetLast(&pThis->LstEvts, RTTRACELOGRDREVTINT, NdGlob); *phRdrEvt = pEvt; if (!pEvt) rc = VERR_NOT_FOUND; RTSemMutexRelease(pThis->hMtx); return rc; } RTDECL(int) RTTraceLogRdrQueryIterator(RTTRACELOGRDR hTraceLogRdr, PRTTRACELOGRDRIT phIt) { PRTTRACELOGRDRINT pThis = hTraceLogRdr; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertPtrReturn(phIt, VERR_INVALID_POINTER); int rc = VINF_SUCCESS; PRTTRACELOGRDRITINT pIt = (PRTTRACELOGRDRITINT)RTMemAllocZ(sizeof(*pIt)); if (RT_LIKELY(pIt)) { pIt->pRdr = pThis; pIt->pEvt = RTListGetFirst(&pThis->LstEvts, RTTRACELOGRDREVTINT, NdGlob); } else rc = VERR_NO_MEMORY; return rc; } RTDECL(void) RTTraceLogRdrIteratorFree(RTTRACELOGRDRIT hIt) { PRTTRACELOGRDRITINT pIt = hIt; AssertPtrReturnVoid(pIt); RTMemFree(pIt); } RTDECL(int) RTTraceLogRdrIteratorNext(RTTRACELOGRDRIT hIt) { PRTTRACELOGRDRITINT pIt = hIt; AssertPtrReturn(pIt, VERR_INVALID_HANDLE); if (!pIt->pEvt) return VERR_TRACELOG_READER_ITERATOR_END; int rc = VINF_SUCCESS; PRTTRACELOGRDREVTINT pEvtNext = RTListGetNext(&pIt->pRdr->LstEvts, pIt->pEvt, RTTRACELOGRDREVTINT, NdGlob); if (pEvtNext) pIt->pEvt = pEvtNext; else rc = VERR_TRACELOG_READER_ITERATOR_END; return rc; } RTDECL(int) RTTraceLogRdrIteratorQueryEvent(RTTRACELOGRDRIT hIt, PRTTRACELOGRDREVT phRdrEvt) { PRTTRACELOGRDRITINT pIt = hIt; AssertPtrReturn(pIt, VERR_INVALID_HANDLE); AssertPtrReturn(phRdrEvt, VERR_INVALID_POINTER); *phRdrEvt = pIt->pEvt; return VINF_SUCCESS; } RTDECL(uint64_t) RTTraceLogRdrEvtGetSeqNo(RTTRACELOGRDREVT hRdrEvt) { PRTTRACELOGRDREVTINT pEvt = hRdrEvt; AssertPtrReturn(pEvt, 0); return pEvt->u64SeqNo; } RTDECL(uint64_t) RTTraceLogRdrEvtGetTs(RTTRACELOGRDREVT hRdrEvt) { PRTTRACELOGRDREVTINT pEvt = hRdrEvt; AssertPtrReturn(pEvt, 0); return pEvt->u64Ts; } RTDECL(bool) RTTraceLogRdrEvtIsGrouped(RTTRACELOGRDREVT hRdrEvt) { PRTTRACELOGRDREVTINT pEvt = hRdrEvt; AssertPtrReturn(pEvt, false); return pEvt->idGrp != 0; } RTDECL(PCRTTRACELOGEVTDESC) RTTraceLogRdrEvtGetDesc(RTTRACELOGRDREVT hRdrEvt) { PRTTRACELOGRDREVTINT pEvt = hRdrEvt; AssertPtrReturn(pEvt, NULL); return &pEvt->pEvtDesc->EvtDesc; } RTDECL(int) RTTraceLogRdrEvtQueryVal(RTTRACELOGRDREVT hRdrEvt, const char *pszName, PRTTRACELOGEVTVAL pVal) { PRTTRACELOGRDREVTINT pEvt = hRdrEvt; AssertPtrReturn(pEvt, VERR_INVALID_HANDLE); uint32_t offData = 0; size_t cbData = 0; PCRTTRACELOGEVTITEMDESC pEvtItemDesc = NULL; int rc = rtTraceLogRdrEvtResolveData(pEvt, pszName, &offData, &cbData, &pEvtItemDesc); if (RT_SUCCESS(rc)) rc = rtTraceLogRdrEvtFillVal(pEvt, offData, cbData, pEvtItemDesc, pVal); return rc; } RTDECL(int) RTTraceLogRdrEvtFillVals(RTTRACELOGRDREVT hRdrEvt, unsigned idxItemStart, PRTTRACELOGEVTVAL paVals, unsigned cVals, unsigned *pcVals) { PRTTRACELOGRDREVTINT pEvt = hRdrEvt; AssertPtrReturn(pEvt, VERR_INVALID_HANDLE); PCRTTRACELOGRDREVTDESC pEvtDesc = pEvt->pEvtDesc; AssertReturn(idxItemStart < pEvtDesc->EvtDesc.cEvtItems, VERR_INVALID_PARAMETER); /* Advance to the item the caller wants to fill in. */ uint32_t offData = 0; unsigned idxRawData = 0; for (unsigned i = 0; i < idxItemStart; i++) { PCRTTRACELOGEVTITEMDESC pEvtItemDesc = &pEvtDesc->aEvtItemDesc[i]; offData += (uint32_t)rtTraceLogRdrEvtItemGetSz(pEvt->pRdr, pEvtItemDesc, pEvt->pacbRawData, &idxRawData); } int rc = VINF_SUCCESS; unsigned idxItemEnd = RT_MIN(idxItemStart + cVals, pEvtDesc->EvtDesc.cEvtItems); for (unsigned i = idxItemStart; i < idxItemEnd && RT_SUCCESS(rc); i++) { PCRTTRACELOGEVTITEMDESC pEvtItemDesc = &pEvtDesc->aEvtItemDesc[i]; size_t cbData = rtTraceLogRdrEvtItemGetSz(pEvt->pRdr, pEvtItemDesc, pEvt->pacbRawData, &idxRawData); rc = rtTraceLogRdrEvtFillVal(pEvt, offData, cbData, pEvtItemDesc, &paVals[i - idxItemStart]); offData += (uint32_t)cbData; } *pcVals = idxItemEnd - idxItemStart; return rc; }