diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-03-26 19:21:20 +0000 |
---|---|---|
committer | <> | 2014-05-08 15:03:54 +0000 |
commit | fb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch) | |
tree | c2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/Runtime/common/dbg/dbgcfg.cpp | |
parent | 58ed4748338f9466599adfc8a9171280ed99e23f (diff) | |
download | VirtualBox-master.tar.gz |
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/Runtime/common/dbg/dbgcfg.cpp')
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgcfg.cpp | 2172 |
1 files changed, 2172 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/dbg/dbgcfg.cpp b/src/VBox/Runtime/common/dbg/dbgcfg.cpp new file mode 100644 index 00000000..77555180 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgcfg.cpp @@ -0,0 +1,2172 @@ +/* $Id: dbgcfg.cpp $ */ +/** @file + * IPRT - Debugging Configuration. + */ + +/* + * Copyright (C) 2013 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 * +*******************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DBG +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/alloca.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/ctype.h> +#include <iprt/dir.h> +#include <iprt/err.h> +#include <iprt/env.h> +#include <iprt/file.h> +#ifdef IPRT_WITH_HTTP +# include <iprt/http.h> +#endif +#include <iprt/list.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/process.h> +#include <iprt/semaphore.h> +#include <iprt/string.h> +#include <iprt/uuid.h> +#include "internal/magics.h" + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * String list entry. + */ +typedef struct RTDBGCFGSTR +{ + /** List entry. */ + RTLISTNODE ListEntry; + /** Domain specific flags. */ + uint16_t fFlags; + /** The length of the string. */ + uint16_t cch; + /** The string. */ + char sz[1]; +} RTDBGCFGSTR; +/** Pointer to a string list entry. */ +typedef RTDBGCFGSTR *PRTDBGCFGSTR; + + +/** + * Configuration instance. + */ +typedef struct RTDBGCFGINT +{ + /** The magic value (RTDBGCFG_MAGIC). */ + uint32_t u32Magic; + /** Reference counter. */ + uint32_t volatile cRefs; + /** Flags, see RTDBGCFG_FLAGS_XXX. */ + uint64_t fFlags; + + /** List of paths to search for debug files and executable images. */ + RTLISTANCHOR PathList; + /** List of debug file suffixes. */ + RTLISTANCHOR SuffixList; + /** List of paths to search for source files. */ + RTLISTANCHOR SrcPathList; + +#ifdef RT_OS_WINDOWS + /** The _NT_ALT_SYMBOL_PATH and _NT_SYMBOL_PATH combined. */ + RTLISTANCHOR NtSymbolPathList; + /** The _NT_EXECUTABLE_PATH. */ + RTLISTANCHOR NtExecutablePathList; + /** The _NT_SOURCE_PATH. */ + RTLISTANCHOR NtSourcePath; +#endif + + /** Log callback function. */ + PFNRTDBGCFGLOG pfnLogCallback; + /** User argument to pass to the log callback. */ + void *pvLogUser; + + /** Critical section protecting the instance data. */ + RTCRITSECTRW CritSect; +} *PRTDBGCFGINT; + +/** + * Mnemonics map entry for a 64-bit unsigned property value. + */ +typedef struct RTDBGCFGU64MNEMONIC +{ + /** The flags to set or clear. */ + uint64_t fFlags; + /** The mnemonic. */ + const char *pszMnemonic; + /** The length of the mnemonic. */ + uint8_t cchMnemonic; + /** If @c true, the bits in fFlags will be set, if @c false they will be + * cleared. */ + bool fSet; +} RTDBGCFGU64MNEMONIC; +/** Pointer to a read only mnemonic map entry for a uint64_t property. */ +typedef RTDBGCFGU64MNEMONIC const *PCRTDBGCFGU64MNEMONIC; + + +/** @name Open flags. + * @{ */ +/** The operative system mask. The values are RT_OPSYS_XXX. */ +#define RTDBGCFG_O_OPSYS_MASK UINT32_C(0x000000ff) +/** The files may be compressed MS styled. */ +#define RTDBGCFG_O_MAYBE_COMPRESSED_MS RT_BIT_32(26) +/** Whether to make a recursive search. */ +#define RTDBGCFG_O_RECURSIVE RT_BIT_32(27) +/** We're looking for a separate debug file. */ +#define RTDBGCFG_O_EXT_DEBUG_FILE RT_BIT_32(28) +/** We're looking for an executable image. */ +#define RTDBGCFG_O_EXECUTABLE_IMAGE RT_BIT_32(29) +/** The file search should be done in an case insensitive fashion. */ +#define RTDBGCFG_O_CASE_INSENSITIVE RT_BIT_32(30) +/** Use Windbg style symbol servers when encountered in the path. */ +#define RTDBGCFG_O_SYMSRV RT_BIT_32(31) +/** @} */ + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** Validates a debug module handle and returns rc if not valid. */ +#define RTDBGCFG_VALID_RETURN_RC(pThis, rc) \ + do { \ + AssertPtrReturn((pThis), (rc)); \ + AssertReturn((pThis)->u32Magic == RTDBGCFG_MAGIC, (rc)); \ + AssertReturn((pThis)->cRefs > 0, (rc)); \ + } while (0) + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** Mnemonics map for RTDBGCFGPROP_FLAGS. */ +static const RTDBGCFGU64MNEMONIC g_aDbgCfgFlags[] = +{ + { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("deferred"), true }, + { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("nodeferred"), false }, + { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("symsrv"), false }, + { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("nosymsrv"), true }, + { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("syspaths"), false }, + { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("nosyspaths"), true }, + { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("rec"), false }, + { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("norec"), true }, + { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("recsrc"), false }, + { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("norecsrc"), true }, + { 0, NULL, 0, false } +}; + + + +/** + * Runtime logging, level 1. + * + * @param pThis The debug config instance data. + * @param pszFormat The message format string. + * @param ... Arguments references in the format string. + */ +static void rtDbgCfgLog1(PRTDBGCFGINT pThis, const char *pszFormat, ...) +{ + if (LogIsEnabled() || (pThis && pThis->pfnLogCallback)) + { + va_list va; + va_start(va, pszFormat); + char *pszMsg = RTStrAPrintf2V(pszFormat, va); + va_end(va); + + Log(("RTDbgCfg: %s", pszMsg)); + if (pThis && pThis->pfnLogCallback) + pThis->pfnLogCallback(pThis, 1, pszMsg, pThis->pvLogUser); + RTStrFree(pszMsg); + } +} + + +/** + * Runtime logging, level 2. + * + * @param pThis The debug config instance data. + * @param pszFormat The message format string. + * @param ... Arguments references in the format string. + */ +static void rtDbgCfgLog2(PRTDBGCFGINT pThis, const char *pszFormat, ...) +{ + if (LogIs2Enabled() || (pThis && pThis->pfnLogCallback)) + { + va_list va; + va_start(va, pszFormat); + char *pszMsg = RTStrAPrintf2V(pszFormat, va); + va_end(va); + + Log(("RTDbgCfg: %s", pszMsg)); + if (pThis && pThis->pfnLogCallback) + pThis->pfnLogCallback(pThis, 2, pszMsg, pThis->pvLogUser); + RTStrFree(pszMsg); + } +} + + +/** + * Checks if the file system at the given path is case insensitive or not. + * + * @returns true / false + * @param pszPath The path to query about. + */ +static int rtDbgCfgIsFsCaseInsensitive(const char *pszPath) +{ + RTFSPROPERTIES Props; + int rc = RTFsQueryProperties(pszPath, &Props); + if (RT_FAILURE(rc)) + return RT_OPSYS == RT_OPSYS_DARWIN + || RT_OPSYS == RT_OPSYS_DOS + || RT_OPSYS == RT_OPSYS_OS2 + || RT_OPSYS == RT_OPSYS_NT + || RT_OPSYS == RT_OPSYS_WINDOWS; + return !Props.fCaseSensitive; +} + + +/** + * Worker that does case sensitive file/dir searching. + * + * @returns true / false. + * @param pszPath The path buffer containing an existing directory. + * RTPATH_MAX in size. On success, this will contain + * the combined path with @a pszName case correct. + * @param offLastComp The offset of the last component (for chopping it + * off). + * @param pszName What we're looking for. + * @param enmType What kind of thing we're looking for. + */ +static bool rtDbgCfgIsXxxxAndFixCaseWorker(char *pszPath, size_t offLastComp, const char *pszName, + RTDIRENTRYTYPE enmType) +{ + /** @todo IPRT should generalize this so we can use host specific tricks to + * speed it up. */ + + /* Return straight away if the name isn't case foldable. */ + if (!RTStrIsCaseFoldable(pszName)) + return false; + + /* + * Try some simple case folding games. + */ + RTStrToLower(&pszPath[offLastComp]); + if (RTFileExists(pszPath)) + return true; + + RTStrToUpper(&pszPath[offLastComp]); + if (RTFileExists(pszPath)) + return true; + + /* + * Open the directory and check each entry in it. + */ + pszPath[offLastComp] = '\0'; + PRTDIR pDir; + int rc = RTDirOpen(&pDir, pszPath); + if (RT_FAILURE(rc)) + return false; + + for (;;) + { + /* Read the next entry. */ + union + { + RTDIRENTRY Entry; + uint8_t ab[_4K]; + } u; + size_t cbBuf = sizeof(u); + rc = RTDirRead(pDir, &u.Entry, &cbBuf); + if (RT_FAILURE(rc)) + break; + + if ( !RTStrICmp(pszName, u.Entry.szName) + && ( u.Entry.enmType == enmType + || u.Entry.enmType == RTDIRENTRYTYPE_UNKNOWN + || u.Entry.enmType == RTDIRENTRYTYPE_SYMLINK) ) + { + pszPath[offLastComp] = '\0'; + rc = RTPathAppend(pszPath, RTPATH_MAX, u.Entry.szName); + if ( u.Entry.enmType != enmType + && RT_SUCCESS(rc)) + RTDirQueryUnknownType(pszPath, true /*fFollowSymlinks*/, &u.Entry.enmType); + + if ( u.Entry.enmType == enmType + || RT_FAILURE(rc)) + { + RTDirClose(pDir); + if (RT_FAILURE(rc)) + { + pszPath[offLastComp] = '\0'; + return false; + } + return true; + } + } + } + + RTDirClose(pDir); + pszPath[offLastComp] = '\0'; + + return false; +} + + +/** + * Appends @a pszSubDir to @a pszPath and check whether it exists and is a + * directory. + * + * If @a fCaseInsensitive is set, we will do a case insensitive search for a + * matching sub directory. + * + * @returns true / false + * @param pszPath The path buffer containing an existing + * directory. RTPATH_MAX in size. + * @param pszSubDir The sub directory to append. + * @param fCaseInsensitive Whether case insensitive searching is required. + */ +static bool rtDbgCfgIsDirAndFixCase(char *pszPath, const char *pszSubDir, bool fCaseInsensitive) +{ + /* Save the length of the input path so we can restore it in the case + insensitive branch further down. */ + size_t const cchPath = strlen(pszPath); + + /* + * Append the sub directory and check if we got a hit. + */ + int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir); + if (RT_FAILURE(rc)) + return false; + + if (RTDirExists(pszPath)) + return true; + + /* + * Do case insensitive lookup if requested. + */ + if (fCaseInsensitive) + return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, pszSubDir, RTDIRENTRYTYPE_DIRECTORY); + + pszPath[cchPath] = '\0'; + return false; +} + + +/** + * Appends @a pszFilename to @a pszPath and check whether it exists and is a + * directory. + * + * If @a fCaseInsensitive is set, we will do a case insensitive search for a + * matching filename. + * + * @returns true / false + * @param pszPath The path buffer containing an existing + * directory. RTPATH_MAX in size. + * @param pszFilename The file name to append. + * @param fCaseInsensitive Whether case insensitive searching is required. + * @param fMsCompressed Whether to look for the MS compressed file name + * variant. + * @param pfProbablyCompressed This is set to true if a MS compressed + * filename variant is returned. Optional. + */ +static bool rtDbgCfgIsFileAndFixCase(char *pszPath, const char *pszFilename, bool fCaseInsensitive, + bool fMsCompressed, bool *pfProbablyCompressed) +{ + /* Save the length of the input path so we can restore it in the case + insensitive branch further down. */ + size_t cchPath = strlen(pszPath); + if (pfProbablyCompressed) + *pfProbablyCompressed = false; + + /* + * Append the filename and check if we got a hit. + */ + int rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename); + if (RT_FAILURE(rc)) + return false; + + if (RTFileExists(pszPath)) + return true; + + /* + * Do case insensitive file lookup if requested. + */ + if (fCaseInsensitive) + { + if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, pszFilename, RTDIRENTRYTYPE_FILE)) + return true; + } + + /* + * Look for MS compressed file if requested. + */ + if ( fMsCompressed + && (unsigned char)pszFilename[strlen(pszFilename) - 1] < 0x7f) + { + pszPath[cchPath] = '\0'; + rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename); + AssertRCReturn(rc, false); + pszPath[strlen(pszPath) - 1] = '_'; + + if (pfProbablyCompressed) + *pfProbablyCompressed = true; + + if (RTFileExists(pszPath)) + return true; + + if (fCaseInsensitive) + { + /* Note! Ugly hack here, the pszName parameter points into pszPath! */ + if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTPathFilename(pszPath), RTDIRENTRYTYPE_FILE)) + return true; + } + + if (pfProbablyCompressed) + *pfProbablyCompressed = false; + } + + pszPath[cchPath] = '\0'; + return false; +} + + +static int rtDbgCfgTryOpenDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn, uint32_t fFlags, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + int rcRet = VWRN_NOT_FOUND; + int rc2; + + /* If the directory doesn't exist, just quit immediately. + Note! Our case insensitivity doesn't extend to the search dirs themselfs, + only to the bits under neath them. */ + if (!RTDirExists(pszPath)) + { + rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath); + return rcRet; + } + + /* Figure out whether we have to do a case sensitive search or not. + Note! As a simplification, we don't ask for case settings in each + directory under the user specified path, we assume the file + systems that mounted there have compatible settings. Faster + that way. */ + bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE) + && !rtDbgCfgIsFsCaseInsensitive(pszPath); + + size_t const cchPath = strlen(pszPath); + + /* + * Look for the file with less and less of the original path given. + */ + for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++) + { + pszPath[cchPath] = '\0'; + + rc2 = VINF_SUCCESS; + for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++) + if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive)) + rc2 = VERR_FILE_NOT_FOUND; + + if (RT_SUCCESS(rc2)) + { + if (rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], fCaseInsensitive, false, NULL)) + { + rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath); + rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + { + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath); + else + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath); + return rc2; + } + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + } + } + + /* + * Do a recursive search if requested. + */ + if ( (fFlags & RTDBGCFG_O_RECURSIVE) + && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) ) + { + /** @todo Recursive searching will be done later. */ + } + + return rcRet; +} + +static int rtDbgCfgUnpackMsCacheFile(PRTDBGCFGINT pThis, char *pszPath, const char *pszFilename) +{ + rtDbgCfgLog2(pThis, "Unpacking '%s'...\n", pszPath); + + /* + * Duplicate the source file path, just for simplicity and restore the + * final character in the orignal. We cheerfully ignorining any + * possibility of multibyte UTF-8 sequences just like the caller did when + * setting it to '_'. + */ + char *pszSrcArchive = RTStrDup(pszPath); + if (!pszSrcArchive) + return VERR_NO_STR_MEMORY; + + pszPath[strlen(pszPath) - 1] = RT_C_TO_LOWER(pszFilename[strlen(pszFilename) - 1]); + + + /* + * Figuring out the argument list for the platform specific unpack util. + */ +#ifdef RT_OS_WINDOWS + const char *papszArgs[] = + { + "expand.exe", + pszSrcArchive, + pszPath, + NULL + }; + +#else + char szExtractDir[RTPATH_MAX]; + strcpy(szExtractDir, pszPath); + RTPathStripFilename(szExtractDir); + + const char *papszArgs[] = + { + "cabextract", + "-L", /* Lower case extracted files. */ + "-d", szExtractDir, /* Extraction path */ + pszSrcArchive, + NULL + }; +#endif + + /* + * Do the unpacking. + */ + RTPROCESS hChild; + int rc = RTProcCreate(papszArgs[0], papszArgs, RTENV_DEFAULT, +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + RTPROC_FLAGS_NO_WINDOW | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SEARCH_PATH, +#else + RTPROC_FLAGS_SEARCH_PATH, +#endif + &hChild); + if (RT_SUCCESS(rc)) + { + RTPROCSTATUS ProcStatus; + rc = RTProcWait(hChild, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus); + if (RT_SUCCESS(rc)) + { + if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL + && ProcStatus.iStatus == 0) + { + if (RTPathExists(pszPath)) + { + rtDbgCfgLog1(pThis, "Successfully unpacked '%s' to '%s'.\n", pszSrcArchive, pszPath); + rc = VINF_SUCCESS; + } + else + { + rtDbgCfgLog1(pThis, "Successfully ran unpacker on '%s', but '%s' is missing!\n", pszSrcArchive, pszPath); + rc = VERR_ZIP_ERROR; + } + } + else + { + rtDbgCfgLog2(pThis, "Unpacking '%s' failed: iStatus=%d enmReason=%d\n", + pszSrcArchive, ProcStatus.iStatus, ProcStatus.enmReason); + rc = VERR_ZIP_CORRUPTED; + } + } + else + rtDbgCfgLog1(pThis, "Error waiting for process: %Rrc\n", rc); + + } + else + rtDbgCfgLog1(pThis, "Error starting unpack process '%s': %Rrc\n", papszArgs[0], rc); + + return rc; +} + +static int rtDbgCfgTryDownloadAndOpen(PRTDBGCFGINT pThis, const char *pszServer, + char *pszPath, const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn, + uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ +#ifdef IPRT_WITH_HTTP + if (pThis->fFlags & RTDBGCFG_FLAGS_NO_SYM_SRV) + return VWRN_NOT_FOUND; + if (!pszCacheSubDir || !*pszCacheSubDir) + return VWRN_NOT_FOUND; + + /* + * Create the path. + */ + size_t cchTmp = strlen(pszPath); + + int rc = RTDirCreateFullPath(pszPath, 0766); + if (!RTDirExists(pszPath)) + { + Log(("Error creating cache dir '%s': %Rrc\n", pszPath, rc)); + return rc; + } + + const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1]; + rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename); + if (RT_FAILURE(rc)) + return rc; + RTStrToLower(&pszPath[cchTmp]); + if (!RTDirExists(pszPath)) + { + rc = RTDirCreate(pszPath, 0766, 0); + if (RT_FAILURE(rc)) + { + Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc)); + } + } + + rc = RTPathAppend(pszPath, RTPATH_MAX, pszCacheSubDir); + if (RT_FAILURE(rc)) + return rc; + if (!RTDirExists(pszPath)) + { + rc = RTDirCreate(pszPath, 0766, 0); + if (RT_FAILURE(rc)) + { + Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc)); + } + } + + /* Prepare the destination file name while we're here. */ + cchTmp = strlen(pszPath); + RTStrToLower(&pszPath[cchTmp]); + rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename); + if (RT_FAILURE(rc)) + return rc; + + /* + * Download the file. + */ + RTHTTP hHttp; + rc = RTHttpCreate(&hHttp); + if (RT_FAILURE(rc)) + return rc; + RTHttpUseSystemProxySettings(hHttp); + + static const char * const s_apszHeaders[] = + { + "User-Agent: Microsoft-Symbol-Server/6.6.0999.9", + "Pragma: no-cache", + }; + + rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeaders), s_apszHeaders); + if (RT_SUCCESS(rc)) + { + char szUrl[_2K]; + RTStrPrintf(szUrl, sizeof(szUrl), "%s/%s/%s/%s", pszServer, pszFilename, pszCacheSubDir, pszFilename); + + /** @todo Use some temporary file name and rename it after the operation + * since not all systems support read-deny file sharing + * settings. */ + rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath); + rc = RTHttpGetFile(hHttp, szUrl, pszPath); + if (RT_FAILURE(rc)) + { + RTFileDelete(pszPath); + rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, pszPath); + } + if (rc == VERR_HTTP_NOT_FOUND) + { + /* Try the compressed version of the file. */ + pszPath[strlen(pszPath) - 1] = '_'; + szUrl[strlen(szUrl) - 1] = '_'; + rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath); + rc = RTHttpGetFile(hHttp, szUrl, pszPath); + if (RT_SUCCESS(rc)) + rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename); + else + { + rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, pszPath); + RTFileDelete(pszPath); + } + } + } + + RTHttpDestroy(hHttp); + + /* + * If we succeeded, give it a try. + */ + if (RT_SUCCESS(rc)) + { + Assert(RTFileExists(pszPath)); + rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath); + rc = pfnCallback(pThis, pszPath, pvUser1, pvUser2); + if (rc == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath); + else if (rc == VERR_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath); + else + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc, pszPath); + } + + return rc; + +#else /* !IPRT_WITH_HTTP */ + return VWRN_NOT_FOUND; +#endif /* !IPRT_WITH_HTTP */ +} + + +static int rtDbgCfgCopyFileToCache(PRTDBGCFGINT pThis, char const *pszSrc, const char *pchCache, size_t cchCache, + const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn) +{ + if (!pszCacheSubDir || !*pszCacheSubDir) + return VINF_SUCCESS; + + /** @todo copy to cache */ + return VINF_SUCCESS; +} + + +static int rtDbgCfgTryOpenCache(PRTDBGCFGINT pThis, char *pszPath, const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn, + uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + /* + * If the cache doesn't exist, fail right away. + */ + if (!pszCacheSubDir || !*pszCacheSubDir) + return VWRN_NOT_FOUND; + if (!RTDirExists(pszPath)) + { + rtDbgCfgLog2(pThis, "Cache does not exist: '%s'\n", pszPath); + return VWRN_NOT_FOUND; + } + + size_t cchPath = strlen(pszPath); + + /* + * Carefully construct the cache path with case insensitivity in mind. + */ + bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE) + && !rtDbgCfgIsFsCaseInsensitive(pszPath); + const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1]; + + if (!rtDbgCfgIsDirAndFixCase(pszPath, pszFilename, fCaseInsensitive)) + return VWRN_NOT_FOUND; + + if (!rtDbgCfgIsDirAndFixCase(pszPath, pszCacheSubDir, fCaseInsensitive)) + return VWRN_NOT_FOUND; + + bool fProbablyCompressed = false; + if (!rtDbgCfgIsFileAndFixCase(pszPath, pszFilename, fCaseInsensitive, + RT_BOOL(fFlags & RTDBGCFG_O_MAYBE_COMPRESSED_MS), &fProbablyCompressed)) + return VWRN_NOT_FOUND; + if (fProbablyCompressed) + { + int rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename); + if (RT_FAILURE(rc)) + return VWRN_NOT_FOUND; + } + + rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath); + int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath); + else if (rc2 == VERR_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath); + else + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath); + return rc2; +} + + +static int rtDbgCfgTryOpenList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, const char *pszCacheSubDir, + uint32_t fFlags, char *pszPath, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + int rcRet = VWRN_NOT_FOUND; + int rc2; + + const char *pchCache = NULL; + size_t cchCache = 0; + int rcCache = VWRN_NOT_FOUND; + + PRTDBGCFGSTR pCur; + RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry) + { + size_t cchDir = pCur->cch; + const char *pszDir = pCur->sz; + rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir); + + /* This is very simplistic, but we have a unreasonably large path + buffer, so it'll work just fine and simplify things greatly below. */ + if (cchDir >= RTPATH_MAX - 8U) + { + if (RT_SUCCESS_NP(rcRet)) + rcRet = VERR_FILENAME_TOO_LONG; + continue; + } + + /* + * Process the path according to it's type. + */ + if (!strncmp(pszDir, RT_STR_TUPLE("srv*"))) + { + /* + * Symbol server. + */ + pszDir += sizeof("srv*") - 1; + cchDir -= sizeof("srv*") - 1; + bool fSearchCache = false; + const char *pszServer = (const char *)memchr(pszDir, '*', cchDir); + if (!pszServer) + pszServer = pszDir; + else if (pszServer == pszDir) + continue; + { + fSearchCache = true; + pchCache = pszDir; + cchCache = pszServer - pszDir; + pszServer++; + } + + /* We don't have any default cache directory, so skip if the cache is missing. */ + if (cchCache == 0) + continue; + + /* Search the cache first (if we haven't already done so). */ + if (fSearchCache) + { + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, pszCacheSubDir, pSplitFn, fFlags, + pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + + /* Try downloading the file. */ + if (rcCache == VWRN_NOT_FOUND) + { + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pSplitFn, fFlags, + pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + } + else if (!strncmp(pszDir, RT_STR_TUPLE("cache*"))) + { + /* + * Cache directory. + */ + pszDir += sizeof("cache*") - 1; + cchDir -= sizeof("cache*") - 1; + if (!cchDir) + continue; + pchCache = pszDir; + cchCache = cchDir; + + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, pszCacheSubDir, pSplitFn, fFlags, + pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + else + { + /* + * Normal directory. Check for our own 'rec*' and 'norec*' prefix + * flags governing recursive searching. + */ + uint32_t fFlagsDir = fFlags; + if (!strncmp(pszDir, RT_STR_TUPLE("rec*"))) + { + pszDir += sizeof("rec*") - 1; + cchDir -= sizeof("rec*") - 1; + fFlagsDir |= RTDBGCFG_O_RECURSIVE; + } + else if (!strncmp(pszDir, RT_STR_TUPLE("norec*"))) + { + pszDir += sizeof("norec*") - 1; + cchDir -= sizeof("norec*") - 1; + fFlagsDir &= ~RTDBGCFG_O_RECURSIVE; + } + + /* Copy the path into the buffer and do the searching. */ + memcpy(pszPath, pszDir, cchDir); + pszPath[cchDir] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rc2 = rtDbgCfgTryOpenDir(pThis, pszPath, pSplitFn, fFlagsDir, pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + { + if ( rc2 == VINF_CALLBACK_RETURN + && cchCache > 0) + rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache, pszCacheSubDir, pSplitFn); + return rc2; + } + } + + /* Propagate errors. */ + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + + return rcRet; +} + + +/** + * Common worker routine for Image and debug info opening. + * + * This will not search using for suffixes. + * + * @returns IPRT status code. + * @param hDbgCfg The debugging configuration handle. NIL_RTDBGCFG is + * accepted, but the result is that no paths will be + * searched beyond the given and the current directory. + * @param pszFilename The filename to search for. This may or may not + * include a full or partial path. + * @param pszCacheSubDir The cache subdirectory to look in. + * @param fFlags Flags and hints. + * @param pfnCallback The open callback routine. + * @param pvUser1 User parameter 1. + * @param pvUser2 User parameter 2. + */ +static int rtDbgCfgOpenWithSubDir(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir, + uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + int rcRet = VINF_SUCCESS; + int rc2; + + /* + * Do a little validating first. + */ + PRTDBGCFGINT pThis = hDbgCfg; + if (pThis != NIL_RTDBGCFG) + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + else + pThis = NULL; + AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); + AssertPtrReturn(pszCacheSubDir, VERR_INVALID_POINTER); + AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER); + + /* + * Do some guessing as to the way we should parse the filename and whether + * it's case exact or not. + */ + bool fDosPath = strchr(pszFilename, ':') != NULL + || strchr(pszFilename, '\\') != NULL + || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK) + || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE); + if (fDosPath) + fFlags |= RTDBGCFG_O_CASE_INSENSITIVE; + + rtDbgCfgLog2(pThis, "Looking for '%s' w/ cache subdir '%s' and %#x flags...\n", pszFilename, pszCacheSubDir, fFlags); + + PRTPATHSPLIT pSplitFn; + rc2 = RTPathSplitA(pszFilename, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX); + if (RT_FAILURE(rc2)) + return rc2; + AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY); + + /* + * Try the stored file name first if it has a kind of absolute path. + */ + char szPath[RTPATH_MAX]; + if (RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps)) + { + rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath)); + if (RT_SUCCESS(rc2) && RTFileExists(szPath)) + { + RTPathChangeToUnixSlashes(szPath, false); + rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath); + rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath); + else if (rc2 == VERR_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath); + else + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath); + } + } + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN) + { + /* + * Try the current directory (will take cover relative paths + * skipped above). + */ + rc2 = RTPathGetCurrent(szPath, sizeof(szPath)); + if (RT_FAILURE(rc2)) + strcpy(szPath, "."); + RTPathChangeToUnixSlashes(szPath, false); + + rc2 = rtDbgCfgTryOpenDir(pThis, szPath, pSplitFn, fFlags, pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN + && pThis) + { + rc2 = RTCritSectRwEnterShared(&pThis->CritSect); + if (RT_SUCCESS(rc2)) + { + /* + * Run the applicable lists. + */ + rc2 = rtDbgCfgTryOpenList(pThis, &pThis->PathList, pSplitFn, pszCacheSubDir, fFlags, szPath, + pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + +#ifdef RT_OS_WINDOWS + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN + && (fFlags & RTDBGCFG_O_EXECUTABLE_IMAGE) + && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) ) + { + rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtExecutablePathList, pSplitFn, pszCacheSubDir, fFlags, szPath, + pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN + && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) ) + { + rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtSymbolPathList, pSplitFn, pszCacheSubDir, fFlags, szPath, + pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } +#endif + RTCritSectRwLeaveShared(&pThis->CritSect); + } + else if (RT_SUCCESS(rcRet)) + rcRet = rc2; + } + } + + RTPathSplitFree(pSplitFn); + if ( rc2 == VINF_CALLBACK_RETURN + || rc2 == VERR_CALLBACK_RETURN) + rcRet = rc2; + else if (RT_SUCCESS(rcRet)) + rcRet = VERR_NOT_FOUND; + return rcRet; +} + + +RTDECL(int) RTDbgCfgOpenPeImage(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, + RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE + | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXECUTABLE_IMAGE, + pfnCallback, pvUser1, pvUser2); +} + + +RTDECL(int) RTDbgCfgOpenPdb70(RTDBGCFG hDbgCfg, const char *pszFilename, PCRTUUID pUuid, uint32_t uAge, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[64]; + if (!pUuid) + szSubDir[0] = '\0'; + else + { + /* Stringify the UUID and remove the dashes. */ + int rc2 = RTUuidToStr(pUuid, szSubDir, sizeof(szSubDir)); + AssertRCReturn(rc2, rc2); + + char *pszSrc = szSubDir; + char *pszDst = szSubDir; + char ch; + while ((ch = *pszSrc++)) + if (ch != '-') + *pszDst++ = RT_C_TO_UPPER(ch); + + RTStrPrintf(pszDst, &szSubDir[sizeof(szSubDir)] - pszDst, "%X", uAge); + } + + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, + RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE + | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE, + pfnCallback, pvUser1, pvUser2); +} + + +RTDECL(int) RTDbgCfgOpenPdb20(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, uint32_t uAge, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + /** @todo test this! */ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, uAge); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, + RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE + | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE, + pfnCallback, pvUser1, pvUser2); +} + + +RTDECL(int) RTDbgCfgOpenDbg(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, + RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE + | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE, + pfnCallback, pvUser1, pvUser2); +} + + +RTDECL(int) RTDbgCfgOpenDwo(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t uCrc32, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08x", uCrc32); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, + RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE, + pfnCallback, pvUser1, pvUser2); +} + + + +/* + * + * D a r w i n . d S Y M b u n d l e s + * D a r w i n . d S Y M b u n d l e s + * D a r w i n . d S Y M b u n d l e s + * + */ + +/** + * Very similar to rtDbgCfgTryOpenDir. + */ +static int rtDbgCfgTryOpenDsymBundleInDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn, const char *pszDsymName, + uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + int rcRet = VWRN_NOT_FOUND; + int rc2; + + /* If the directory doesn't exist, just quit immediately. + Note! Our case insensitivity doesn't extend to the search dirs themselfs, + only to the bits under neath them. */ + if (!RTDirExists(pszPath)) + { + rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath); + return rcRet; + } + + /* Figure out whether we have to do a case sensitive search or not. + Note! As a simplification, we don't ask for case settings in each + directory under the user specified path, we assume the file + systems that mounted there have compatible settings. Faster + that way. */ + bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE) + && !rtDbgCfgIsFsCaseInsensitive(pszPath); + + size_t const cchPath = strlen(pszPath); + + /* + * Look for the file with less and less of the original path given. + */ + for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++) + { + pszPath[cchPath] = '\0'; + + rc2 = VINF_SUCCESS; + for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++) + if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive)) + rc2 = VERR_FILE_NOT_FOUND; + if ( RT_SUCCESS(rc2) + && !rtDbgCfgIsDirAndFixCase(pszPath, pszDsymName, fCaseInsensitive) + && !rtDbgCfgIsDirAndFixCase(pszPath, "Contents", fCaseInsensitive) + && !rtDbgCfgIsDirAndFixCase(pszPath, "Resources", fCaseInsensitive) + && !rtDbgCfgIsDirAndFixCase(pszPath, "DWARF", fCaseInsensitive)) + { + if (rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], fCaseInsensitive, false, NULL)) + { + rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath); + rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + { + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath); + else + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath); + return rc2; + } + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + } + rc2 = VERR_FILE_NOT_FOUND; + } + + /* + * Do a recursive search if requested. + */ + if ( (fFlags & RTDBGCFG_O_RECURSIVE) + && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) ) + { + /** @todo Recursive searching will be done later. */ + } + + return rcRet; +} + + +/** + * Very similar to rtDbgCfgTryOpenList. + */ +static int rtDbgCfgTryOpenDsumBundleInList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, + const char *pszDsymName, const char *pszCacheSubDir, uint32_t fFlags, char *pszPath, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + int rcRet = VWRN_NOT_FOUND; + int rc2; + + const char *pchCache = NULL; + size_t cchCache = 0; + int rcCache = VWRN_NOT_FOUND; + + PRTDBGCFGSTR pCur; + RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry) + { + size_t cchDir = pCur->cch; + const char *pszDir = pCur->sz; + rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir); + + /* This is very simplistic, but we have a unreasonably large path + buffer, so it'll work just fine and simplify things greatly below. */ + if (cchDir >= RTPATH_MAX - 8U) + { + if (RT_SUCCESS_NP(rcRet)) + rcRet = VERR_FILENAME_TOO_LONG; + continue; + } + + /* + * Process the path according to it's type. + */ + if (!strncmp(pszDir, RT_STR_TUPLE("srv*"))) + { + /* + * Symbol server. + */ + pszDir += sizeof("srv*") - 1; + cchDir -= sizeof("srv*") - 1; + bool fSearchCache = false; + const char *pszServer = (const char *)memchr(pszDir, '*', cchDir); + if (!pszServer) + pszServer = pszDir; + else if (pszServer == pszDir) + continue; + { + fSearchCache = true; + pchCache = pszDir; + cchCache = pszServer - pszDir; + pszServer++; + } + + /* We don't have any default cache directory, so skip if the cache is missing. */ + if (cchCache == 0) + continue; + + /* Search the cache first (if we haven't already done so). */ + if (fSearchCache) + { + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, pszCacheSubDir, pSplitFn, fFlags, + pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + + /* Try downloading the file. */ + if (rcCache == VWRN_NOT_FOUND) + { + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pSplitFn, fFlags, + pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + } + else if (!strncmp(pszDir, RT_STR_TUPLE("cache*"))) + { + /* + * Cache directory. + */ + pszDir += sizeof("cache*") - 1; + cchDir -= sizeof("cache*") - 1; + if (!cchDir) + continue; + pchCache = pszDir; + cchCache = cchDir; + + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, pszCacheSubDir, pSplitFn, fFlags, + pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + else + { + /* + * Normal directory. Check for our own 'rec*' and 'norec*' prefix + * flags governing recursive searching. + */ + uint32_t fFlagsDir = fFlags; + if (!strncmp(pszDir, RT_STR_TUPLE("rec*"))) + { + pszDir += sizeof("rec*") - 1; + cchDir -= sizeof("rec*") - 1; + fFlagsDir |= RTDBGCFG_O_RECURSIVE; + } + else if (!strncmp(pszDir, RT_STR_TUPLE("norec*"))) + { + pszDir += sizeof("norec*") - 1; + cchDir -= sizeof("norec*") - 1; + fFlagsDir &= ~RTDBGCFG_O_RECURSIVE; + } + + /* Copy the path into the buffer and do the searching. */ + memcpy(pszPath, pszDir, cchDir); + pszPath[cchDir] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, pszPath, pSplitFn, pszDsymName, fFlagsDir, + pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + { + if ( rc2 == VINF_CALLBACK_RETURN + && cchCache > 0) + rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache, pszCacheSubDir, pSplitFn); + return rc2; + } + } + + /* Propagate errors. */ + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + + return rcRet; +} + + +RTDECL(int) RTDbgCfgOpenDsymBundle(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + /* + * Bundles are directories, means we can forget about sharing code much + * with the other RTDbgCfgOpenXXX methods. Thus we're duplicating a lot of + * code from rtDbgCfgOpenWithSubDir with .dSYM related adjustments, so, a bug + * found here or there probably means the other version needs updating. + */ + int rcRet = VINF_SUCCESS; + int rc2; + + //RTStrPrintf(szFile, sizeof(szFile), "%s.dSYM/Contents/Resources/DWARF/%s", pszFilename, pszFilename); + + /* + * Do a little validating first. + */ + PRTDBGCFGINT pThis = hDbgCfg; + if (pThis != NIL_RTDBGCFG) + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + else + pThis = NULL; + AssertPtrReturn(pszImage, VERR_INVALID_POINTER); + AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER); + + /* + * Set up rtDbgCfgOpenWithSubDir parameters. + */ + uint32_t fFlags = RTDBGCFG_O_EXT_DEBUG_FILE | RT_OPSYS_DARWIN; + const char *pszCacheSubDir = NULL; + char szCacheSubDir[RTUUID_STR_LENGTH]; + if (pUuid) + { + RTUuidToStr(pUuid, szCacheSubDir, sizeof(szCacheSubDir)); + pszCacheSubDir = szCacheSubDir; + } + + /* + * Do some guessing as to the way we should parse the filename and whether + * it's case exact or not. + */ + bool fDosPath = strchr(pszImage, ':') != NULL + || strchr(pszImage, '\\') != NULL + || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK) + || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE); + if (fDosPath) + fFlags |= RTDBGCFG_O_CASE_INSENSITIVE; + + rtDbgCfgLog2(pThis, "Looking for '%s' with %#x flags...\n", pszImage, fFlags); + + PRTPATHSPLIT pSplitFn; + rc2 = RTPathSplitA(pszImage, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX); + if (RT_FAILURE(rc2)) + return rc2; + AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY); + + /* + * Try the image directory first. + */ + char szPath[RTPATH_MAX]; + if (pSplitFn->cComps > 0) + { + rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath)); + if (RT_SUCCESS(rc2)) + rc2 = RTStrCat(szPath, sizeof(szPath), + ".dSYM" RTPATH_SLASH_STR "Contents" RTPATH_SLASH_STR "Resources" RTPATH_SLASH_STR "DWARF"); + if (RT_SUCCESS(rc2)) + rc2 = RTPathAppend(szPath, sizeof(szPath), pSplitFn->apszComps[pSplitFn->cComps - 1]); + if (RT_SUCCESS(rc2)) + { + RTPathChangeToUnixSlashes(szPath, false); + rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath); + rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath); + else if (rc2 == VERR_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath); + else + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath); + } + } + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN) + { + char *pszDsymName = (char *)alloca(strlen(pSplitFn->apszComps[pSplitFn->cComps - 1]) + sizeof(".dSYM")); + strcat(strcpy(pszDsymName, pSplitFn->apszComps[pSplitFn->cComps - 1]), ".dSYM"); + + /* + * Try the current directory (will take cover relative paths + * skipped above). + */ + rc2 = RTPathGetCurrent(szPath, sizeof(szPath)); + if (RT_FAILURE(rc2)) + strcpy(szPath, "."); + RTPathChangeToUnixSlashes(szPath, false); + + rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, szPath, pSplitFn, pszDsymName, fFlags, pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN + && pThis) + { + rc2 = RTCritSectRwEnterShared(&pThis->CritSect); + if (RT_SUCCESS(rc2)) + { + /* + * Run the applicable lists. + */ + rc2 = rtDbgCfgTryOpenDsumBundleInList(pThis, &pThis->PathList, pSplitFn, pszDsymName, + pszCacheSubDir, fFlags, szPath, + pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + + RTCritSectRwLeaveShared(&pThis->CritSect); + } + else if (RT_SUCCESS(rcRet)) + rcRet = rc2; + } + } + + RTPathSplitFree(pSplitFn); + if ( rc2 == VINF_CALLBACK_RETURN + || rc2 == VERR_CALLBACK_RETURN) + rcRet = rc2; + else if (RT_SUCCESS(rcRet)) + rcRet = VERR_NOT_FOUND; + return rcRet; + + +} + + + +RTDECL(int) RTDbgCfgSetLogCallback(RTDBGCFG hDbgCfg, PFNRTDBGCFGLOG pfnCallback, void *pvUser) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + AssertPtrNullReturn(pfnCallback, VERR_INVALID_POINTER); + + int rc = RTCritSectRwEnterExcl(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + if ( pThis->pfnLogCallback == NULL + || pfnCallback == NULL + || pfnCallback == pThis->pfnLogCallback) + { + pThis->pfnLogCallback = NULL; + pThis->pvLogUser = NULL; + ASMCompilerBarrier(); /* paranoia */ + pThis->pvLogUser = pvUser; + pThis->pfnLogCallback = pfnCallback; + rc = VINF_SUCCESS; + } + else + rc = VERR_ACCESS_DENIED; + RTCritSectRwLeaveExcl(&pThis->CritSect); + } + + return rc; +} + + +/** + * Frees a string list. + * + * @param pList The list to free. + */ +static void rtDbgCfgFreeStrList(PRTLISTANCHOR pList) +{ + PRTDBGCFGSTR pCur; + PRTDBGCFGSTR pNext; + RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry) + { + RTListNodeRemove(&pCur->ListEntry); + RTMemFree(pCur); + } +} + + +/** + * Make changes to a string list, given a semicolon separated input string. + * + * @returns VINF_SUCCESS, VERR_FILENAME_TOO_LONG, VERR_NO_MEMORY + * @param pThis The config instance. + * @param enmOp The change operation. + * @param pszValue The input strings separated by semicolon. + * @param fPaths Indicates that this is a path list and that we + * should look for srv and cache prefixes. + * @param pList The string list anchor. + */ +static int rtDbgCfgChangeStringList(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, bool fPaths, + PRTLISTANCHOR pList) +{ + if (enmOp == RTDBGCFGOP_SET) + rtDbgCfgFreeStrList(pList); + + while (*pszValue) + { + /* Skip separators. */ + while (*pszValue == ';') + pszValue++; + if (!*pszValue) + break; + + /* Find the end of this path. */ + const char *pchPath = pszValue++; + char ch; + while ((ch = *pszValue) && ch != ';') + pszValue++; + size_t cchPath = pszValue - pchPath; + if (cchPath >= UINT16_MAX) + return VERR_FILENAME_TOO_LONG; + + if (enmOp == RTDBGCFGOP_REMOVE) + { + /* + * Remove all occurences. + */ + PRTDBGCFGSTR pCur; + PRTDBGCFGSTR pNext; + RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry) + { + if ( pCur->cch == cchPath + && !memcmp(pCur->sz, pchPath, cchPath)) + { + RTListNodeRemove(&pCur->ListEntry); + RTMemFree(pCur); + } + } + } + else + { + /* + * We're adding a new one. + */ + PRTDBGCFGSTR pNew = (PRTDBGCFGSTR)RTMemAlloc(RT_OFFSETOF(RTDBGCFGSTR, sz[cchPath + 1])); + if (!pNew) + return VERR_NO_MEMORY; + pNew->cch = (uint16_t)cchPath; + pNew->fFlags = 0; + memcpy(pNew->sz, pchPath, cchPath); + pNew->sz[cchPath] = '\0'; + + if (enmOp == RTDBGCFGOP_PREPEND) + RTListPrepend(pList, &pNew->ListEntry); + else + RTListAppend(pList, &pNew->ListEntry); + } + } + + return VINF_SUCCESS; +} + + +/** + * Make changes to a 64-bit value + * + * @returns VINF_SUCCESS, VERR_DBG_CFG_INVALID_VALUE. + * @param pThis The config instance. + * @param enmOp The change operation. + * @param pszValue The input value. + * @param pszMnemonics The mnemonics map for this value. + * @param puValue The value to change. + */ +static int rtDbgCfgChangeStringU64(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, + PCRTDBGCFGU64MNEMONIC paMnemonics, uint64_t *puValue) +{ + uint64_t uNew = enmOp == RTDBGCFGOP_SET ? 0 : *puValue; + + char ch; + while ((ch = *pszValue)) + { + /* skip whitespace and separators */ + while (RT_C_IS_SPACE(ch) || RT_C_IS_CNTRL(ch) || ch == ';' || ch == ':') + ch = *++pszValue; + if (!ch) + break; + + if (RT_C_IS_DIGIT(ch)) + { + uint64_t uTmp; + int rc = RTStrToUInt64Ex(pszValue, (char **)&pszValue, 0, &uTmp); + if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG) + return VERR_DBG_CFG_INVALID_VALUE; + + if (enmOp != RTDBGCFGOP_REMOVE) + uNew |= uTmp; + else + uNew &= ~uTmp; + } + else + { + /* A mnemonic, find the end of it. */ + const char *pszMnemonic = pszValue - 1; + do + ch = *++pszValue; + while (ch && !RT_C_IS_SPACE(ch) && !RT_C_IS_CNTRL(ch) && ch != ';' && ch != ':'); + size_t cchMnemonic = pszValue - pszMnemonic; + + /* Look it up in the map and apply it. */ + unsigned i = 0; + while (paMnemonics[i].pszMnemonic) + { + if ( cchMnemonic == paMnemonics[i].cchMnemonic + && !memcmp(pszMnemonic, paMnemonics[i].pszMnemonic, cchMnemonic)) + { + if (paMnemonics[i].fSet ? enmOp != RTDBGCFGOP_REMOVE : enmOp == RTDBGCFGOP_REMOVE) + uNew |= paMnemonics[i].fFlags; + else + uNew &= ~paMnemonics[i].fFlags; + break; + } + i++; + } + + if (!paMnemonics[i].pszMnemonic) + return VERR_DBG_CFG_INVALID_VALUE; + } + } + + *puValue = uNew; + return VINF_SUCCESS; +} + + +RTDECL(int) RTDbgCfgChangeString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, const char *pszValue) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER); + AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER); + if (!pszValue) + pszValue = ""; + else + AssertPtrReturn(pszValue, VERR_INVALID_POINTER); + + int rc = RTCritSectRwEnterExcl(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + switch (enmProp) + { + case RTDBGCFGPROP_FLAGS: + rc = rtDbgCfgChangeStringU64(pThis, enmOp, pszValue, g_aDbgCfgFlags, &pThis->fFlags); + break; + case RTDBGCFGPROP_PATH: + rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->PathList); + break; + case RTDBGCFGPROP_SUFFIXES: + rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, false, &pThis->SuffixList); + break; + case RTDBGCFGPROP_SRC_PATH: + rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->SrcPathList); + break; + default: + AssertFailed(); + rc = VERR_INTERNAL_ERROR_3; + } + + RTCritSectRwLeaveExcl(&pThis->CritSect); + } + + return rc; +} + + +RTDECL(int) RTDbgCfgChangeUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, uint64_t uValue) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER); + AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER); + + int rc = RTCritSectRwEnterExcl(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + uint64_t *puValue = NULL; + switch (enmProp) + { + case RTDBGCFGPROP_FLAGS: + puValue = &pThis->fFlags; + break; + default: + rc = VERR_DBG_CFG_NOT_UINT_PROP; + } + if (RT_SUCCESS(rc)) + { + switch (enmOp) + { + case RTDBGCFGOP_SET: + *puValue = uValue; + break; + case RTDBGCFGOP_APPEND: + case RTDBGCFGOP_PREPEND: + *puValue |= uValue; + break; + case RTDBGCFGOP_REMOVE: + *puValue &= ~uValue; + break; + default: + AssertFailed(); + rc = VERR_INTERNAL_ERROR_2; + } + } + + RTCritSectRwLeaveExcl(&pThis->CritSect); + } + + return rc; +} + + +/** + * Querys a string list as a single string (semicolon separators). + * + * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW. + * @param pThis The config instance. + * @param pList The string list anchor. + * @param pszValue The output buffer. + * @param cbValue The size of the output buffer. + */ +static int rtDbgCfgQueryStringList(RTDBGCFG hDbgCfg, PRTLISTANCHOR pList, + char *pszValue, size_t cbValue) +{ + /* + * Check the length first. + */ + size_t cbReq = 1; + PRTDBGCFGSTR pCur; + RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry) + cbReq += pCur->cch + 1; + if (cbReq > cbValue) + return VERR_BUFFER_OVERFLOW; + + /* + * Construct the string list in the buffer. + */ + char *psz = pszValue; + RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry) + { + if (psz != pszValue) + *psz++ = ';'; + memcpy(psz, pCur->sz, pCur->cch); + psz += pCur->cch; + } + *psz = '\0'; + + return VINF_SUCCESS; +} + + +/** + * Querys the string value of a 64-bit unsigned int. + * + * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW. + * @param pThis The config instance. + * @param uValue The value to query. + * @param pszMnemonics The mnemonics map for this value. + * @param pszValue The output buffer. + * @param cbValue The size of the output buffer. + */ +static int rtDbgCfgQueryStringU64(RTDBGCFG hDbgCfg, uint64_t uValue, PCRTDBGCFGU64MNEMONIC paMnemonics, + char *pszValue, size_t cbValue) +{ + /* + * If no mnemonics, just return the hex value. + */ + if (!paMnemonics || paMnemonics[0].pszMnemonic) + { + char szTmp[64]; + size_t cch = RTStrPrintf(szTmp, sizeof(szTmp), "%#x", uValue); + if (cch + 1 > cbValue) + return VERR_BUFFER_OVERFLOW; + memcpy(pszValue, szTmp, cbValue); + return VINF_SUCCESS; + } + + /* + * Check that there is sufficient buffer space first. + */ + size_t cbReq = 1; + for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++) + if ( paMnemonics[i].fSet + ? (paMnemonics[i].fFlags & uValue) + : !(paMnemonics[i].fFlags & uValue)) + cbReq += (cbReq != 1) + paMnemonics[i].cchMnemonic; + if (cbReq > cbValue) + return VERR_BUFFER_OVERFLOW; + + /* + * Construct the string. + */ + char *psz = pszValue; + for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++) + if ( paMnemonics[i].fSet + ? (paMnemonics[i].fFlags & uValue) + : !(paMnemonics[i].fFlags & uValue)) + { + if (psz != pszValue) + *psz++ = ' '; + memcpy(psz, paMnemonics[i].pszMnemonic, paMnemonics[i].cchMnemonic); + psz += paMnemonics[i].cchMnemonic; + } + *psz = '\0'; + return VINF_SUCCESS; +} + + +RTDECL(int) RTDbgCfgQueryString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, char *pszValue, size_t cbValue) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszValue, VERR_INVALID_POINTER); + + int rc = RTCritSectRwEnterShared(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + switch (enmProp) + { + case RTDBGCFGPROP_FLAGS: + rc = rtDbgCfgQueryStringU64(pThis, pThis->fFlags, g_aDbgCfgFlags, pszValue, cbValue); + break; + case RTDBGCFGPROP_PATH: + rc = rtDbgCfgQueryStringList(pThis, &pThis->PathList, pszValue, cbValue); + break; + case RTDBGCFGPROP_SUFFIXES: + rc = rtDbgCfgQueryStringList(pThis, &pThis->SuffixList, pszValue, cbValue); + break; + case RTDBGCFGPROP_SRC_PATH: + rc = rtDbgCfgQueryStringList(pThis, &pThis->SrcPathList, pszValue, cbValue); + break; + default: + AssertFailed(); + rc = VERR_INTERNAL_ERROR_3; + } + + RTCritSectRwLeaveShared(&pThis->CritSect); + } + + return rc; +} + + +RTDECL(int) RTDbgCfgQueryUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, uint64_t *puValue) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER); + AssertPtrReturn(puValue, VERR_INVALID_POINTER); + + int rc = RTCritSectRwEnterShared(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + switch (enmProp) + { + case RTDBGCFGPROP_FLAGS: + *puValue = pThis->fFlags; + break; + default: + rc = VERR_DBG_CFG_NOT_UINT_PROP; + } + + RTCritSectRwLeaveShared(&pThis->CritSect); + } + + return rc; +} + +RTDECL(uint32_t) RTDbgCfgRetain(RTDBGCFG hDbgCfg) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX); + + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs < UINT32_MAX / 2); + return cRefs; +} + + +RTDECL(uint32_t) RTDbgCfgRelease(RTDBGCFG hDbgCfg) +{ + if (hDbgCfg == NIL_RTDBGCFG) + return 0; + + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + if (!cRefs) + { + /* + * Last reference - free all memory. + */ + ASMAtomicWriteU32(&pThis->u32Magic, ~RTDBGCFG_MAGIC); + rtDbgCfgFreeStrList(&pThis->PathList); + rtDbgCfgFreeStrList(&pThis->SuffixList); + rtDbgCfgFreeStrList(&pThis->SrcPathList); +#ifdef RT_OS_WINDOWS + rtDbgCfgFreeStrList(&pThis->NtSymbolPathList); + rtDbgCfgFreeStrList(&pThis->NtExecutablePathList); + rtDbgCfgFreeStrList(&pThis->NtSourcePath); +#endif + RTCritSectRwDelete(&pThis->CritSect); + RTMemFree(pThis); + } + else + Assert(cRefs < UINT32_MAX / 2); + return cRefs; +} + + +RTDECL(int) RTDbgCfgCreate(PRTDBGCFG phDbgCfg, const char *pszEnvVarPrefix, bool fNativePaths) +{ + /* + * Validate input. + */ + AssertPtrReturn(phDbgCfg, VERR_INVALID_POINTER); + if (pszEnvVarPrefix) + { + AssertPtrReturn(pszEnvVarPrefix, VERR_INVALID_POINTER); + AssertReturn(*pszEnvVarPrefix, VERR_INVALID_PARAMETER); + } + + /* + * Allocate and initialize a new instance. + */ + PRTDBGCFGINT pThis = (PRTDBGCFGINT)RTMemAllocZ(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + pThis->u32Magic = RTDBGCFG_MAGIC; + pThis->cRefs = 1; + RTListInit(&pThis->PathList); + RTListInit(&pThis->SuffixList); + RTListInit(&pThis->SrcPathList); +#ifdef RT_OS_WINDOWS + RTListInit(&pThis->NtSymbolPathList); + RTListInit(&pThis->NtExecutablePathList); + RTListInit(&pThis->NtSourcePath); +#endif + + int rc = RTCritSectRwInit(&pThis->CritSect); + if (RT_FAILURE(rc)) + { + RTMemFree(pThis); + return rc; + } + + /* + * Read configurtion from the environment if requested to do so. + */ + if (pszEnvVarPrefix || fNativePaths) + { + const size_t cbEnvVar = 256; + const size_t cbEnvVal = 65536 - cbEnvVar; + char *pszEnvVar = (char *)RTMemTmpAlloc(cbEnvVar + cbEnvVal); + if (pszEnvVar) + { + char *pszEnvVal = pszEnvVar + cbEnvVar; + + if (pszEnvVarPrefix) + { + static struct + { + RTDBGCFGPROP enmProp; + const char *pszVar; + } const s_aProps[] = + { + { RTDBGCFGPROP_FLAGS, "FLAGS" }, + { RTDBGCFGPROP_PATH, "PATH" }, + { RTDBGCFGPROP_SUFFIXES, "SUFFIXES" }, + { RTDBGCFGPROP_SRC_PATH, "SRC_PATH" }, + }; + + for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++) + { + size_t cchEnvVar = RTStrPrintf(pszEnvVar, cbEnvVar, "%s_%s", pszEnvVarPrefix, s_aProps[i].pszVar); + if (cchEnvVar >= cbEnvVar - 1) + { + rc = VERR_BUFFER_OVERFLOW; + break; + } + + rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, pszEnvVal, cbEnvVal, NULL); + if (RT_SUCCESS(rc)) + { + rc = RTDbgCfgChangeString(pThis, s_aProps[i].enmProp, RTDBGCFGOP_SET, pszEnvVal); + if (RT_FAILURE(rc)) + break; + } + else if (rc != VERR_ENV_VAR_NOT_FOUND) + break; + else + rc = VINF_SUCCESS; + } + } + + /* + * Pick up system specific search paths. + */ + if (RT_SUCCESS(rc) && fNativePaths) + { + struct + { + PRTLISTANCHOR pList; + const char *pszVar; + char chSep; + } aNativePaths[] = + { +#ifdef RT_OS_WINDOWS + { &pThis->NtExecutablePathList, "_NT_EXECUTABLE_PATH", ';' }, + { &pThis->NtSymbolPathList, "_NT_ALT_SYMBOL_PATH", ';' }, + { &pThis->NtSymbolPathList, "_NT_SYMBOL_PATH", ';' }, + { &pThis->NtSourcePath, "_NT_SOURCE_PATH", ';' }, +#endif + { NULL, NULL, 0 } + }; + for (unsigned i = 0; aNativePaths[i].pList; i++) + { + Assert(aNativePaths[i].chSep == ';'); /* fix when needed */ + rc = RTEnvGetEx(RTENV_DEFAULT, aNativePaths[i].pszVar, pszEnvVal, cbEnvVal, NULL); + if (RT_SUCCESS(rc)) + { + rc = rtDbgCfgChangeStringList(pThis, RTDBGCFGOP_APPEND, pszEnvVal, true, aNativePaths[i].pList); + if (RT_FAILURE(rc)) + break; + } + else if (rc != VERR_ENV_VAR_NOT_FOUND) + break; + else + rc = VINF_SUCCESS; + } + } + RTMemTmpFree(pszEnvVar); + } + else + rc = VERR_NO_TMP_MEMORY; + if (RT_FAILURE(rc)) + { + /* + * Error, bail out. + */ + RTDbgCfgRelease(pThis); + return rc; + } + } + + /* + * Returns successfully. + */ + *phDbgCfg = pThis; + + return VINF_SUCCESS; +} + |