/** * Destroys an module after the reference count has reached zero. * * @param pDbgMod The module instance. */ static void rtDbgModDestroy(PRTDBGMODINT pDbgMod) { /* * Close the debug info interpreter first, then the image interpret. */ RTCritSectEnter(&pDbgMod->CritSect); /* paranoia */ if (pDbgMod->pDbgVt) { pDbgMod->pDbgVt->pfnClose(pDbgMod); pDbgMod->pDbgVt = NULL; pDbgMod->pvDbgPriv = NULL; } if (pDbgMod->pImgVt) { pDbgMod->pImgVt->pfnClose(pDbgMod); pDbgMod->pImgVt = NULL; pDbgMod->pvImgPriv = NULL; } /* * Free the resources. */ ASMAtomicWriteU32(&pDbgMod->u32Magic, ~RTDBGMOD_MAGIC); RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile); RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile); RTCritSectLeave(&pDbgMod->CritSect); /* paranoia */ RTCritSectDelete(&pDbgMod->CritSect); RTMemFree(pDbgMod); }
/** @copydoc RTDBGMODVTDBG::pfnClose */ static DECLCALLBACK(int) rtDbgModContainer_Close(PRTDBGMODINT pMod) { PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; /* * Destroy the symbols and instance data. */ for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++) { RTAvlrUIntPtrDestroy(&pThis->paSegs[iSeg].SymAddrTree, rtDbgModContainer_DestroyTreeNode, NULL); RTStrCacheRelease(g_hDbgModStrCache, pThis->paSegs[iSeg].pszName); pThis->paSegs[iSeg].pszName = NULL; } RTAvlrUIntPtrDestroy(&pThis->AbsAddrTree, rtDbgModContainer_DestroyTreeNode, NULL); pThis->Names = NULL; #ifdef RTDBGMODCNT_WITH_MEM_CACHE RTMemCacheDestroy(pThis->hLineNumAllocator); pThis->hLineNumAllocator = NIL_RTMEMCACHE; #else RTAvlU32Destroy(&pThis->LineOrdinalTree, rtDbgModContainer_DestroyTreeLineNode, pThis); #endif RTMemFree(pThis->paSegs); pThis->paSegs = NULL; RTMemFree(pThis); return VINF_SUCCESS; }
/** @copydoc RTDBGMODVTDBG::pfnLineAdd */ static DECLCALLBACK(int) rtDbgModContainer_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) { PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; /* * Validate the input address. */ AssertMsgReturn(iSeg < pThis->cSegs, ("iSeg=%#x cSegs=%#x\n", iSeg, pThis->cSegs), VERR_DBG_INVALID_SEGMENT_INDEX); AssertMsgReturn(off < pThis->paSegs[iSeg].cb, ("off=%RTptr cbSeg=%RTptr\n", off, pThis->paSegs[iSeg].cb), VERR_DBG_INVALID_SEGMENT_OFFSET); /* * Create a new entry. */ #ifdef RTDBGMODCNT_WITH_MEM_CACHE PRTDBGMODCTNLINE pLine = (PRTDBGMODCTNLINE)RTMemCacheAlloc(pThis->hLineNumAllocator); #else PRTDBGMODCTNLINE pLine = (PRTDBGMODCTNLINE)RTMemAllocZ(sizeof(*pLine)); #endif if (!pLine) return VERR_NO_MEMORY; pLine->AddrCore.Key = off; pLine->OrdinalCore.Key = pThis->iNextLineOrdinal; pLine->uLineNo = uLineNo; pLine->iSeg = iSeg; pLine->pszFile = RTStrCacheEnterN(g_hDbgModStrCache, pszFile, cchFile); int rc; if (pLine->pszFile) { if (RTAvlUIntPtrInsert(&pThis->paSegs[iSeg].LineAddrTree, &pLine->AddrCore)) { if (RTAvlU32Insert(&pThis->LineOrdinalTree, &pLine->OrdinalCore)) { if (piOrdinal) *piOrdinal = pThis->iNextLineOrdinal; pThis->iNextLineOrdinal++; return VINF_SUCCESS; } rc = VERR_INTERNAL_ERROR_5; RTAvlUIntPtrRemove(&pThis->paSegs[iSeg].LineAddrTree, pLine->AddrCore.Key); } /* bail out */ rc = VERR_DBG_ADDRESS_CONFLICT; RTStrCacheRelease(g_hDbgModStrCache, pLine->pszFile); } else rc = VERR_NO_MEMORY; #ifdef RTDBGMODCNT_WITH_MEM_CACHE RTMemCacheFree(pThis->hLineNumAllocator, pLine); #else RTMemFree(pLine); #endif return rc; }
/** Destroy a symbol node. */ static DECLCALLBACK(int) rtDbgModContainer_DestroyTreeNode(PAVLRUINTPTRNODECORE pNode, void *pvUser) { PRTDBGMODCTNSYMBOL pSym = RT_FROM_MEMBER(pNode, RTDBGMODCTNSYMBOL, AddrCore); RTStrCacheRelease(g_hDbgModStrCache, pSym->NameCore.pszString); pSym->NameCore.pszString = NULL; RTMemFree(pSym); NOREF(pvUser); return 0; }
/** Destroy a line number node. */ static DECLCALLBACK(int) rtDbgModContainer_DestroyTreeLineNode(PAVLU32NODECORE pNode, void *pvUser) { PRTDBGMODCTN pThis = (PRTDBGMODCTN)pvUser; PRTDBGMODCTNLINE pLine = RT_FROM_MEMBER(pNode, RTDBGMODCTNLINE, OrdinalCore); RTStrCacheRelease(g_hDbgModStrCache, pLine->pszFile); pLine->pszFile = NULL; #ifdef RTDBGMODCNT_WITH_MEM_CACHE RTMemCacheFree(pThis->hLineNumAllocator, pLine); #else RTMemFree(pLine); NOREF(pThis); #endif return 0; }
/** * Special container operation for removing everything. * * @returns IPRT status code. * @param pMod The module instance. */ DECLHIDDEN(int) rtDbgModContainer_RemoveAll(PRTDBGMODINT pMod) { PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; rtDbgModContainer_LineRemoveAll(pMod); rtDbgModContainer_SymbolRemoveAll(pMod); for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++) { RTStrCacheRelease(g_hDbgModStrCache, pThis->paSegs[iSeg].pszName); pThis->paSegs[iSeg].pszName = NULL; } pThis->cSegs = 0; pThis->cb = 0; return VINF_SUCCESS; }
/** * Creates a module based on the default debug info container. * * This can be used to manually load a module and its symbol. The primary user * group is the debug info interpreters, which use this API to create an * efficient debug info container behind the scenes and forward all queries to * it once the info has been loaded. * * @returns IPRT status code. * * @param phDbgMod Where to return the module handle. * @param pszName The name of the module (mandatory). * @param cbSeg The size of initial segment. If zero, segments will * have to be added manually using RTDbgModSegmentAdd. * @param fFlags Flags reserved for future extensions, MBZ for now. */ RTDECL(int) RTDbgModCreate(PRTDBGMOD phDbgMod, const char *pszName, RTUINTPTR cbSeg, uint32_t fFlags) { /* * Input validation and lazy initialization. */ AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER); *phDbgMod = NIL_RTDBGMOD; AssertPtrReturn(pszName, VERR_INVALID_POINTER); AssertReturn(*pszName, VERR_INVALID_PARAMETER); AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); int rc = rtDbgModLazyInit(); if (RT_FAILURE(rc)) return rc; /* * Allocate a new module instance. */ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod)); if (!pDbgMod) return VERR_NO_MEMORY; pDbgMod->u32Magic = RTDBGMOD_MAGIC; pDbgMod->cRefs = 1; rc = RTCritSectInit(&pDbgMod->CritSect); if (RT_SUCCESS(rc)) { pDbgMod->pszName = RTStrCacheEnter(g_hDbgModStrCache, pszName); if (pDbgMod->pszName) { rc = rtDbgModContainerCreate(pDbgMod, cbSeg); if (RT_SUCCESS(rc)) { *phDbgMod = pDbgMod; return rc; } RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); } RTCritSectDelete(&pDbgMod->CritSect); } RTMemFree(pDbgMod); return rc; }
/** @copydoc RTDBGMODVTDBG::pfnClose */ static DECLCALLBACK(int) rtDbgModContainer_Close(PRTDBGMODINT pMod) { PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; /* * Destroy the symbols and instance data. */ for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++) { RTAvlrUIntPtrDestroy(&pThis->paSegs[iSeg].SymAddrTree, rtDbgModContainer_DestroyTreeNode, NULL); RTStrCacheRelease(g_hDbgModStrCache, pThis->paSegs[iSeg].pszName); pThis->paSegs[iSeg].pszName = NULL; } RTAvlrUIntPtrDestroy(&pThis->AbsAddrTree, rtDbgModContainer_DestroyTreeNode, NULL); pThis->Names = NULL; RTMemFree(pThis->paSegs); pThis->paSegs = NULL; RTMemFree(pThis); return VINF_SUCCESS; }
/** @copydoc RTDBGMODVTDBG::pfnSymbolAdd */ static DECLCALLBACK(int) rtDbgModContainer_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, uint32_t *piOrdinal) { PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; /* * Address validation. The other arguments have already been validated. */ AssertMsgReturn( iSeg == RTDBGSEGIDX_ABS || iSeg < pThis->cSegs, ("iSeg=%#x cSegs=%#x\n", iSeg, pThis->cSegs), VERR_DBG_INVALID_SEGMENT_INDEX); AssertMsgReturn( iSeg >= RTDBGSEGIDX_SPECIAL_FIRST || off <= pThis->paSegs[iSeg].cb, ("off=%RTptr cb=%RTptr cbSeg=%RTptr\n", off, cb, pThis->paSegs[iSeg].cb), VERR_DBG_INVALID_SEGMENT_OFFSET); /* Be a little relaxed wrt to the symbol size. */ int rc = VINF_SUCCESS; if (iSeg != RTDBGSEGIDX_ABS && off + cb > pThis->paSegs[iSeg].cb) { cb = pThis->paSegs[iSeg].cb - off; rc = VINF_DBG_ADJUSTED_SYM_SIZE; } /* * Create a new entry. */ PRTDBGMODCTNSYMBOL pSymbol = (PRTDBGMODCTNSYMBOL)RTMemAllocZ(sizeof(*pSymbol)); if (!pSymbol) return VERR_NO_MEMORY; pSymbol->AddrCore.Key = off; pSymbol->AddrCore.KeyLast = off + (cb ? cb - 1 : 0); pSymbol->OrdinalCore.Key = pThis->iNextSymbolOrdinal; pSymbol->iSeg = iSeg; pSymbol->cb = cb; pSymbol->fFlags = fFlags; pSymbol->NameCore.pszString = RTStrCacheEnterN(g_hDbgModStrCache, pszSymbol, cchSymbol); if (pSymbol->NameCore.pszString) { if (RTStrSpaceInsert(&pThis->Names, &pSymbol->NameCore)) { PAVLRUINTPTRTREE pAddrTree = iSeg == RTDBGSEGIDX_ABS ? &pThis->AbsAddrTree : &pThis->paSegs[iSeg].SymAddrTree; if (RTAvlrUIntPtrInsert(pAddrTree, &pSymbol->AddrCore)) { if (RTAvlU32Insert(&pThis->SymbolOrdinalTree, &pSymbol->OrdinalCore)) { if (piOrdinal) *piOrdinal = pThis->iNextSymbolOrdinal; pThis->iNextSymbolOrdinal++; return rc; } /* bail out */ rc = VERR_INTERNAL_ERROR_5; RTAvlrUIntPtrRemove(pAddrTree, pSymbol->AddrCore.Key); } else rc = VERR_DBG_ADDRESS_CONFLICT; RTStrSpaceRemove(&pThis->Names, pSymbol->NameCore.pszString); } else rc = VERR_DBG_DUPLICATE_SYMBOL; RTStrCacheRelease(g_hDbgModStrCache, pSymbol->NameCore.pszString); } else rc = VERR_NO_MEMORY; RTMemFree(pSymbol); return rc; }
RTDECL(int) RTDbgModCreateFromMap(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName, RTUINTPTR uSubtrahend, uint32_t fFlags) { /* * Input validation and lazy initialization. */ AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER); *phDbgMod = NIL_RTDBGMOD; AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); AssertReturn(*pszFilename, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pszName, VERR_INVALID_POINTER); AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); int rc = rtDbgModLazyInit(); if (RT_FAILURE(rc)) return rc; if (!pszName) pszName = RTPathFilename(pszFilename); /* * Allocate a new module instance. */ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod)); if (!pDbgMod) return VERR_NO_MEMORY; pDbgMod->u32Magic = RTDBGMOD_MAGIC; pDbgMod->cRefs = 1; rc = RTCritSectInit(&pDbgMod->CritSect); if (RT_SUCCESS(rc)) { pDbgMod->pszName = RTStrCacheEnter(g_hDbgModStrCache, pszName); if (pDbgMod->pszName) { pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); if (pDbgMod->pszDbgFile) { /* * Try the map file readers. */ rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT); if (RT_SUCCESS(rc)) { rc = VERR_DBG_NO_MATCHING_INTERPRETER; for (PRTDBGMODREGDBG pCur = g_pDbgHead; pCur; pCur = pCur->pNext) { if (pCur->pVt->fSupports & RT_DBGTYPE_MAP) { pDbgMod->pDbgVt = pCur->pVt; pDbgMod->pvDbgPriv = NULL; rc = pCur->pVt->pfnTryOpen(pDbgMod); if (RT_SUCCESS(rc)) { ASMAtomicIncU32(&pCur->cUsers); RTSemRWReleaseRead(g_hDbgModRWSem); *phDbgMod = pDbgMod; return rc; } } } /* bail out */ RTSemRWReleaseRead(g_hDbgModRWSem); } RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); } RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile); } RTCritSectDelete(&pDbgMod->CritSect); } RTMemFree(pDbgMod); return rc; }
/** * Basic API checks. * We'll return if any of these fails. */ static void tst1(RTSTRCACHE hStrCache) { const char *psz; /* Simple string entering and length. */ RTTESTI_CHECK_RETV(psz = RTStrCacheEnter(hStrCache, "abcdefgh")); RTTESTI_CHECK_RETV(strcmp(psz, "abcdefgh") == 0); RTTESTI_CHECK_RETV(RTStrCacheLength(psz) == strlen("abcdefgh")); RTTESTI_CHECK_RETV(RTStrCacheRelease(hStrCache, psz) == 0); RTTESTI_CHECK_RETV(psz = RTStrCacheEnter(hStrCache, "abcdefghijklmnopqrstuvwxyz")); RTTESTI_CHECK_RETV(strcmp(psz, "abcdefghijklmnopqrstuvwxyz") == 0); RTTESTI_CHECK_RETV(RTStrCacheLength(psz) == strlen("abcdefghijklmnopqrstuvwxyz")); RTTESTI_CHECK_RETV(RTStrCacheRelease(hStrCache, psz) == 0); /* Unterminated strings. */ RTTESTI_CHECK_RETV(psz = RTStrCacheEnterN(hStrCache, "0123456789", 3)); RTTESTI_CHECK_RETV(strcmp(psz, "012") == 0); RTTESTI_CHECK_RETV(RTStrCacheLength(psz) == strlen("012")); RTTESTI_CHECK_RETV(RTStrCacheRelease(hStrCache, psz) == 0); RTTESTI_CHECK_RETV(psz = RTStrCacheEnterN(hStrCache, "0123456789abcdefghijklmnopqrstuvwxyz", 16)); RTTESTI_CHECK_RETV(strcmp(psz, "0123456789abcdef") == 0); RTTESTI_CHECK_RETV(RTStrCacheLength(psz) == strlen("0123456789abcdef")); RTTESTI_CHECK_RETV(RTStrCacheRelease(hStrCache, psz) == 0); /* String referencing. */ char szTest[4096+16]; memset(szTest, 'a', sizeof(szTest)); char szTest2[4096+16]; memset(szTest2, 'f', sizeof(szTest)); for (int32_t i = 4096; i > 3; i /= 3) { void *pv2; RTTESTI_CHECK_RETV(psz = RTStrCacheEnterN(hStrCache, szTest, i)); RTTESTI_CHECK_MSG_RETV((pv2 = ASMMemFirstMismatchingU8(psz, i, 'a')) == NULL && !psz[i], ("i=%#x psz=%p off=%#x\n", i, psz, (uintptr_t)pv2 - (uintptr_t)psz)); RTTESTI_CHECK(RTStrCacheRetain(psz) == 2); RTTESTI_CHECK(RTStrCacheRetain(psz) == 3); RTTESTI_CHECK(RTStrCacheRetain(psz) == 4); RTTESTI_CHECK_MSG_RETV((pv2 = ASMMemFirstMismatchingU8(psz, i, 'a')) == NULL && !psz[i], ("i=%#x psz=%p off=%#x\n", i, psz, (uintptr_t)pv2 - (uintptr_t)psz)); RTTESTI_CHECK(RTStrCacheRelease(hStrCache, psz) == 3); RTTESTI_CHECK_MSG_RETV((pv2 = ASMMemFirstMismatchingU8(psz, i, 'a')) == NULL && !psz[i], ("i=%#x psz=%p off=%#x\n", i, psz, (uintptr_t)pv2 - (uintptr_t)psz)); RTTESTI_CHECK(RTStrCacheRetain(psz) == 4); RTTESTI_CHECK(RTStrCacheRetain(psz) == 5); RTTESTI_CHECK(RTStrCacheRetain(psz) == 6); RTTESTI_CHECK(RTStrCacheRelease(hStrCache, psz) == 5); RTTESTI_CHECK(RTStrCacheRelease(hStrCache, psz) == 4); RTTESTI_CHECK_MSG_RETV((pv2 = ASMMemFirstMismatchingU8(psz, i, 'a')) == NULL && !psz[i], ("i=%#x psz=%p off=%#x\n", i, psz, (uintptr_t)pv2 - (uintptr_t)psz)); for (uint32_t cRefs = 3;; cRefs--) { RTTESTI_CHECK(RTStrCacheRelease(hStrCache, psz) == cRefs); if (cRefs == 0) break; RTTESTI_CHECK_MSG_RETV((pv2 = ASMMemFirstMismatchingU8(psz, i, 'a')) == NULL && !psz[i], ("i=%#x psz=%p off=%#x cRefs=%d\n", i, psz, (uintptr_t)pv2 - (uintptr_t)psz, cRefs)); for (uint32_t j = 0; j < 42; j++) { const char *psz2; RTTESTI_CHECK_RETV(psz2 = RTStrCacheEnterN(hStrCache, szTest2, i)); RTTESTI_CHECK_RETV(psz2 != psz); RTTESTI_CHECK(RTStrCacheRelease(hStrCache, psz2) == 0); RTTESTI_CHECK_MSG_RETV((pv2 = ASMMemFirstMismatchingU8(psz, i, 'a')) == NULL && !psz[i], ("i=%#x psz=%p off=%#x cRefs=%d\n", i, psz, (uintptr_t)pv2 - (uintptr_t)psz, cRefs)); } } } /* Lots of allocations. */ memset(szTest, 'b', sizeof(szTest)); memset(szTest2, 'e', sizeof(szTest)); const char *pszTest1Rets[4096 + 16]; const char *pszTest2Rets[4096 + 16]; for (uint32_t i = 1; i < RT_ELEMENTS(pszTest1Rets); i++) { RTTESTI_CHECK(pszTest1Rets[i] = RTStrCacheEnterN(hStrCache, szTest, i)); RTTESTI_CHECK(strlen(pszTest1Rets[i]) == i); RTTESTI_CHECK(pszTest2Rets[i] = RTStrCacheEnterN(hStrCache, szTest2, i)); RTTESTI_CHECK(strlen(pszTest2Rets[i]) == i); } if (RTStrCacheIsRealImpl()) { for (uint32_t i = 1; i < RT_ELEMENTS(pszTest1Rets); i++) { uint32_t cRefs; const char *psz1, *psz2; RTTESTI_CHECK((psz1 = RTStrCacheEnterN(hStrCache, szTest, i)) == pszTest1Rets[i]); RTTESTI_CHECK((psz2 = RTStrCacheEnterN(hStrCache, szTest2, i)) == pszTest2Rets[i]); RTTESTI_CHECK_MSG((cRefs = RTStrCacheRelease(hStrCache, psz1)) == 1, ("cRefs=%#x i=%#x\n", cRefs, i)); RTTESTI_CHECK_MSG((cRefs = RTStrCacheRelease(hStrCache, psz2)) == 1, ("cRefs=%#x i=%#x\n", cRefs, i)); } } for (uint32_t i = 1; i < RT_ELEMENTS(pszTest1Rets); i++) { uint32_t cRefs; RTTESTI_CHECK(strlen(pszTest1Rets[i]) == i); RTTESTI_CHECK_MSG((cRefs = RTStrCacheRelease(hStrCache, pszTest1Rets[i])) == 0, ("cRefs=%#x i=%#x\n", cRefs, i)); RTTESTI_CHECK(strlen(pszTest2Rets[i]) == i); RTTESTI_CHECK_MSG((cRefs = RTStrCacheRelease(hStrCache, pszTest2Rets[i])) == 0, ("cRefs=%#x i=%#x\n", cRefs, i)); } }