VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/dbg/dbgcfg.cpp@ 46025

Last change on this file since 46025 was 46025, checked in by vboxsync, 12 years ago

RTDbgCfg: Directory and cache searching code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.4 KB
Line 
1/* $Id: dbgcfg.cpp 46025 2013-05-13 15:36:27Z vboxsync $ */
2/** @file
3 * IPRT - Debugging Configuration.
4 */
5
6/*
7 * Copyright (C) 2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#define LOG_GROUP RTLOGGROUP_DBG
32#include <iprt/dbg.h>
33#include "internal/iprt.h"
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/critsect.h>
38#include <iprt/ctype.h>
39#include <iprt/dir.h>
40#include <iprt/err.h>
41#include <iprt/env.h>
42#include <iprt/file.h>
43#include <iprt/list.h>
44#include <iprt/log.h>
45#include <iprt/mem.h>
46#include <iprt/path.h>
47#include <iprt/semaphore.h>
48#include <iprt/string.h>
49#include <iprt/uuid.h>
50#include "internal/magics.h"
51
52
53/*******************************************************************************
54* Structures and Typedefs *
55*******************************************************************************/
56/**
57 * String list entry.
58 */
59typedef struct RTDBGCFGSTR
60{
61 /** List entry. */
62 RTLISTNODE ListEntry;
63 /** Domain specific flags. */
64 uint16_t fFlags;
65 /** The length of the string. */
66 uint16_t cch;
67 /** The string. */
68 char sz[1];
69} RTDBGCFGSTR;
70/** Pointer to a string list entry. */
71typedef RTDBGCFGSTR *PRTDBGCFGSTR;
72
73
74/**
75 * Log callback.
76 *
77 * @param hDbgCfg The debug config instance.
78 * @param iLevel The message level.
79 * @param pszMsg The message.
80 * @param pvUser User argument.
81 */
82typedef DECLCALLBACK(int) FNRTDBGCFGLOG(RTDBGCFG hDbgCfg, uint32_t iLevel, const char *pszMsg, void *pvUser);
83/** Pointer to a log callback. */
84typedef FNRTDBGCFGLOG *PFNRTDBGCFGLOG;
85
86/**
87 * Configuration instance.
88 */
89typedef struct RTDBGCFGINT
90{
91 /** The magic value (RTDBGCFG_MAGIC). */
92 uint32_t u32Magic;
93 /** Reference counter. */
94 uint32_t volatile cRefs;
95 /** Flags, see RTDBGCFG_FLAGS_XXX. */
96 uint64_t fFlags;
97
98 /** List of paths to search for debug files and executable images. */
99 RTLISTANCHOR PathList;
100 /** List of debug file suffixes. */
101 RTLISTANCHOR SuffixList;
102 /** List of paths to search for source files. */
103 RTLISTANCHOR SrcPathList;
104
105#ifdef RT_OS_WINDOWS
106 /** The _NT_ALT_SYMBOL_PATH and _NT_SYMBOL_PATH combined. */
107 RTLISTANCHOR NtSymbolPathList;
108 /** The _NT_EXECUTABLE_PATH. */
109 RTLISTANCHOR NtExecutablePathList;
110 /** The _NT_SOURCE_PATH. */
111 RTLISTANCHOR NtSourcePath;
112#endif
113
114 /** Log callback function. */
115 PFNRTDBGCFGLOG pfnLogCallback;
116 /** User argument to pass to the log callback. */
117 void *pvLogUser;
118
119 /** Critical section protecting the instance data. */
120 RTCRITSECTRW CritSect;
121} *PRTDBGCFGINT;
122
123/**
124 * Mnemonics map entry for a 64-bit unsigned property value.
125 */
126typedef struct RTDBGCFGU64MNEMONIC
127{
128 /** The flags to set or clear. */
129 uint64_t fFlags;
130 /** The mnemonic. */
131 const char *pszMnemonic;
132 /** The length of the mnemonic. */
133 uint8_t cchMnemonic;
134 /** If @c true, the bits in fFlags will be set, if @c false they will be
135 * cleared. */
136 bool fSet;
137} RTDBGCFGU64MNEMONIC;
138/** Pointer to a read only mnemonic map entry for a uint64_t property. */
139typedef RTDBGCFGU64MNEMONIC const *PCRTDBGCFGU64MNEMONIC;
140
141
142/** @name Open flags.
143 * @{ */
144/** The operative system mask. The values are RT_OPSYS_XXX. */
145#define RTDBGCFG_O_OPSYS_MASK UINT32_C(0x000000ff)
146/** Whether to make a recursive search. */
147#define RTDBGCFG_O_RECURSIVE RT_BIT_32(27)
148/** We're looking for a separate debug file. */
149#define RTDBGCFG_O_EXT_DEBUG_FILE RT_BIT_32(28)
150/** We're looking for an executable image. */
151#define RTDBGCFG_O_EXECUTABLE_IMAGE RT_BIT_32(29)
152/** The file search should be done in an case insensitive fashion. */
153#define RTDBGCFG_O_CASE_INSENSITIVE RT_BIT_32(30)
154/** Use Windbg style symbol servers when encountered in the path. */
155#define RTDBGCFG_O_SYMSRV RT_BIT_32(31)
156/** @} */
157
158
159/*******************************************************************************
160* Defined Constants And Macros *
161*******************************************************************************/
162/** Validates a debug module handle and returns rc if not valid. */
163#define RTDBGCFG_VALID_RETURN_RC(pThis, rc) \
164 do { \
165 AssertPtrReturn((pThis), (rc)); \
166 AssertReturn((pThis)->u32Magic == RTDBGCFG_MAGIC, (rc)); \
167 AssertReturn((pThis)->cRefs > 0, (rc)); \
168 } while (0)
169
170
171/*******************************************************************************
172* Global Variables *
173*******************************************************************************/
174/** Mnemonics map for RTDBGCFGPROP_FLAGS. */
175static const RTDBGCFGU64MNEMONIC g_aDbgCfgFlags[] =
176{
177 { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("deferred"), true },
178 { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("nodeferred"), false },
179 { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("symsrv"), false },
180 { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("nosymsrv"), true },
181 { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("syspaths"), false },
182 { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("nosyspaths"), true },
183 { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("rec"), false },
184 { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("norec"), true },
185 { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("recsrc"), false },
186 { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("norecsrc"), true },
187 { 0, NULL, 0, false }
188};
189
190
191
192/**
193 * Runtime logging, level 1.
194 *
195 * @param pThis The debug config instance data.
196 * @param pszFormat The message format string.
197 * @param ... Arguments references in the format string.
198 */
199static void rtDbgCfgLog1(PRTDBGCFGINT pThis, const char *pszFormat, ...)
200{
201 if (LogIsEnabled() || (pThis && pThis->pfnLogCallback))
202 {
203 va_list va;
204 va_start(va, pszFormat);
205 char *pszMsg = RTStrAPrintf2V(pszFormat, va);
206 va_end(va);
207
208 Log(("RTDbgCfg: %s", pszMsg));
209 if (pThis && pThis->pfnLogCallback)
210 pThis->pfnLogCallback(pThis, 1, pszMsg, pThis->pvLogUser);
211 RTStrFree(pszMsg);
212 }
213}
214
215
216/**
217 * Runtime logging, level 2.
218 *
219 * @param pThis The debug config instance data.
220 * @param pszFormat The message format string.
221 * @param ... Arguments references in the format string.
222 */
223static void rtDbgCfgLog2(PRTDBGCFGINT pThis, const char *pszFormat, ...)
224{
225 if (LogIs2Enabled() || (pThis && pThis->pfnLogCallback))
226 {
227 va_list va;
228 va_start(va, pszFormat);
229 char *pszMsg = RTStrAPrintf2V(pszFormat, va);
230 va_end(va);
231
232 Log(("RTDbgCfg: %s", pszMsg));
233 if (pThis && pThis->pfnLogCallback)
234 pThis->pfnLogCallback(pThis, 2, pszMsg, pThis->pvLogUser);
235 RTStrFree(pszMsg);
236 }
237}
238
239
240/**
241 * Checks if the file system at the given path is case insensitive or not.
242 *
243 * @returns true / false
244 * @param pszPath The path to query about.
245 */
246static int rtDbgCfgIsFsCaseInsensitive(const char *pszPath)
247{
248 RTFSPROPERTIES Props;
249 int rc = RTFsQueryProperties(pszPath, &Props);
250 if (RT_FAILURE(rc))
251 return RT_OPSYS == RT_OPSYS_DARWIN
252 || RT_OPSYS == RT_OPSYS_DOS
253 || RT_OPSYS == RT_OPSYS_OS2
254 || RT_OPSYS == RT_OPSYS_NT
255 || RT_OPSYS == RT_OPSYS_WINDOWS;
256 return !Props.fCaseSensitive;
257}
258
259
260/**
261 * Worker that does case sensitive file/dir searching.
262 *
263 * @returns true / false.
264 * @param pszPath The path buffer containing an existing directory.
265 * RTPATH_MAX in size. On success, this will contain
266 * the combined path with @a pszName case correct.
267 * @param offLastComp The offset of the last component (for chopping it
268 * off).
269 * @param pszName What we're looking for.
270 * @param enmType What kind of thing we're looking for.
271 */
272static bool rtDbgCfgIsXxxxAndFixCaseWorker(char *pszPath, size_t offLastComp, const char *pszName,
273 RTDIRENTRYTYPE enmType)
274{
275 /** @todo IPRT should generalize this so we can use host specific tricks to
276 * speed it up. */
277
278 /* Return straight away if the name isn't case foldable. */
279 if (!RTStrIsCaseFoldable(pszName))
280 return false;
281
282 /*
283 * Open the directory and check each entry in it.
284 */
285 pszPath[offLastComp] = '\0';
286 PRTDIR pDir;
287 int rc = RTDirOpen(&pDir, pszPath);
288 if (RT_SUCCESS(rc))
289 return false;
290
291 for (;;)
292 {
293 /* Read the next entry. */
294 union
295 {
296 RTDIRENTRY Entry;
297 uint8_t ab[_4K];
298 } u;
299 size_t cbBuf = sizeof(u);
300 rc = RTDirRead(pDir, &u.Entry, &cbBuf);
301 if (RT_FAILURE(rc))
302 break;
303
304 if ( !RTStrICmp(pszName, u.Entry.szName)
305 && ( u.Entry.enmType == enmType
306 || u.Entry.enmType == RTDIRENTRYTYPE_UNKNOWN
307 || u.Entry.enmType == RTDIRENTRYTYPE_SYMLINK) )
308 {
309 pszPath[offLastComp] = '\0';
310 rc = RTPathAppend(pszPath, RTPATH_MAX, u.Entry.szName);
311 if ( u.Entry.enmType != enmType
312 && RT_SUCCESS(rc))
313 RTDirQueryUnknownType(pszPath, true /*fFollowSymlinks*/, &u.Entry.enmType);
314
315 if ( u.Entry.enmType == enmType
316 || RT_FAILURE(rc))
317 {
318 RTDirClose(pDir);
319 if (RT_FAILURE(rc))
320 {
321 pszPath[offLastComp] = '\0';
322 return false;
323 }
324 return true;
325 }
326 }
327 }
328
329 RTDirClose(pDir);
330 pszPath[offLastComp] = '\0';
331
332 return false;
333}
334
335
336/**
337 * Appends @a pszSubDir to @a pszPath and check whether it exists and is a
338 * directory.
339 *
340 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
341 * matching sub directory.
342 *
343 * @returns true / false
344 * @param pszPath The path buffer containing an existing
345 * directory. RTPATH_MAX in size.
346 * @param pszSubDir The sub directory to append.
347 * @param fCaseInsensitive Whether case insensitive searching is required.
348 */
349static bool rtDbgCfgIsDirAndFixCase(char *pszPath, const char *pszSubDir, bool fCaseInsensitive)
350{
351 /* Save the length of the input path so we can restore it in the case
352 insensitive branch further down. */
353 size_t const cchPath = strlen(pszPath);
354
355 /*
356 * Append the sub directory and check if we got a hit.
357 */
358 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
359 if (RT_FAILURE(rc))
360 return false;
361
362 if (RTDirExists(pszPath))
363 return true;
364
365 /*
366 * Do case insensitive lookup if requested.
367 */
368 if (fCaseInsensitive)
369 return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, pszSubDir, RTDIRENTRYTYPE_DIRECTORY);
370 return false;
371}
372
373
374/**
375 * Appends @a pszFilename to @a pszPath and check whether it exists and is a
376 * directory.
377 *
378 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
379 * matching filename.
380 *
381 * @returns true / false
382 * @param pszPath The path buffer containing an existing
383 * directory. RTPATH_MAX in size.
384 * @param pszFilename The file name to append.
385 * @param fCaseInsensitive Whether case insensitive searching is required.
386 */
387static bool rtDbgCfgIsFileAndFixCase(char *pszPath, const char *pszFilename, bool fCaseInsensitive)
388{
389 /* Save the length of the input path so we can restore it in the case
390 insensitive branch further down. */
391 size_t cchPath = strlen(pszPath);
392
393 /*
394 * Append the filename and check if we got a hit.
395 */
396 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
397 if (RT_FAILURE(rc))
398 return false;
399
400 if (RTFileExists(pszPath))
401 return true;
402
403 /*
404 * Do case insensitive lookup if requested.
405 */
406 if (fCaseInsensitive)
407 return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, pszFilename, RTDIRENTRYTYPE_FILE);
408 return false;
409}
410
411
412static int rtDbgCfgTryOpenDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn, uint32_t fFlags,
413 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
414{
415 int rcRet = VWRN_NOT_FOUND;
416 int rc2;
417
418 /* If the directory doesn't exist, just quit immediately.
419 Note! Our case insensitivity doesn't extend to the search dirs themselfs,
420 only to the bits under neath them. */
421 if (!RTDirExists(pszPath))
422 {
423 rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
424 return rcRet;
425 }
426
427 /* Figure out whether we have to do a case sensitive search or not.
428 Note! As a simplification, we don't ask for case settings in each
429 directory under the user specified path, we assume the file
430 systems that mounted there have compatible settings. Faster
431 that way. */
432 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
433 && rtDbgCfgIsFsCaseInsensitive(pszPath);
434
435 size_t const cchPath = strlen(pszPath);
436
437 /*
438 * Look for the file with less and less of the original path given.
439 */
440 for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
441 {
442 pszPath[cchPath] = '\0';
443
444 rc2 = VINF_SUCCESS;
445 for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
446 if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
447 rc2 = VERR_FILE_NOT_FOUND;
448
449 if (RT_SUCCESS(rc2))
450 {
451 if (rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], fCaseInsensitive))
452 {
453 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
454 rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
455 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
456 {
457 if (rc2 == VINF_CALLBACK_RETURN)
458 rtDbgCfgLog1(pThis, "Found '%s'.", pszPath);
459 else
460 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
461 return rc2;
462 }
463 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
464 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
465 rcRet = rc2;
466 }
467 }
468 }
469
470 /*
471 * Do a recursive search if requested.
472 */
473 if ( (fFlags & RTDBGCFG_O_RECURSIVE)
474 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
475 {
476 /** @todo Recursive searching will be done later. */
477 }
478
479 return rcRet;
480}
481
482
483static int rtDbgCfgTryDownloadAndOpen(PRTDBGCFGINT pThis, const char *pszServer,
484 char *pszPath, const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn,
485 uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
486{
487 if (pThis->fFlags & RTDBGCFG_FLAGS_NO_SYM_SRV)
488 return VWRN_NOT_FOUND;
489
490
491 return VERR_NOT_IMPLEMENTED;
492}
493
494
495static int rtDbgCfgCopyFileToCache(PRTDBGCFGINT pThis, char const *pszSrc, const char *pchCache, size_t cchCache,
496 const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn)
497{
498 /** @todo copy to cache */
499 return VINF_SUCCESS;
500}
501
502
503static int rtDbgCfgTryOpenCache(PRTDBGCFGINT pThis, char *pszPath, const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn,
504 uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
505{
506 /*
507 * If the cache doesn't exist, fail right away.
508 */
509 if (!pszCacheSubDir || !*pszCacheSubDir)
510 return VWRN_NOT_FOUND;
511 if (!RTDirExists(pszPath))
512 {
513 rtDbgCfgLog2(pThis, "Cache does not exist: '%s'\n", pszPath);
514 return VWRN_NOT_FOUND;
515 }
516
517 size_t cchPath = strlen(pszPath);
518
519 /*
520 * Carefully construct the cache path with case insensitivity in mind.
521 */
522 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
523 && rtDbgCfgIsFsCaseInsensitive(pszPath);
524
525 if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], fCaseInsensitive))
526 return VWRN_NOT_FOUND;
527
528 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszCacheSubDir, fCaseInsensitive))
529 return VWRN_NOT_FOUND;
530
531 if (!rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], fCaseInsensitive))
532 return VWRN_NOT_FOUND;
533
534 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
535 int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
536 if (rc2 == VINF_CALLBACK_RETURN)
537 rtDbgCfgLog1(pThis, "Found '%s'.", pszPath);
538 else if (rc2 == VERR_CALLBACK_RETURN)
539 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
540 else
541 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
542 return rc2;
543}
544
545
546static int rtDbgCfgTryOpenList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, const char *pszCacheSubDir,
547 uint32_t fFlags, char *pszPath, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
548{
549 int rcRet = VWRN_NOT_FOUND;
550 int rc2;
551
552 const char *pchCache = NULL;
553 size_t cchCache = 0;
554
555 PRTDBGCFGSTR pCur;
556 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
557 {
558 size_t cchDir = pCur->cch;
559 const char *pszDir = pCur->sz;
560 rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
561
562 /* This is very simplistic, but we have a unreasonably large path
563 buffer, so it'll work just fine and simplify things greatly below. */
564 if (cchDir >= RTPATH_MAX - 8U)
565 {
566 if (RT_SUCCESS_NP(rcRet))
567 rcRet = VERR_FILENAME_TOO_LONG;
568 continue;
569 }
570
571 /*
572 * Process the path according to it's type.
573 */
574 if (!strncmp(pszDir, RT_STR_TUPLE("srv*")))
575 {
576 /*
577 * Symbol server.
578 */
579 pszDir += sizeof("srv*") - 1;
580 cchDir -= sizeof("srv*") - 1;
581 bool fSearchCache = false;
582 const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
583 if (!pszServer)
584 pszServer = pszDir;
585 else if (pszServer == pszDir)
586 continue;
587 {
588 fSearchCache = true;
589 pchCache = pszDir;
590 cchCache = pszServer - pszDir;
591 pszServer++;
592 }
593
594 /* We don't have any default cache directory, so skip if the cache is missing. */
595 if (cchCache == 0)
596 continue;
597
598 /* Search the cache first (if we haven't already done so). */
599 if (fSearchCache)
600 {
601 memcpy(pszPath, pchCache, cchCache);
602 pszPath[cchCache] = '\0';
603 rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, pszCacheSubDir, pSplitFn, fFlags,
604 pfnCallback, pvUser1, pvUser2);
605 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
606 return rc2;
607 }
608
609 /* Try downloading the file. */
610 memcpy(pszPath, pchCache, cchCache);
611 pszPath[cchCache] = '\0';
612 rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pSplitFn, fFlags,
613 pfnCallback, pvUser1, pvUser2);
614 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
615 return rc2;
616 }
617 else if (!strncmp(pszDir, RT_STR_TUPLE("cache*")))
618 {
619 /*
620 * Cache directory.
621 */
622 pszDir += sizeof("cache*") - 1;
623 cchDir -= sizeof("cache*") - 1;
624 if (!cchDir)
625 continue;
626 pchCache = pszDir;
627 cchCache = cchDir;
628
629 memcpy(pszPath, pchCache, cchCache);
630 pszPath[cchCache] = '\0';
631 rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, pszCacheSubDir, pSplitFn, fFlags,
632 pfnCallback, pvUser1, pvUser2);
633 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
634 return rc2;
635 }
636 else
637 {
638 /*
639 * Normal directory. Check for our own 'rec*' and 'norec*' prefix
640 * flags governing recursive searching.
641 */
642 uint32_t fFlagsDir = fFlags;
643 if (!strncmp(pszDir, RT_STR_TUPLE("rec*")))
644 {
645 pszDir += sizeof("rec*") - 1;
646 cchDir -= sizeof("rec*") - 1;
647 fFlagsDir |= RTDBGCFG_O_RECURSIVE;
648 }
649 else if (!strncmp(pszDir, RT_STR_TUPLE("norec*")))
650 {
651 pszDir += sizeof("norec*") - 1;
652 cchDir -= sizeof("norec*") - 1;
653 fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
654 }
655
656 /* Copy the path into the buffer and do the searching. */
657 memcpy(pszPath, pszDir, cchDir);
658 pszPath[cchDir] = '\0';
659
660 rc2 = rtDbgCfgTryOpenDir(pThis, pszPath, pSplitFn, fFlagsDir, pfnCallback, pvUser1, pvUser2);
661 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
662 {
663 if ( rc2 == VINF_CALLBACK_RETURN
664 && cchCache > 0)
665 rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache, pszCacheSubDir, pSplitFn);
666 return rc2;
667 }
668 }
669 }
670
671 return rcRet;
672}
673
674
675/**
676 * Common worker routine for Image and debug info opening.
677 *
678 * This will not search using for suffixes.
679 *
680 * @returns IPRT status code.
681 * @param hDbgCfg The debugging configuration handle. NIL_RTDBGCFG is
682 * accepted, but the result is that no paths will be
683 * searched beyond the given and the current directory.
684 * @param pszFilename The filename to search for. This may or may not
685 * include a full or partial path.
686 * @param pszCacheSubDir The cache subdirectory to look in.
687 * @param fFlags Flags and hints.
688 * @param pfnCallback The open callback routine.
689 * @param pvUser1 User parameter 1.
690 * @param pvUser2 User parameter 2.
691 */
692static int rtDbgCfgOpenWithSubDir(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir,
693 uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
694{
695 int rcRet = VINF_SUCCESS;
696 int rc2;
697
698 /*
699 * Do a little validating first.
700 */
701 PRTDBGCFGINT pThis = hDbgCfg;
702 if (pThis != NIL_RTDBGCFG)
703 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
704 else
705 pThis = NULL;
706 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
707 AssertPtrReturn(pszCacheSubDir, VERR_INVALID_POINTER);
708 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
709
710 /*
711 * Do some guessing as to the way we should parse the filename and whether
712 * it's case exact or not.
713 */
714 bool fDosPath = strchr(pszFilename, ':') != NULL
715 || strchr(pszFilename, '\\') != NULL
716 || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
717 || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE);
718 if (fDosPath)
719 fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
720
721 rtDbgCfgLog2(pThis, "Looking for '%s' w/ cache subdir '%s' and %#x flags\n", pszFilename, pszCacheSubDir, fFlags);
722
723 PRTPATHSPLIT pSplitFn;
724 rc2 = RTPathSplitA(pszFilename, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
725 if (RT_FAILURE(rc2))
726 return rc2;
727
728 /*
729 * Try the stored file name first if it has a kind of absolute path.
730 */
731 char szPath[RTPATH_MAX];
732 if (RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps))
733 {
734 rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
735 if (RT_SUCCESS(rc2) && RTFileExists(szPath))
736 rc2 = pfnCallback(pThis, pszFilename, pvUser1, pvUser2);
737 }
738 if ( rc2 != VINF_CALLBACK_RETURN
739 && rc2 != VERR_CALLBACK_RETURN)
740 {
741 /*
742 * Try the current directory (will take cover relative paths
743 * skipped above).
744 */
745 rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
746 if (RT_FAILURE(rc2))
747 strcpy(szPath, ".");
748 rc2 = rtDbgCfgTryOpenDir(pThis, szPath, pSplitFn, fFlags, pfnCallback, pvUser1, pvUser2);
749 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
750 rcRet = rc2;
751
752 if ( rc2 != VINF_CALLBACK_RETURN
753 && rc2 != VERR_CALLBACK_RETURN
754 && pThis)
755 {
756 rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
757 if (RT_SUCCESS(rc2))
758 {
759 /*
760 * Run the applicable lists.
761 */
762 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->PathList, pSplitFn, pszCacheSubDir, fFlags, szPath,
763 pfnCallback, pvUser1, pvUser2);
764 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
765 rcRet = rc2;
766
767#ifdef RT_OS_WINDOWS
768 if ( rc2 != VINF_CALLBACK_RETURN
769 && rc2 != VERR_CALLBACK_RETURN
770 && (fFlags & RTDBGCFG_O_EXECUTABLE_IMAGE)
771 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
772 {
773 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtExecutablePathList, pSplitFn, pszCacheSubDir, fFlags, szPath,
774 pfnCallback, pvUser1, pvUser2);
775 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
776 rcRet = rc2;
777 }
778
779 if ( rc2 != VINF_CALLBACK_RETURN
780 && rc2 != VERR_CALLBACK_RETURN
781 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
782 {
783 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtSymbolPathList, pSplitFn, pszCacheSubDir, fFlags, szPath,
784 pfnCallback, pvUser1, pvUser2);
785 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
786 rcRet = rc2;
787 }
788#endif
789 RTCritSectRwLeaveShared(&pThis->CritSect);
790 }
791 else if (RT_SUCCESS(rcRet))
792 rcRet = rc2;
793 }
794 }
795
796 RTPathSplitFree(pSplitFn);
797 if ( rc2 == VINF_CALLBACK_RETURN
798 || rc2 == VERR_CALLBACK_RETURN)
799 rcRet = rc2;
800 else if (RT_SUCCESS(rcRet))
801 rcRet = VERR_NOT_FOUND;
802 return rcRet;
803}
804
805
806RTDECL(int) RTDbgCfgOpenPeImage(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
807 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
808{
809 char szSubDir[32];
810 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%X", uTimestamp, cbImage);
811 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir,
812 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
813 | RTDBGCFG_O_EXECUTABLE_IMAGE,
814 pfnCallback, pvUser1, pvUser2);
815}
816
817
818RTDECL(int) RTDbgCfgOpenPdb70(RTDBGCFG hDbgCfg, const char *pszFilename, PCRTUUID pUuid, uint32_t uAge,
819 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
820{
821 char szSubDir[64];
822 if (!pUuid)
823 szSubDir[0] = '\0';
824 else
825 {
826 /* Stringify the UUID and remove the dashes. */
827 int rc2 = RTUuidToStr(pUuid, szSubDir, sizeof(szSubDir));
828 AssertRCReturn(rc2, rc2);
829
830 char *pszSrc = szSubDir;
831 char *pszDst = szSubDir;
832 char ch;
833 while ((ch = *pszSrc++))
834 if (ch != '-')
835 *pszDst++ = ch;
836 else if (RT_C_IS_LOWER(ch))
837 *pszDst++ = RT_C_TO_UPPER(ch);
838
839 RTStrPrintf(pszDst, &szSubDir[sizeof(szSubDir)] - pszDst, "%X", uAge);
840 }
841
842 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir,
843 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
844 | RTDBGCFG_O_EXT_DEBUG_FILE,
845 pfnCallback, pvUser1, pvUser2);
846}
847
848
849RTDECL(int) RTDbgCfgOpenPdb20(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, uint32_t uAge,
850 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
851{
852 /** @todo test this! */
853 char szSubDir[32];
854 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%X", uTimestamp, uAge);
855 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir,
856 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
857 | RTDBGCFG_O_EXT_DEBUG_FILE,
858 pfnCallback, pvUser1, pvUser2);
859}
860
861
862RTDECL(int) RTDbgCfgOpenDbg(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
863 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
864{
865 char szSubDir[32];
866 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%X", uTimestamp, cbImage);
867 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir,
868 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
869 | RTDBGCFG_O_EXT_DEBUG_FILE,
870 pfnCallback, pvUser1, pvUser2);
871}
872
873
874RTDECL(int) RTDbgCfgOpenDwo(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t uCrc32,
875 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
876{
877 char szSubDir[32];
878 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08x", uCrc32);
879 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir,
880 RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE,
881 pfnCallback, pvUser1, pvUser2);
882}
883
884
885
886
887/**
888 * Frees a string list.
889 *
890 * @param pList The list to free.
891 */
892static void rtDbgCfgFreeStrList(PRTLISTANCHOR pList)
893{
894 PRTDBGCFGSTR pCur;
895 PRTDBGCFGSTR pNext;
896 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
897 {
898 RTListNodeRemove(&pCur->ListEntry);
899 RTMemFree(pCur);
900 }
901}
902
903
904/**
905 * Make changes to a string list, given a semicolon separated input string.
906 *
907 * @returns VINF_SUCCESS, VERR_FILENAME_TOO_LONG, VERR_NO_MEMORY
908 * @param pThis The config instance.
909 * @param enmOp The change operation.
910 * @param pszValue The input strings separated by semicolon.
911 * @param fPaths Indicates that this is a path list and that we
912 * should look for srv and cache prefixes.
913 * @param pList The string list anchor.
914 */
915static int rtDbgCfgChangeStringList(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, bool fPaths,
916 PRTLISTANCHOR pList)
917{
918 if (enmOp == RTDBGCFGOP_SET)
919 rtDbgCfgFreeStrList(pList);
920
921 while (*pszValue)
922 {
923 /* Skip separators. */
924 while (*pszValue == ';')
925 pszValue++;
926 if (!*pszValue)
927 break;
928
929 /* Find the end of this path. */
930 const char *pchPath = pszValue++;
931 char ch;
932 while ((ch = *pszValue) && ch != ';')
933 pszValue++;
934 size_t cchPath = pszValue - pchPath;
935 if (cchPath >= UINT16_MAX)
936 return VERR_FILENAME_TOO_LONG;
937
938 if (enmOp == RTDBGCFGOP_REMOVE)
939 {
940 /*
941 * Remove all occurences.
942 */
943 PRTDBGCFGSTR pCur;
944 PRTDBGCFGSTR pNext;
945 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
946 {
947 if ( pCur->cch == cchPath
948 && !memcmp(pCur->sz, pchPath, cchPath))
949 {
950 RTListNodeRemove(&pCur->ListEntry);
951 RTMemFree(pCur);
952 }
953 }
954 }
955 else
956 {
957 /*
958 * We're adding a new one.
959 */
960 PRTDBGCFGSTR pNew = (PRTDBGCFGSTR)RTMemAlloc(RT_OFFSETOF(RTDBGCFGSTR, sz[cchPath + 1]));
961 if (!pNew)
962 return VERR_NO_MEMORY;
963 pNew->cch = (uint16_t)cchPath;
964 pNew->fFlags = 0;
965 memcpy(pNew->sz, pchPath, cchPath);
966 pNew->sz[cchPath] = '\0';
967
968 if (enmOp == RTDBGCFGOP_PREPEND)
969 RTListPrepend(pList, &pNew->ListEntry);
970 else
971 RTListAppend(pList, &pNew->ListEntry);
972 }
973 }
974
975 return VINF_SUCCESS;
976}
977
978
979/**
980 * Make changes to a 64-bit value
981 *
982 * @returns VINF_SUCCESS, VERR_DBG_CFG_INVALID_VALUE.
983 * @param pThis The config instance.
984 * @param enmOp The change operation.
985 * @param pszValue The input value.
986 * @param pszMnemonics The mnemonics map for this value.
987 * @param puValue The value to change.
988 */
989static int rtDbgCfgChangeStringU64(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue,
990 PCRTDBGCFGU64MNEMONIC paMnemonics, uint64_t *puValue)
991{
992 uint64_t uNew = enmOp == RTDBGCFGOP_SET ? 0 : *puValue;
993
994 char ch;
995 while ((ch = *pszValue))
996 {
997 /* skip whitespace and separators */
998 while (RT_C_IS_SPACE(ch) || RT_C_IS_CNTRL(ch) || ch == ';' || ch == ':')
999 ch = *++pszValue;
1000 if (!ch)
1001 break;
1002
1003 if (RT_C_IS_DIGIT(ch))
1004 {
1005 uint64_t uTmp;
1006 int rc = RTStrToUInt64Ex(pszValue, (char **)&pszValue, 0, &uTmp);
1007 if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG)
1008 return VERR_DBG_CFG_INVALID_VALUE;
1009
1010 if (enmOp != RTDBGCFGOP_REMOVE)
1011 uNew |= uTmp;
1012 else
1013 uNew &= ~uTmp;
1014 }
1015 else
1016 {
1017 /* A mnemonic, find the end of it. */
1018 const char *pszMnemonic = pszValue - 1;
1019 do
1020 ch = *++pszValue;
1021 while (ch && !RT_C_IS_SPACE(ch) && !RT_C_IS_CNTRL(ch) && ch != ';' && ch != ':');
1022 size_t cchMnemonic = pszValue - pszMnemonic;
1023
1024 /* Look it up in the map and apply it. */
1025 unsigned i = 0;
1026 while (paMnemonics[i].pszMnemonic)
1027 {
1028 if ( cchMnemonic == paMnemonics[i].cchMnemonic
1029 && !memcmp(pszMnemonic, paMnemonics[i].pszMnemonic, cchMnemonic))
1030 {
1031 if (paMnemonics[i].fSet ? enmOp != RTDBGCFGOP_REMOVE : enmOp == RTDBGCFGOP_REMOVE)
1032 uNew |= paMnemonics[i].fFlags;
1033 else
1034 uNew &= ~paMnemonics[i].fFlags;
1035 break;
1036 }
1037 i++;
1038 }
1039
1040 if (!paMnemonics[i].pszMnemonic)
1041 return VERR_DBG_CFG_INVALID_VALUE;
1042 }
1043 }
1044
1045 *puValue = uNew;
1046 return VINF_SUCCESS;
1047}
1048
1049
1050RTDECL(int) RTDbgCfgChangeString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, const char *pszValue)
1051{
1052 PRTDBGCFGINT pThis = hDbgCfg;
1053 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1054 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
1055 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
1056 if (!pszValue)
1057 pszValue = "";
1058 else
1059 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
1060
1061 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
1062 if (RT_SUCCESS(rc))
1063 {
1064 switch (enmProp)
1065 {
1066 case RTDBGCFGPROP_FLAGS:
1067 rc = rtDbgCfgChangeStringU64(pThis, enmOp, pszValue, g_aDbgCfgFlags, &pThis->fFlags);
1068 break;
1069 case RTDBGCFGPROP_PATH:
1070 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->PathList);
1071 break;
1072 case RTDBGCFGPROP_SUFFIXES:
1073 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, false, &pThis->SuffixList);
1074 break;
1075 case RTDBGCFGPROP_SRC_PATH:
1076 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->SrcPathList);
1077 break;
1078 default:
1079 AssertFailed();
1080 rc = VERR_INTERNAL_ERROR_3;
1081 }
1082
1083 RTCritSectRwLeaveExcl(&pThis->CritSect);
1084 }
1085
1086 return rc;
1087}
1088
1089
1090RTDECL(int) RTDbgCfgChangeUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, uint64_t uValue)
1091{
1092 PRTDBGCFGINT pThis = hDbgCfg;
1093 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1094 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
1095 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
1096
1097 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
1098 if (RT_SUCCESS(rc))
1099 {
1100 uint64_t *puValue = NULL;
1101 switch (enmProp)
1102 {
1103 case RTDBGCFGPROP_FLAGS:
1104 puValue = &pThis->fFlags;
1105 break;
1106 default:
1107 rc = VERR_DBG_CFG_NOT_UINT_PROP;
1108 }
1109 if (RT_SUCCESS(rc))
1110 {
1111 switch (enmOp)
1112 {
1113 case RTDBGCFGOP_SET:
1114 *puValue = uValue;
1115 break;
1116 case RTDBGCFGOP_APPEND:
1117 case RTDBGCFGOP_PREPEND:
1118 *puValue |= uValue;
1119 break;
1120 case RTDBGCFGOP_REMOVE:
1121 *puValue &= ~uValue;
1122 break;
1123 default:
1124 AssertFailed();
1125 rc = VERR_INTERNAL_ERROR_2;
1126 }
1127 }
1128
1129 RTCritSectRwLeaveExcl(&pThis->CritSect);
1130 }
1131
1132 return rc;
1133}
1134
1135
1136/**
1137 * Querys a string list as a single string (semicolon separators).
1138 *
1139 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
1140 * @param pThis The config instance.
1141 * @param pList The string list anchor.
1142 * @param pszValue The output buffer.
1143 * @param cbValue The size of the output buffer.
1144 */
1145static int rtDbgCfgQueryStringList(RTDBGCFG hDbgCfg, PRTLISTANCHOR pList,
1146 char *pszValue, size_t cbValue)
1147{
1148 /*
1149 * Check the length first.
1150 */
1151 size_t cbReq = 1;
1152 PRTDBGCFGSTR pCur;
1153 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
1154 cbReq += pCur->cch + 1;
1155 if (cbReq > cbValue)
1156 return VERR_BUFFER_OVERFLOW;
1157
1158 /*
1159 * Construct the string list in the buffer.
1160 */
1161 char *psz = pszValue;
1162 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
1163 {
1164 if (psz != pszValue)
1165 *psz++ = ';';
1166 memcpy(psz, pCur->sz, pCur->cch);
1167 psz += pCur->cch;
1168 }
1169 *psz = '\0';
1170
1171 return VINF_SUCCESS;
1172}
1173
1174
1175/**
1176 * Querys the string value of a 64-bit unsigned int.
1177 *
1178 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
1179 * @param pThis The config instance.
1180 * @param uValue The value to query.
1181 * @param pszMnemonics The mnemonics map for this value.
1182 * @param pszValue The output buffer.
1183 * @param cbValue The size of the output buffer.
1184 */
1185static int rtDbgCfgQueryStringU64(RTDBGCFG hDbgCfg, uint64_t uValue, PCRTDBGCFGU64MNEMONIC paMnemonics,
1186 char *pszValue, size_t cbValue)
1187{
1188 /*
1189 * If no mnemonics, just return the hex value.
1190 */
1191 if (!paMnemonics || paMnemonics[0].pszMnemonic)
1192 {
1193 char szTmp[64];
1194 size_t cch = RTStrPrintf(szTmp, sizeof(szTmp), "%#x", uValue);
1195 if (cch + 1 > cbValue)
1196 return VERR_BUFFER_OVERFLOW;
1197 memcpy(pszValue, szTmp, cbValue);
1198 return VINF_SUCCESS;
1199 }
1200
1201 /*
1202 * Check that there is sufficient buffer space first.
1203 */
1204 size_t cbReq = 1;
1205 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
1206 if ( paMnemonics[i].fSet
1207 ? (paMnemonics[i].fFlags & uValue)
1208 : !(paMnemonics[i].fFlags & uValue))
1209 cbReq += (cbReq != 1) + paMnemonics[i].cchMnemonic;
1210 if (cbReq > cbValue)
1211 return VERR_BUFFER_OVERFLOW;
1212
1213 /*
1214 * Construct the string.
1215 */
1216 char *psz = pszValue;
1217 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
1218 if ( paMnemonics[i].fSet
1219 ? (paMnemonics[i].fFlags & uValue)
1220 : !(paMnemonics[i].fFlags & uValue))
1221 {
1222 if (psz != pszValue)
1223 *psz++ = ' ';
1224 memcpy(psz, paMnemonics[i].pszMnemonic, paMnemonics[i].cchMnemonic);
1225 psz += paMnemonics[i].cchMnemonic;
1226 }
1227 *psz = '\0';
1228 return VINF_SUCCESS;
1229}
1230
1231
1232RTDECL(int) RTDbgCfgQueryString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, char *pszValue, size_t cbValue)
1233{
1234 PRTDBGCFGINT pThis = hDbgCfg;
1235 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1236 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
1237 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
1238
1239 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
1240 if (RT_SUCCESS(rc))
1241 {
1242 switch (enmProp)
1243 {
1244 case RTDBGCFGPROP_FLAGS:
1245 rc = rtDbgCfgQueryStringU64(pThis, pThis->fFlags, g_aDbgCfgFlags, pszValue, cbValue);
1246 break;
1247 case RTDBGCFGPROP_PATH:
1248 rc = rtDbgCfgQueryStringList(pThis, &pThis->PathList, pszValue, cbValue);
1249 break;
1250 case RTDBGCFGPROP_SUFFIXES:
1251 rc = rtDbgCfgQueryStringList(pThis, &pThis->SuffixList, pszValue, cbValue);
1252 break;
1253 case RTDBGCFGPROP_SRC_PATH:
1254 rc = rtDbgCfgQueryStringList(pThis, &pThis->SrcPathList, pszValue, cbValue);
1255 break;
1256 default:
1257 AssertFailed();
1258 rc = VERR_INTERNAL_ERROR_3;
1259 }
1260
1261 RTCritSectRwLeaveShared(&pThis->CritSect);
1262 }
1263
1264 return rc;
1265}
1266
1267
1268RTDECL(int) RTDbgCfgQueryUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, uint64_t *puValue)
1269{
1270 PRTDBGCFGINT pThis = hDbgCfg;
1271 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1272 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
1273 AssertPtrReturn(puValue, VERR_INVALID_POINTER);
1274
1275 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
1276 if (RT_SUCCESS(rc))
1277 {
1278 switch (enmProp)
1279 {
1280 case RTDBGCFGPROP_FLAGS:
1281 *puValue = pThis->fFlags;
1282 break;
1283 default:
1284 rc = VERR_DBG_CFG_NOT_UINT_PROP;
1285 }
1286
1287 RTCritSectRwLeaveShared(&pThis->CritSect);
1288 }
1289
1290 return rc;
1291}
1292
1293RTDECL(uint32_t) RTDbgCfgRetain(RTDBGCFG hDbgCfg)
1294{
1295 PRTDBGCFGINT pThis = hDbgCfg;
1296 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
1297
1298 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
1299 Assert(cRefs < UINT32_MAX / 2);
1300 return cRefs;
1301}
1302
1303
1304RTDECL(uint32_t) RTDbgCfgRelease(RTDBGCFG hDbgCfg)
1305{
1306 if (hDbgCfg == NIL_RTDBGCFG)
1307 return 0;
1308
1309 PRTDBGCFGINT pThis = hDbgCfg;
1310 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
1311
1312 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
1313 if (!cRefs)
1314 {
1315 /*
1316 * Last reference - free all memory.
1317 */
1318 ASMAtomicWriteU32(&pThis->u32Magic, ~RTDBGCFG_MAGIC);
1319 rtDbgCfgFreeStrList(&pThis->PathList);
1320 rtDbgCfgFreeStrList(&pThis->SuffixList);
1321 rtDbgCfgFreeStrList(&pThis->SrcPathList);
1322#ifdef RT_OS_WINDOWS
1323 rtDbgCfgFreeStrList(&pThis->NtSymbolPathList);
1324 rtDbgCfgFreeStrList(&pThis->NtExecutablePathList);
1325 rtDbgCfgFreeStrList(&pThis->NtSourcePath);
1326#endif
1327 RTCritSectRwDelete(&pThis->CritSect);
1328 RTMemFree(pThis);
1329 }
1330 else
1331 Assert(cRefs < UINT32_MAX / 2);
1332 return cRefs;
1333}
1334
1335
1336RTDECL(int) RTDbgCfgCreate(PRTDBGCFG phDbgCfg, const char *pszEnvVarPrefix)
1337{
1338 /*
1339 * Validate input.
1340 */
1341 AssertPtrReturn(phDbgCfg, VERR_INVALID_POINTER);
1342 if (pszEnvVarPrefix)
1343 {
1344 AssertPtrReturn(pszEnvVarPrefix, VERR_INVALID_POINTER);
1345 AssertReturn(*pszEnvVarPrefix, VERR_INVALID_PARAMETER);
1346 }
1347
1348 /*
1349 * Allocate and initialize a new instance.
1350 */
1351 PRTDBGCFGINT pThis = (PRTDBGCFGINT)RTMemAllocZ(sizeof(*pThis));
1352 if (!pThis)
1353 return VERR_NO_MEMORY;
1354
1355 pThis->u32Magic = RTDBGCFG_MAGIC;
1356 pThis->cRefs = 1;
1357 RTListInit(&pThis->PathList);
1358 RTListInit(&pThis->SuffixList);
1359 RTListInit(&pThis->SrcPathList);
1360#ifdef RT_OS_WINDOWS
1361 RTListInit(&pThis->NtSymbolPathList);
1362 RTListInit(&pThis->NtExecutablePathList);
1363 RTListInit(&pThis->NtSourcePath);
1364#endif
1365
1366 int rc = RTCritSectRwInit(&pThis->CritSect);
1367 if (RT_FAILURE(rc))
1368 {
1369 RTMemFree(pThis);
1370 return rc;
1371 }
1372
1373 /*
1374 * Read configurtion from the environment if requested to do so.
1375 */
1376 if (pszEnvVarPrefix)
1377 {
1378 static struct
1379 {
1380 RTDBGCFGPROP enmProp;
1381 const char *pszVar;
1382 } const s_aProps[] =
1383 {
1384 { RTDBGCFGPROP_FLAGS, "FLAGS" },
1385 { RTDBGCFGPROP_PATH, "PATH" },
1386 { RTDBGCFGPROP_SUFFIXES, "SUFFIXES" },
1387 { RTDBGCFGPROP_SRC_PATH, "SRC_PATH" },
1388 };
1389 const size_t cbEnvVar = 256;
1390 const size_t cbEnvVal = 65536 - cbEnvVar;
1391 char *pszEnvVar = (char *)RTMemTmpAlloc(cbEnvVar + cbEnvVal);
1392 if (pszEnvVar)
1393 {
1394 char *pszEnvVal = pszEnvVar + cbEnvVar;
1395 for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++)
1396 {
1397 size_t cchEnvVar = RTStrPrintf(pszEnvVar, cbEnvVar, "%s_%s", pszEnvVarPrefix, s_aProps[i].pszVar);
1398 if (cchEnvVar >= cbEnvVar - 1)
1399 {
1400 rc = VERR_BUFFER_OVERFLOW;
1401 break;
1402 }
1403
1404 rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, pszEnvVal, cbEnvVal, NULL);
1405 if (RT_SUCCESS(rc))
1406 {
1407 rc = RTDbgCfgChangeString(pThis, s_aProps[i].enmProp, RTDBGCFGOP_SET, pszEnvVal);
1408 if (RT_FAILURE(rc))
1409 break;
1410 }
1411 else if (rc != VERR_ENV_VAR_NOT_FOUND)
1412 break;
1413 else
1414 rc = VINF_SUCCESS;
1415 }
1416
1417 /*
1418 * Pick up system specific search paths.
1419 */
1420 if (RT_SUCCESS(rc))
1421 {
1422 struct
1423 {
1424 PRTLISTANCHOR pList;
1425 const char *pszVar;
1426 char chSep;
1427 } aNativePaths[] =
1428 {
1429#ifdef RT_OS_WINDOWS
1430 { &pThis->NtExecutablePathList, "_NT_EXECUTABLE_PATH", ';' },
1431 { &pThis->NtSymbolPathList, "_NT_ALT_SYMBOL_PATH", ';' },
1432 { &pThis->NtSymbolPathList, "_NT_SYMBOL_PATH", ';' },
1433 { &pThis->NtSourcePath, "_NT_SOURCE_PATH", ';' },
1434#endif
1435 { NULL, NULL, 0 }
1436 };
1437 for (unsigned i = 0; i < RT_ELEMENTS(aNativePaths) - 1U; i++)
1438 {
1439 Assert(aNativePaths[i].chSep == ';'); /* fix when needed */
1440 rc = RTEnvGetEx(RTENV_DEFAULT, aNativePaths[i].pszVar, pszEnvVal, cbEnvVal, NULL);
1441 if (RT_SUCCESS(rc))
1442 {
1443 rc = rtDbgCfgChangeStringList(pThis, RTDBGCFGOP_APPEND, pszEnvVal, true, aNativePaths[i].pList);
1444 if (RT_FAILURE(rc))
1445 break;
1446 }
1447 else if (rc != VERR_ENV_VAR_NOT_FOUND)
1448 break;
1449 else
1450 rc = VINF_SUCCESS;
1451 }
1452 }
1453 RTMemTmpFree(pszEnvVar);
1454 }
1455 else
1456 rc = VERR_NO_TMP_MEMORY;
1457 if (RT_FAILURE(rc))
1458 {
1459 /*
1460 * Error, bail out.
1461 */
1462 RTDbgCfgRelease(pThis);
1463 return rc;
1464 }
1465 }
1466
1467 /*
1468 * Returns successfully.
1469 */
1470 *phDbgCfg = pThis;
1471
1472 return VINF_SUCCESS;
1473}
1474
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette