VirtualBox

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

最後變更 在這個檔案從62564是 62564,由 vboxsync 提交於 9 年 前

IPRT: Mark unused parameters.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 83.9 KB
 
1/* $Id: dbgcfg.cpp 62564 2016-07-26 14:43:03Z vboxsync $ */
2/** @file
3 * IPRT - Debugging Configuration.
4 */
5
6/*
7 * Copyright (C) 2013-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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/alloca.h>
36#include <iprt/asm.h>
37#include <iprt/assert.h>
38#include <iprt/critsect.h>
39#include <iprt/ctype.h>
40#include <iprt/dir.h>
41#include <iprt/err.h>
42#include <iprt/env.h>
43#include <iprt/file.h>
44#ifdef IPRT_WITH_HTTP
45# include <iprt/http.h>
46#endif
47#include <iprt/list.h>
48#include <iprt/log.h>
49#include <iprt/mem.h>
50#include <iprt/path.h>
51#include <iprt/process.h>
52#include <iprt/semaphore.h>
53#include <iprt/string.h>
54#include <iprt/uuid.h>
55#include "internal/magics.h"
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61/**
62 * String list entry.
63 */
64typedef struct RTDBGCFGSTR
65{
66 /** List entry. */
67 RTLISTNODE ListEntry;
68 /** Domain specific flags. */
69 uint16_t fFlags;
70 /** The length of the string. */
71 uint16_t cch;
72 /** The string. */
73 char sz[1];
74} RTDBGCFGSTR;
75/** Pointer to a string list entry. */
76typedef RTDBGCFGSTR *PRTDBGCFGSTR;
77
78
79/**
80 * Configuration instance.
81 */
82typedef struct RTDBGCFGINT
83{
84 /** The magic value (RTDBGCFG_MAGIC). */
85 uint32_t u32Magic;
86 /** Reference counter. */
87 uint32_t volatile cRefs;
88 /** Flags, see RTDBGCFG_FLAGS_XXX. */
89 uint64_t fFlags;
90
91 /** List of paths to search for debug files and executable images. */
92 RTLISTANCHOR PathList;
93 /** List of debug file suffixes. */
94 RTLISTANCHOR SuffixList;
95 /** List of paths to search for source files. */
96 RTLISTANCHOR SrcPathList;
97
98#ifdef RT_OS_WINDOWS
99 /** The _NT_ALT_SYMBOL_PATH and _NT_SYMBOL_PATH combined. */
100 RTLISTANCHOR NtSymbolPathList;
101 /** The _NT_EXECUTABLE_PATH. */
102 RTLISTANCHOR NtExecutablePathList;
103 /** The _NT_SOURCE_PATH. */
104 RTLISTANCHOR NtSourcePath;
105#endif
106
107 /** Log callback function. */
108 PFNRTDBGCFGLOG pfnLogCallback;
109 /** User argument to pass to the log callback. */
110 void *pvLogUser;
111
112 /** Critical section protecting the instance data. */
113 RTCRITSECTRW CritSect;
114} *PRTDBGCFGINT;
115
116/**
117 * Mnemonics map entry for a 64-bit unsigned property value.
118 */
119typedef struct RTDBGCFGU64MNEMONIC
120{
121 /** The flags to set or clear. */
122 uint64_t fFlags;
123 /** The mnemonic. */
124 const char *pszMnemonic;
125 /** The length of the mnemonic. */
126 uint8_t cchMnemonic;
127 /** If @c true, the bits in fFlags will be set, if @c false they will be
128 * cleared. */
129 bool fSet;
130} RTDBGCFGU64MNEMONIC;
131/** Pointer to a read only mnemonic map entry for a uint64_t property. */
132typedef RTDBGCFGU64MNEMONIC const *PCRTDBGCFGU64MNEMONIC;
133
134
135/** @name Open flags.
136 * @{ */
137/** The operative system mask. The values are RT_OPSYS_XXX. */
138#define RTDBGCFG_O_OPSYS_MASK UINT32_C(0x000000ff)
139/** The files may be compressed MS styled. */
140#define RTDBGCFG_O_MAYBE_COMPRESSED_MS RT_BIT_32(26)
141/** Whether to make a recursive search. */
142#define RTDBGCFG_O_RECURSIVE RT_BIT_32(27)
143/** We're looking for a separate debug file. */
144#define RTDBGCFG_O_EXT_DEBUG_FILE RT_BIT_32(28)
145/** We're looking for an executable image. */
146#define RTDBGCFG_O_EXECUTABLE_IMAGE RT_BIT_32(29)
147/** The file search should be done in an case insensitive fashion. */
148#define RTDBGCFG_O_CASE_INSENSITIVE RT_BIT_32(30)
149/** Use Windbg style symbol servers when encountered in the path. */
150#define RTDBGCFG_O_SYMSRV RT_BIT_32(31)
151/** @} */
152
153
154/*********************************************************************************************************************************
155* Defined Constants And Macros *
156*********************************************************************************************************************************/
157/** Validates a debug module handle and returns rc if not valid. */
158#define RTDBGCFG_VALID_RETURN_RC(pThis, rc) \
159 do { \
160 AssertPtrReturn((pThis), (rc)); \
161 AssertReturn((pThis)->u32Magic == RTDBGCFG_MAGIC, (rc)); \
162 AssertReturn((pThis)->cRefs > 0, (rc)); \
163 } while (0)
164
165
166/*********************************************************************************************************************************
167* Global Variables *
168*********************************************************************************************************************************/
169/** Mnemonics map for RTDBGCFGPROP_FLAGS. */
170static const RTDBGCFGU64MNEMONIC g_aDbgCfgFlags[] =
171{
172 { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("deferred"), true },
173 { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("nodeferred"), false },
174 { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("symsrv"), false },
175 { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("nosymsrv"), true },
176 { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("syspaths"), false },
177 { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("nosyspaths"), true },
178 { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("rec"), false },
179 { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("norec"), true },
180 { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("recsrc"), false },
181 { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("norecsrc"), true },
182 { 0, NULL, 0, false }
183};
184
185
186/** Interesting bundle suffixes. */
187static const char * const g_apszBundleSuffixes[] =
188{
189 ".kext",
190 ".app",
191 ".framework",
192 ".component",
193 ".action",
194 ".caction",
195 ".bundle",
196 ".sourcebundle",
197 ".menu",
198 ".plugin",
199 ".ppp",
200 ".monitorpanel",
201 ".scripting",
202 ".prefPane",
203 ".qlgenerator",
204 ".brailledriver",
205 ".saver",
206 ".SpeechVoice",
207 ".SpeechRecognizer",
208 ".SpeechSynthesizer",
209 ".mdimporter",
210 ".spreporter",
211 ".xpc",
212 NULL
213};
214
215/** Debug bundle suffixes. (Same as above + .dSYM) */
216static const char * const g_apszDSymBundleSuffixes[] =
217{
218 ".dSYM",
219 ".kext.dSYM",
220 ".app.dSYM",
221 ".framework.dSYM",
222 ".component.dSYM",
223 ".action.dSYM",
224 ".caction.dSYM",
225 ".bundle.dSYM",
226 ".sourcebundle.dSYM",
227 ".menu.dSYM",
228 ".plugin.dSYM",
229 ".ppp.dSYM",
230 ".monitorpanel.dSYM",
231 ".scripting.dSYM",
232 ".prefPane.dSYM",
233 ".qlgenerator.dSYM",
234 ".brailledriver.dSYM",
235 ".saver.dSYM",
236 ".SpeechVoice.dSYM",
237 ".SpeechRecognizer.dSYM",
238 ".SpeechSynthesizer.dSYM",
239 ".mdimporter.dSYM",
240 ".spreporter.dSYM",
241 ".xpc.dSYM",
242 NULL
243};
244
245
246
247/**
248 * Runtime logging, level 1.
249 *
250 * @param pThis The debug config instance data.
251 * @param pszFormat The message format string.
252 * @param ... Arguments references in the format string.
253 */
254static void rtDbgCfgLog1(PRTDBGCFGINT pThis, const char *pszFormat, ...)
255{
256 if (LogIsEnabled() || (pThis && pThis->pfnLogCallback))
257 {
258 va_list va;
259 va_start(va, pszFormat);
260 char *pszMsg = RTStrAPrintf2V(pszFormat, va);
261 va_end(va);
262
263 Log(("RTDbgCfg: %s", pszMsg));
264 if (pThis && pThis->pfnLogCallback)
265 pThis->pfnLogCallback(pThis, 1, pszMsg, pThis->pvLogUser);
266 RTStrFree(pszMsg);
267 }
268}
269
270
271/**
272 * Runtime logging, level 2.
273 *
274 * @param pThis The debug config instance data.
275 * @param pszFormat The message format string.
276 * @param ... Arguments references in the format string.
277 */
278static void rtDbgCfgLog2(PRTDBGCFGINT pThis, const char *pszFormat, ...)
279{
280 if (LogIs2Enabled() || (pThis && pThis->pfnLogCallback))
281 {
282 va_list va;
283 va_start(va, pszFormat);
284 char *pszMsg = RTStrAPrintf2V(pszFormat, va);
285 va_end(va);
286
287 Log(("RTDbgCfg: %s", pszMsg));
288 if (pThis && pThis->pfnLogCallback)
289 pThis->pfnLogCallback(pThis, 2, pszMsg, pThis->pvLogUser);
290 RTStrFree(pszMsg);
291 }
292}
293
294
295/**
296 * Checks if the file system at the given path is case insensitive or not.
297 *
298 * @returns true / false
299 * @param pszPath The path to query about.
300 */
301static int rtDbgCfgIsFsCaseInsensitive(const char *pszPath)
302{
303 RTFSPROPERTIES Props;
304 int rc = RTFsQueryProperties(pszPath, &Props);
305 if (RT_FAILURE(rc))
306 return RT_OPSYS == RT_OPSYS_DARWIN
307 || RT_OPSYS == RT_OPSYS_DOS
308 || RT_OPSYS == RT_OPSYS_OS2
309 || RT_OPSYS == RT_OPSYS_NT
310 || RT_OPSYS == RT_OPSYS_WINDOWS;
311 return !Props.fCaseSensitive;
312}
313
314
315/**
316 * Worker that does case sensitive file/dir searching.
317 *
318 * @returns true / false.
319 * @param pszPath The path buffer containing an existing directory and
320 * at @a offLastComp the name we're looking for.
321 * RTPATH_MAX in size. On success, this last component
322 * will have the correct case. On failure, the last
323 * component is stripped off.
324 * @param offLastComp The offset of the last component (for chopping it
325 * off).
326 * @param enmType What kind of thing we're looking for.
327 */
328static bool rtDbgCfgIsXxxxAndFixCaseWorker(char *pszPath, size_t offLastComp, RTDIRENTRYTYPE enmType)
329{
330 /** @todo IPRT should generalize this so we can use host specific tricks to
331 * speed it up. */
332
333 char *pszName = &pszPath[offLastComp];
334
335 /* Return straight away if the name isn't case foldable. */
336 if (!RTStrIsCaseFoldable(pszName))
337 {
338 *pszName = '\0';
339 return false;
340 }
341
342 /*
343 * Try some simple case folding games.
344 */
345 RTStrToLower(pszName);
346 if (RTFileExists(pszPath))
347 return true;
348
349 RTStrToUpper(pszName);
350 if (RTFileExists(pszPath))
351 return true;
352
353 /*
354 * Open the directory and check each entry in it.
355 */
356 char chSaved = *pszName;
357 *pszName = '\0';
358
359 PRTDIR pDir;
360 int rc = RTDirOpen(&pDir, pszPath);
361 if (RT_FAILURE(rc))
362 return false;
363
364 *pszName = chSaved;
365
366 for (;;)
367 {
368 /* Read the next entry. */
369 union
370 {
371 RTDIRENTRY Entry;
372 uint8_t ab[_4K];
373 } u;
374 size_t cbBuf = sizeof(u);
375 rc = RTDirRead(pDir, &u.Entry, &cbBuf);
376 if (RT_FAILURE(rc))
377 break;
378
379 if ( !RTStrICmp(pszName, u.Entry.szName)
380 && ( u.Entry.enmType == enmType
381 || u.Entry.enmType == RTDIRENTRYTYPE_UNKNOWN
382 || u.Entry.enmType == RTDIRENTRYTYPE_SYMLINK) )
383 {
384 strcpy(pszName, u.Entry.szName);
385 if (u.Entry.enmType != enmType)
386 RTDirQueryUnknownType(pszPath, true /*fFollowSymlinks*/, &u.Entry.enmType);
387 if (u.Entry.enmType == enmType)
388 {
389 RTDirClose(pDir);
390 return true;
391 }
392 }
393 }
394
395 RTDirClose(pDir);
396 *pszName = '\0';
397
398 return false;
399}
400
401
402/**
403 * Appends @a pszSubDir to @a pszPath and check whether it exists and is a
404 * directory.
405 *
406 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
407 * matching sub directory.
408 *
409 * @returns true / false
410 * @param pszPath The path buffer containing an existing
411 * directory. RTPATH_MAX in size.
412 * @param pszSubDir The sub directory to append.
413 * @param fCaseInsensitive Whether case insensitive searching is required.
414 */
415static bool rtDbgCfgIsDirAndFixCase(char *pszPath, const char *pszSubDir, bool fCaseInsensitive)
416{
417 /* Save the length of the input path so we can restore it in the case
418 insensitive branch further down. */
419 size_t const cchPath = strlen(pszPath);
420
421 /*
422 * Append the sub directory and check if we got a hit.
423 */
424 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
425 if (RT_FAILURE(rc))
426 return false;
427
428 if (RTDirExists(pszPath))
429 return true;
430
431 /*
432 * Do case insensitive lookup if requested.
433 */
434 if (fCaseInsensitive)
435 return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_DIRECTORY);
436
437 pszPath[cchPath] = '\0';
438 return false;
439}
440
441
442/**
443 * Appends @a pszSubDir1 and @a pszSuffix to @a pszPath and check whether it
444 * exists and is a directory.
445 *
446 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
447 * matching sub directory.
448 *
449 * @returns true / false
450 * @param pszPath The path buffer containing an existing
451 * directory. RTPATH_MAX in size.
452 * @param pszSubDir The sub directory to append.
453 * @param pszSuffix The suffix to append.
454 * @param fCaseInsensitive Whether case insensitive searching is required.
455 */
456static bool rtDbgCfgIsDirAndFixCase2(char *pszPath, const char *pszSubDir, const char *pszSuffix, bool fCaseInsensitive)
457{
458 Assert(!strpbrk(pszSuffix, ":/\\"));
459
460 /* Save the length of the input path so we can restore it in the case
461 insensitive branch further down. */
462 size_t const cchPath = strlen(pszPath);
463
464 /*
465 * Append the subdirectory and suffix, then check if we got a hit.
466 */
467 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
468 if (RT_SUCCESS(rc))
469 {
470 rc = RTStrCat(pszPath, RTPATH_MAX, pszSuffix);
471 if (RT_SUCCESS(rc))
472 {
473 if (RTDirExists(pszPath))
474 return true;
475
476 /*
477 * Do case insensitive lookup if requested.
478 */
479 if (fCaseInsensitive)
480 return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_DIRECTORY);
481 }
482 }
483
484 pszPath[cchPath] = '\0';
485 return false;
486}
487
488
489/**
490 * Appends @a pszFilename to @a pszPath and check whether it exists and is a
491 * directory.
492 *
493 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
494 * matching filename.
495 *
496 * @returns true / false
497 * @param pszPath The path buffer containing an existing
498 * directory. RTPATH_MAX in size.
499 * @param pszFilename The filename to append.
500 * @param pszSuffix Optional filename suffix to append.
501 * @param fCaseInsensitive Whether case insensitive searching is required.
502 * @param fMsCompressed Whether to look for the MS compressed file name
503 * variant.
504 * @param pfProbablyCompressed This is set to true if a MS compressed
505 * filename variant is returned. Optional.
506 */
507static bool rtDbgCfgIsFileAndFixCase(char *pszPath, const char *pszFilename, const char *pszSuffix, bool fCaseInsensitive,
508 bool fMsCompressed, bool *pfProbablyCompressed)
509{
510 /* Save the length of the input path so we can restore it in the case
511 insensitive branch further down. */
512 size_t cchPath = strlen(pszPath);
513 if (pfProbablyCompressed)
514 *pfProbablyCompressed = false;
515
516 /*
517 * Append the filename and optionally suffix, then check if we got a hit.
518 */
519 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
520 if (RT_FAILURE(rc))
521 return false;
522 if (pszSuffix)
523 {
524 Assert(!fMsCompressed);
525 rc = RTStrCat(pszPath, RTPATH_MAX, pszSuffix);
526 if (RT_FAILURE(rc))
527 return false;
528 }
529
530 if (RTFileExists(pszPath))
531 return true;
532
533 /*
534 * Do case insensitive file lookup if requested.
535 */
536 if (fCaseInsensitive)
537 {
538 if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_FILE))
539 return true;
540 }
541
542 /*
543 * Look for MS compressed file if requested.
544 */
545 if ( fMsCompressed
546 && (unsigned char)pszFilename[strlen(pszFilename) - 1] < 0x7f)
547 {
548 pszPath[cchPath] = '\0';
549 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
550 AssertRCReturn(rc, false);
551 pszPath[strlen(pszPath) - 1] = '_';
552
553 if (pfProbablyCompressed)
554 *pfProbablyCompressed = true;
555
556 if ( RTFileExists(pszPath)
557 || ( fCaseInsensitive
558 && rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_FILE) ))
559 return true;
560
561 if (pfProbablyCompressed)
562 *pfProbablyCompressed = false;
563 }
564
565 pszPath[cchPath] = '\0';
566 return false;
567}
568
569
570static int rtDbgCfgTryOpenDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn, uint32_t fFlags,
571 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
572{
573 int rcRet = VWRN_NOT_FOUND;
574 int rc2;
575
576 /* If the directory doesn't exist, just quit immediately.
577 Note! Our case insensitivity doesn't extend to the search dirs themselfs,
578 only to the bits under neath them. */
579 if (!RTDirExists(pszPath))
580 {
581 rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
582 return rcRet;
583 }
584
585 /* Figure out whether we have to do a case sensitive search or not.
586 Note! As a simplification, we don't ask for case settings in each
587 directory under the user specified path, we assume the file
588 systems that mounted there have compatible settings. Faster
589 that way. */
590 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
591 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
592
593 size_t const cchPath = strlen(pszPath);
594
595 /*
596 * Look for the file with less and less of the original path given.
597 */
598 for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
599 {
600 pszPath[cchPath] = '\0';
601
602 rc2 = VINF_SUCCESS;
603 for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
604 if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
605 rc2 = VERR_FILE_NOT_FOUND;
606
607 if (RT_SUCCESS(rc2))
608 {
609 if (rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], NULL /*pszSuffix*/,
610 fCaseInsensitive, false, NULL))
611 {
612 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
613 rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
614 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
615 {
616 if (rc2 == VINF_CALLBACK_RETURN)
617 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
618 else
619 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
620 return rc2;
621 }
622 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
623 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
624 rcRet = rc2;
625 }
626 }
627 }
628
629 /*
630 * Do a recursive search if requested.
631 */
632 if ( (fFlags & RTDBGCFG_O_RECURSIVE)
633 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
634 {
635 /** @todo Recursive searching will be done later. */
636 }
637
638 return rcRet;
639}
640
641static int rtDbgCfgUnpackMsCacheFile(PRTDBGCFGINT pThis, char *pszPath, const char *pszFilename)
642{
643 rtDbgCfgLog2(pThis, "Unpacking '%s'...\n", pszPath);
644
645 /*
646 * Duplicate the source file path, just for simplicity and restore the
647 * final character in the orignal. We cheerfully ignorining any
648 * possibility of multibyte UTF-8 sequences just like the caller did when
649 * setting it to '_'.
650 */
651 char *pszSrcArchive = RTStrDup(pszPath);
652 if (!pszSrcArchive)
653 return VERR_NO_STR_MEMORY;
654
655 pszPath[strlen(pszPath) - 1] = RT_C_TO_LOWER(pszFilename[strlen(pszFilename) - 1]);
656
657
658 /*
659 * Figuring out the argument list for the platform specific unpack util.
660 */
661#ifdef RT_OS_WINDOWS
662 RTPathChangeToDosSlashes(pszSrcArchive, false /*fForce*/);
663 RTPathChangeToDosSlashes(pszPath, false /*fForce*/);
664 const char *papszArgs[] =
665 {
666 "expand.exe",
667 pszSrcArchive,
668 pszPath,
669 NULL
670 };
671
672#else
673 char szExtractDir[RTPATH_MAX];
674 strcpy(szExtractDir, pszPath);
675 RTPathStripFilename(szExtractDir);
676
677 const char *papszArgs[] =
678 {
679 "cabextract",
680 "-L", /* Lower case extracted files. */
681 "-d", szExtractDir, /* Extraction path */
682 pszSrcArchive,
683 NULL
684 };
685#endif
686
687 /*
688 * Do the unpacking.
689 */
690 RTPROCESS hChild;
691 int rc = RTProcCreate(papszArgs[0], papszArgs, RTENV_DEFAULT,
692#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
693 RTPROC_FLAGS_NO_WINDOW | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SEARCH_PATH,
694#else
695 RTPROC_FLAGS_SEARCH_PATH,
696#endif
697 &hChild);
698 if (RT_SUCCESS(rc))
699 {
700 RTPROCSTATUS ProcStatus;
701 rc = RTProcWait(hChild, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus);
702 if (RT_SUCCESS(rc))
703 {
704 if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
705 && ProcStatus.iStatus == 0)
706 {
707 if (RTPathExists(pszPath))
708 {
709 rtDbgCfgLog1(pThis, "Successfully unpacked '%s' to '%s'.\n", pszSrcArchive, pszPath);
710 rc = VINF_SUCCESS;
711 }
712 else
713 {
714 rtDbgCfgLog1(pThis, "Successfully ran unpacker on '%s', but '%s' is missing!\n", pszSrcArchive, pszPath);
715 rc = VERR_ZIP_ERROR;
716 }
717 }
718 else
719 {
720 rtDbgCfgLog2(pThis, "Unpacking '%s' failed: iStatus=%d enmReason=%d\n",
721 pszSrcArchive, ProcStatus.iStatus, ProcStatus.enmReason);
722 rc = VERR_ZIP_CORRUPTED;
723 }
724 }
725 else
726 rtDbgCfgLog1(pThis, "Error waiting for process: %Rrc\n", rc);
727
728 }
729 else
730 rtDbgCfgLog1(pThis, "Error starting unpack process '%s': %Rrc\n", papszArgs[0], rc);
731
732 return rc;
733}
734
735
736static int rtDbgCfgTryDownloadAndOpen(PRTDBGCFGINT pThis, const char *pszServer, char *pszPath,
737 const char *pszCacheSubDir, const char *pszUuidMappingSubDir,
738 PRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags,
739 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
740{
741#ifdef IPRT_WITH_HTTP
742 NOREF(pszUuidMappingSubDir); /** @todo do we bother trying pszUuidMappingSubDir? */
743 NOREF(pszCacheSuffix); /** @todo do we bother trying pszUuidMappingSubDir? */
744
745 if (pThis->fFlags & RTDBGCFG_FLAGS_NO_SYM_SRV)
746 return VWRN_NOT_FOUND;
747 if (!pszCacheSubDir || !*pszCacheSubDir)
748 return VWRN_NOT_FOUND;
749
750 /*
751 * Create the path.
752 */
753 size_t cchTmp = strlen(pszPath);
754
755 int rc = RTDirCreateFullPath(pszPath, 0766);
756 if (!RTDirExists(pszPath))
757 {
758 Log(("Error creating cache dir '%s': %Rrc\n", pszPath, rc));
759 return rc;
760 }
761
762 const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
763 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
764 if (RT_FAILURE(rc))
765 return rc;
766 RTStrToLower(&pszPath[cchTmp]);
767 if (!RTDirExists(pszPath))
768 {
769 rc = RTDirCreate(pszPath, 0766, 0);
770 if (RT_FAILURE(rc))
771 {
772 Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
773 }
774 }
775
776 rc = RTPathAppend(pszPath, RTPATH_MAX, pszCacheSubDir);
777 if (RT_FAILURE(rc))
778 return rc;
779 if (!RTDirExists(pszPath))
780 {
781 rc = RTDirCreate(pszPath, 0766, 0);
782 if (RT_FAILURE(rc))
783 {
784 Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
785 }
786 }
787
788 /* Prepare the destination file name while we're here. */
789 cchTmp = strlen(pszPath);
790 RTStrToLower(&pszPath[cchTmp]);
791 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
792 if (RT_FAILURE(rc))
793 return rc;
794
795 /*
796 * Download the file.
797 */
798 RTHTTP hHttp;
799 rc = RTHttpCreate(&hHttp);
800 if (RT_FAILURE(rc))
801 return rc;
802 RTHttpUseSystemProxySettings(hHttp);
803
804 static const char * const s_apszHeaders[] =
805 {
806 "User-Agent: Microsoft-Symbol-Server/6.6.0999.9",
807 "Pragma: no-cache",
808 };
809
810 rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeaders), s_apszHeaders);
811 if (RT_SUCCESS(rc))
812 {
813 char szUrl[_2K];
814 RTStrPrintf(szUrl, sizeof(szUrl), "%s/%s/%s/%s", pszServer, pszFilename, pszCacheSubDir, pszFilename);
815
816 /** @todo Use some temporary file name and rename it after the operation
817 * since not all systems support read-deny file sharing
818 * settings. */
819 rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
820 rc = RTHttpGetFile(hHttp, szUrl, pszPath);
821 if (RT_FAILURE(rc))
822 {
823 RTFileDelete(pszPath);
824 rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, szUrl);
825 }
826 if (rc == VERR_HTTP_NOT_FOUND)
827 {
828 /* Try the compressed version of the file. */
829 pszPath[strlen(pszPath) - 1] = '_';
830 szUrl[strlen(szUrl) - 1] = '_';
831 rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
832 rc = RTHttpGetFile(hHttp, szUrl, pszPath);
833 if (RT_SUCCESS(rc))
834 rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
835 else
836 {
837 rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, pszPath);
838 RTFileDelete(pszPath);
839 }
840 }
841 }
842
843 RTHttpDestroy(hHttp);
844
845 if (RT_SUCCESS(rc))
846 {
847 /*
848 * Succeeded in downloading it. Add UUID mapping?
849 */
850 if (pszUuidMappingSubDir)
851 {
852 /** @todo UUID mapping when downloading. */
853 }
854
855 /*
856 * Give the file a try.
857 */
858 Assert(RTFileExists(pszPath));
859 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
860 rc = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
861 if (rc == VINF_CALLBACK_RETURN)
862 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
863 else if (rc == VERR_CALLBACK_RETURN)
864 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
865 else
866 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc, pszPath);
867 }
868
869 return rc;
870
871#else /* !IPRT_WITH_HTTP */
872 RT_NOREF_PV(pThis); RT_NOREF_PV(pszServer); RT_NOREF_PV(pszPath); RT_NOREF_PV(pszCacheSubDir);
873 RT_NOREF_PV(pszUuidMappingSubDir); RT_NOREF_PV(pSplitFn); RT_NOREF_PV(pszCacheSuffix); RT_NOREF_PV(fFlags);
874 RT_NOREF_PV(pfnCallback); RT_NOREF_PV(pvUser1); RT_NOREF_PV(pvUser2);
875 return VWRN_NOT_FOUND;
876#endif /* !IPRT_WITH_HTTP */
877}
878
879
880static int rtDbgCfgCopyFileToCache(PRTDBGCFGINT pThis, char const *pszSrc, const char *pchCache, size_t cchCache,
881 const char *pszCacheSubDir, const char *pszUuidMappingSubDir, PRTPATHSPLIT pSplitFn)
882{
883 RT_NOREF_PV(pThis); RT_NOREF_PV(pszSrc); RT_NOREF_PV(pchCache); RT_NOREF_PV(cchCache);
884 RT_NOREF_PV(pszUuidMappingSubDir); RT_NOREF_PV(pSplitFn);
885
886 if (!pszCacheSubDir || !*pszCacheSubDir)
887 return VINF_SUCCESS;
888
889 /** @todo copy to cache */
890 return VINF_SUCCESS;
891}
892
893
894static int rtDbgCfgTryOpenCache(PRTDBGCFGINT pThis, char *pszPath, size_t cchCachePath,
895 const char *pszCacheSubDir, const char *pszUuidMappingSubDir,
896 PCRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags,
897 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
898{
899 Assert(pszPath[cchCachePath] == '\0');
900
901 /*
902 * If the cache doesn't exist, fail right away.
903 */
904 if (!pszCacheSubDir || !*pszCacheSubDir)
905 return VWRN_NOT_FOUND;
906 if (!RTDirExists(pszPath))
907 {
908 rtDbgCfgLog2(pThis, "Cache does not exist: '%s'\n", pszPath);
909 return VWRN_NOT_FOUND;
910 }
911
912 /*
913 * If we got a UUID mapping option, try it first as we can hopefully
914 * dispense with case folding.
915 */
916 if (pszUuidMappingSubDir)
917 {
918 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszUuidMappingSubDir);
919 if ( RT_SUCCESS(rc)
920 && RTFileExists(pszPath))
921 {
922 /* Try resolve the path before presenting it to the client, a
923 12 digit filename is of little worth. */
924 char szBackup[RTPATH_MAX];
925 strcpy(szBackup, pszPath);
926 rc = RTPathAbs(szBackup, pszPath, RTPATH_MAX);
927 if (RT_FAILURE(rc))
928 strcpy(pszPath, szBackup);
929
930 /* Do the callback thing. */
931 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
932 int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
933 if (rc2 == VINF_CALLBACK_RETURN)
934 rtDbgCfgLog1(pThis, "Found '%s' via uuid mapping.\n", pszPath);
935 else if (rc2 == VERR_CALLBACK_RETURN)
936 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
937 else
938 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
939 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
940 return rc2;
941
942 /* Failed, restore the cache path. */
943 memcpy(pszPath, szBackup, cchCachePath);
944 }
945 pszPath[cchCachePath] = '\0';
946 }
947
948 /*
949 * Carefully construct the cache path with case insensitivity in mind.
950 */
951 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
952 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
953 const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
954
955 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszFilename, fCaseInsensitive))
956 return VWRN_NOT_FOUND;
957
958 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszCacheSubDir, fCaseInsensitive))
959 return VWRN_NOT_FOUND;
960
961 bool fProbablyCompressed = false;
962 if (!rtDbgCfgIsFileAndFixCase(pszPath, pszFilename, pszCacheSuffix, fCaseInsensitive,
963 RT_BOOL(fFlags & RTDBGCFG_O_MAYBE_COMPRESSED_MS), &fProbablyCompressed))
964 return VWRN_NOT_FOUND;
965 if (fProbablyCompressed)
966 {
967 int rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
968 if (RT_FAILURE(rc))
969 return VWRN_NOT_FOUND;
970 }
971
972 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
973 int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
974 if (rc2 == VINF_CALLBACK_RETURN)
975 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
976 else if (rc2 == VERR_CALLBACK_RETURN)
977 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
978 else
979 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
980 return rc2;
981}
982
983
984static int rtDbgCfgTryOpenList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, const char *pszCacheSubDir,
985 const char *pszUuidMappingSubDir, uint32_t fFlags, char *pszPath,
986 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
987{
988 int rcRet = VWRN_NOT_FOUND;
989 int rc2 = VINF_SUCCESS;
990
991 const char *pchCache = NULL;
992 size_t cchCache = 0;
993 int rcCache = VWRN_NOT_FOUND;
994
995 PRTDBGCFGSTR pCur;
996 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
997 {
998 size_t cchDir = pCur->cch;
999 const char *pszDir = pCur->sz;
1000 rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
1001
1002 /* This is very simplistic, but we have a unreasonably large path
1003 buffer, so it'll work just fine and simplify things greatly below. */
1004 if (cchDir >= RTPATH_MAX - 8U)
1005 {
1006 if (RT_SUCCESS_NP(rcRet))
1007 rcRet = VERR_FILENAME_TOO_LONG;
1008 continue;
1009 }
1010
1011 /*
1012 * Process the path according to it's type.
1013 */
1014 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*")))
1015 {
1016 /*
1017 * Symbol server.
1018 */
1019 pszDir += sizeof("srv*") - 1;
1020 cchDir -= sizeof("srv*") - 1;
1021 bool fSearchCache = false;
1022 const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
1023 if (!pszServer)
1024 pszServer = pszDir;
1025 else if (pszServer == pszDir)
1026 continue;
1027 else
1028 {
1029 fSearchCache = true;
1030 pchCache = pszDir;
1031 cchCache = pszServer - pszDir;
1032 pszServer++;
1033 }
1034
1035 /* We don't have any default cache directory, so skip if the cache is missing. */
1036 if (cchCache == 0)
1037 continue;
1038
1039 /* Search the cache first (if we haven't already done so). */
1040 if (fSearchCache)
1041 {
1042 memcpy(pszPath, pchCache, cchCache);
1043 pszPath[cchCache] = '\0';
1044 RTPathChangeToUnixSlashes(pszPath, false);
1045
1046 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1047 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1048 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1049 return rc2;
1050 }
1051
1052 /* Try downloading the file. */
1053 if (rcCache == VWRN_NOT_FOUND)
1054 {
1055 memcpy(pszPath, pchCache, cchCache);
1056 pszPath[cchCache] = '\0';
1057 RTPathChangeToUnixSlashes(pszPath, false);
1058
1059 rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir,
1060 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1061 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1062 return rc2;
1063 }
1064 }
1065 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*")))
1066 {
1067 /*
1068 * Cache directory.
1069 */
1070 pszDir += sizeof("cache*") - 1;
1071 cchDir -= sizeof("cache*") - 1;
1072 if (!cchDir)
1073 continue;
1074 pchCache = pszDir;
1075 cchCache = cchDir;
1076
1077 memcpy(pszPath, pchCache, cchCache);
1078 pszPath[cchCache] = '\0';
1079 RTPathChangeToUnixSlashes(pszPath, false);
1080
1081 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1082 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1083 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1084 return rc2;
1085 }
1086 else
1087 {
1088 /*
1089 * Normal directory. Check for our own 'rec*' and 'norec*' prefix
1090 * flags governing recursive searching.
1091 */
1092 uint32_t fFlagsDir = fFlags;
1093 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*")))
1094 {
1095 pszDir += sizeof("rec*") - 1;
1096 cchDir -= sizeof("rec*") - 1;
1097 fFlagsDir |= RTDBGCFG_O_RECURSIVE;
1098 }
1099 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*")))
1100 {
1101 pszDir += sizeof("norec*") - 1;
1102 cchDir -= sizeof("norec*") - 1;
1103 fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
1104 }
1105
1106 /* Copy the path into the buffer and do the searching. */
1107 memcpy(pszPath, pszDir, cchDir);
1108 pszPath[cchDir] = '\0';
1109 RTPathChangeToUnixSlashes(pszPath, false);
1110
1111 rc2 = rtDbgCfgTryOpenDir(pThis, pszPath, pSplitFn, fFlagsDir, pfnCallback, pvUser1, pvUser2);
1112 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1113 {
1114 if ( rc2 == VINF_CALLBACK_RETURN
1115 && cchCache > 0)
1116 rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache,
1117 pszCacheSubDir, pszUuidMappingSubDir, pSplitFn);
1118 return rc2;
1119 }
1120 }
1121
1122 /* Propagate errors. */
1123 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1124 rcRet = rc2;
1125 }
1126
1127 return rcRet;
1128}
1129
1130
1131/**
1132 * Common worker routine for Image and debug info opening.
1133 *
1134 * This will not search using for suffixes.
1135 *
1136 * @returns IPRT status code.
1137 * @param hDbgCfg The debugging configuration handle.
1138 * NIL_RTDBGCFG is accepted, but the result is
1139 * that no paths will be searched beyond the
1140 * given and the current directory.
1141 * @param pszFilename The filename to search for. This may or may
1142 * not include a full or partial path.
1143 * @param pszCacheSubDir The cache subdirectory to look in.
1144 * @param pszUuidMappingSubDir UUID mapping subdirectory to check, NULL if
1145 * no mapping wanted.
1146 * @param fFlags Flags and hints.
1147 * @param pfnCallback The open callback routine.
1148 * @param pvUser1 User parameter 1.
1149 * @param pvUser2 User parameter 2.
1150 */
1151static int rtDbgCfgOpenWithSubDir(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir,
1152 const char *pszUuidMappingSubDir, uint32_t fFlags,
1153 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1154{
1155 int rcRet = VINF_SUCCESS;
1156 int rc2;
1157
1158 /*
1159 * Do a little validating first.
1160 */
1161 PRTDBGCFGINT pThis = hDbgCfg;
1162 if (pThis != NIL_RTDBGCFG)
1163 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1164 else
1165 pThis = NULL;
1166 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
1167 AssertPtrReturn(pszCacheSubDir, VERR_INVALID_POINTER);
1168 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
1169
1170 /*
1171 * Do some guessing as to the way we should parse the filename and whether
1172 * it's case exact or not.
1173 */
1174 bool fDosPath = strchr(pszFilename, ':') != NULL
1175 || strchr(pszFilename, '\\') != NULL
1176 || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
1177 || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE);
1178 if (fDosPath)
1179 fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
1180
1181 rtDbgCfgLog2(pThis, "Looking for '%s' w/ cache subdir '%s' and %#x flags...\n", pszFilename, pszCacheSubDir, fFlags);
1182
1183 PRTPATHSPLIT pSplitFn;
1184 rc2 = RTPathSplitA(pszFilename, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1185 if (RT_FAILURE(rc2))
1186 return rc2;
1187 AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY);
1188
1189 /*
1190 * Try the stored file name first if it has a kind of absolute path.
1191 */
1192 char szPath[RTPATH_MAX];
1193 if (RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps))
1194 {
1195 rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
1196 if (RT_SUCCESS(rc2) && RTFileExists(szPath))
1197 {
1198 RTPathChangeToUnixSlashes(szPath, false);
1199 rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath);
1200 rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2);
1201 if (rc2 == VINF_CALLBACK_RETURN)
1202 rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath);
1203 else if (rc2 == VERR_CALLBACK_RETURN)
1204 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath);
1205 else
1206 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath);
1207 }
1208 }
1209 if ( rc2 != VINF_CALLBACK_RETURN
1210 && rc2 != VERR_CALLBACK_RETURN)
1211 {
1212 /*
1213 * Try the current directory (will take cover relative paths
1214 * skipped above).
1215 */
1216 rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
1217 if (RT_FAILURE(rc2))
1218 strcpy(szPath, ".");
1219 RTPathChangeToUnixSlashes(szPath, false);
1220
1221 rc2 = rtDbgCfgTryOpenDir(pThis, szPath, pSplitFn, fFlags, pfnCallback, pvUser1, pvUser2);
1222 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1223 rcRet = rc2;
1224
1225 if ( rc2 != VINF_CALLBACK_RETURN
1226 && rc2 != VERR_CALLBACK_RETURN
1227 && pThis)
1228 {
1229 rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
1230 if (RT_SUCCESS(rc2))
1231 {
1232 /*
1233 * Run the applicable lists.
1234 */
1235 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->PathList, pSplitFn, pszCacheSubDir,
1236 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1237 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1238 rcRet = rc2;
1239
1240#ifdef RT_OS_WINDOWS
1241 if ( rc2 != VINF_CALLBACK_RETURN
1242 && rc2 != VERR_CALLBACK_RETURN
1243 && (fFlags & RTDBGCFG_O_EXECUTABLE_IMAGE)
1244 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
1245 {
1246 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtExecutablePathList, pSplitFn, pszCacheSubDir,
1247 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1248 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1249 rcRet = rc2;
1250 }
1251
1252 if ( rc2 != VINF_CALLBACK_RETURN
1253 && rc2 != VERR_CALLBACK_RETURN
1254 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
1255 {
1256 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtSymbolPathList, pSplitFn, pszCacheSubDir,
1257 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1258 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1259 rcRet = rc2;
1260 }
1261#endif
1262 RTCritSectRwLeaveShared(&pThis->CritSect);
1263 }
1264 else if (RT_SUCCESS(rcRet))
1265 rcRet = rc2;
1266 }
1267 }
1268
1269 RTPathSplitFree(pSplitFn);
1270 if ( rc2 == VINF_CALLBACK_RETURN
1271 || rc2 == VERR_CALLBACK_RETURN)
1272 rcRet = rc2;
1273 else if (RT_SUCCESS(rcRet))
1274 rcRet = VERR_NOT_FOUND;
1275 return rcRet;
1276}
1277
1278
1279RTDECL(int) RTDbgCfgOpenPeImage(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
1280 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1281{
1282 char szSubDir[32];
1283 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
1284 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1285 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1286 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXECUTABLE_IMAGE,
1287 pfnCallback, pvUser1, pvUser2);
1288}
1289
1290
1291RTDECL(int) RTDbgCfgOpenPdb70(RTDBGCFG hDbgCfg, const char *pszFilename, PCRTUUID pUuid, uint32_t uAge,
1292 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1293{
1294 char szSubDir[64];
1295 if (!pUuid)
1296 szSubDir[0] = '\0';
1297 else
1298 {
1299 /* Stringify the UUID and remove the dashes. */
1300 int rc2 = RTUuidToStr(pUuid, szSubDir, sizeof(szSubDir));
1301 AssertRCReturn(rc2, rc2);
1302
1303 char *pszSrc = szSubDir;
1304 char *pszDst = szSubDir;
1305 char ch;
1306 while ((ch = *pszSrc++))
1307 if (ch != '-')
1308 *pszDst++ = RT_C_TO_UPPER(ch);
1309
1310 RTStrPrintf(pszDst, &szSubDir[sizeof(szSubDir)] - pszDst, "%X", uAge);
1311 }
1312
1313 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1314 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1315 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1316 pfnCallback, pvUser1, pvUser2);
1317}
1318
1319
1320RTDECL(int) RTDbgCfgOpenPdb20(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, uint32_t uAge,
1321 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1322{
1323 RT_NOREF_PV(cbImage);
1324 /** @todo test this! */
1325 char szSubDir[32];
1326 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, uAge);
1327 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1328 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1329 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1330 pfnCallback, pvUser1, pvUser2);
1331}
1332
1333
1334RTDECL(int) RTDbgCfgOpenDbg(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
1335 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1336{
1337 char szSubDir[32];
1338 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
1339 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1340 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1341 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1342 pfnCallback, pvUser1, pvUser2);
1343}
1344
1345
1346RTDECL(int) RTDbgCfgOpenDwo(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t uCrc32,
1347 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1348{
1349 char szSubDir[32];
1350 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08x", uCrc32);
1351 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1352 RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE,
1353 pfnCallback, pvUser1, pvUser2);
1354}
1355
1356
1357
1358/*
1359 *
1360 * D a r w i n . d S Y M b u n d l e s
1361 * D a r w i n . d S Y M b u n d l e s
1362 * D a r w i n . d S Y M b u n d l e s
1363 *
1364 */
1365
1366/**
1367 * Very similar to rtDbgCfgTryOpenDir.
1368 */
1369static int rtDbgCfgTryOpenDsymBundleInDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn,
1370 const char * const *papszSuffixes, uint32_t fFlags,
1371 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1372{
1373 int rcRet = VWRN_NOT_FOUND;
1374 int rc2;
1375
1376 /* If the directory doesn't exist, just quit immediately.
1377 Note! Our case insensitivity doesn't extend to the search dirs themselfs,
1378 only to the bits under neath them. */
1379 if (!RTDirExists(pszPath))
1380 {
1381 rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
1382 return rcRet;
1383 }
1384
1385 /* Figure out whether we have to do a case sensitive search or not.
1386 Note! As a simplification, we don't ask for case settings in each
1387 directory under the user specified path, we assume the file
1388 systems that mounted there have compatible settings. Faster
1389 that way. */
1390 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
1391 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
1392
1393 size_t const cchPath = strlen(pszPath);
1394
1395 /*
1396 * Look for the file with less and less of the original path given.
1397 * Also try out typical bundle extension variations.
1398 */
1399 const char *pszName = pSplitFn->apszComps[pSplitFn->cComps - 1];
1400 for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
1401 {
1402 pszPath[cchPath] = '\0';
1403
1404 rc2 = VINF_SUCCESS;
1405 for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
1406 if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
1407 rc2 = VERR_FILE_NOT_FOUND;
1408 if (RT_SUCCESS(rc2))
1409 {
1410 for (uint32_t iSuffix = 0; papszSuffixes[iSuffix]; iSuffix++)
1411 {
1412 if ( !rtDbgCfgIsDirAndFixCase2(pszPath, pszName, papszSuffixes[iSuffix], fCaseInsensitive)
1413 && !rtDbgCfgIsDirAndFixCase(pszPath, "Contents", fCaseInsensitive)
1414 && !rtDbgCfgIsDirAndFixCase(pszPath, "Resources", fCaseInsensitive)
1415 && !rtDbgCfgIsDirAndFixCase(pszPath, "DWARF", fCaseInsensitive))
1416 {
1417 if (rtDbgCfgIsFileAndFixCase(pszPath, pszName, NULL /*pszSuffix*/, fCaseInsensitive, false, NULL))
1418 {
1419 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
1420 rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
1421 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1422 {
1423 if (rc2 == VINF_CALLBACK_RETURN)
1424 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
1425 else
1426 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
1427 return rc2;
1428 }
1429 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
1430 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1431 rcRet = rc2;
1432 }
1433 }
1434 }
1435 }
1436 rc2 = VERR_FILE_NOT_FOUND;
1437 }
1438
1439 /*
1440 * Do a recursive search if requested.
1441 */
1442 if ( (fFlags & RTDBGCFG_O_RECURSIVE)
1443 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
1444 {
1445 /** @todo Recursive searching will be done later. */
1446 }
1447
1448 return rcRet;
1449}
1450
1451
1452/**
1453 * Very similar to rtDbgCfgTryOpenList.
1454 */
1455static int rtDbgCfgTryOpenBundleInList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn,
1456 const char * const *papszSuffixes, const char *pszCacheSubDir,
1457 const char *pszCacheSuffix, const char *pszUuidMappingSubDir,
1458 uint32_t fFlags, char *pszPath,
1459 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1460{
1461 int rcRet = VWRN_NOT_FOUND;
1462 int rc2;
1463
1464 const char *pchCache = NULL;
1465 size_t cchCache = 0;
1466 int rcCache = VWRN_NOT_FOUND;
1467
1468 PRTDBGCFGSTR pCur;
1469 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
1470 {
1471 size_t cchDir = pCur->cch;
1472 const char *pszDir = pCur->sz;
1473 rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
1474
1475 /* This is very simplistic, but we have a unreasonably large path
1476 buffer, so it'll work just fine and simplify things greatly below. */
1477 if (cchDir >= RTPATH_MAX - 8U)
1478 {
1479 if (RT_SUCCESS_NP(rcRet))
1480 rcRet = VERR_FILENAME_TOO_LONG;
1481 continue;
1482 }
1483
1484 /*
1485 * Process the path according to it's type.
1486 */
1487 rc2 = VINF_SUCCESS;
1488 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*")))
1489 {
1490 /*
1491 * Symbol server.
1492 */
1493 pszDir += sizeof("srv*") - 1;
1494 cchDir -= sizeof("srv*") - 1;
1495 bool fSearchCache = false;
1496 const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
1497 if (!pszServer)
1498 pszServer = pszDir;
1499 else if (pszServer == pszDir)
1500 continue;
1501 else
1502 {
1503 fSearchCache = true;
1504 pchCache = pszDir;
1505 cchCache = pszServer - pszDir;
1506 pszServer++;
1507 }
1508
1509 /* We don't have any default cache directory, so skip if the cache is missing. */
1510 if (cchCache == 0)
1511 continue;
1512
1513 /* Search the cache first (if we haven't already done so). */
1514 if (fSearchCache)
1515 {
1516 memcpy(pszPath, pchCache, cchCache);
1517 pszPath[cchCache] = '\0';
1518 RTPathChangeToUnixSlashes(pszPath, false);
1519
1520 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1521 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1522 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1523 return rc2;
1524 }
1525
1526 /* Try downloading the file. */
1527 if (rcCache == VWRN_NOT_FOUND)
1528 {
1529 memcpy(pszPath, pchCache, cchCache);
1530 pszPath[cchCache] = '\0';
1531 RTPathChangeToUnixSlashes(pszPath, false);
1532
1533 rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir,
1534 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1535 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1536 return rc2;
1537 }
1538 }
1539 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*")))
1540 {
1541 /*
1542 * Cache directory.
1543 */
1544 pszDir += sizeof("cache*") - 1;
1545 cchDir -= sizeof("cache*") - 1;
1546 if (!cchDir)
1547 continue;
1548 pchCache = pszDir;
1549 cchCache = cchDir;
1550
1551 memcpy(pszPath, pchCache, cchCache);
1552 pszPath[cchCache] = '\0';
1553 RTPathChangeToUnixSlashes(pszPath, false);
1554
1555 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1556 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1557 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1558 return rc2;
1559 }
1560 else
1561 {
1562 /*
1563 * Normal directory. Check for our own 'rec*' and 'norec*' prefix
1564 * flags governing recursive searching.
1565 */
1566 uint32_t fFlagsDir = fFlags;
1567 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*")))
1568 {
1569 pszDir += sizeof("rec*") - 1;
1570 cchDir -= sizeof("rec*") - 1;
1571 fFlagsDir |= RTDBGCFG_O_RECURSIVE;
1572 }
1573 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*")))
1574 {
1575 pszDir += sizeof("norec*") - 1;
1576 cchDir -= sizeof("norec*") - 1;
1577 fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
1578 }
1579
1580 /* Copy the path into the buffer and do the searching. */
1581 memcpy(pszPath, pszDir, cchDir);
1582 pszPath[cchDir] = '\0';
1583 RTPathChangeToUnixSlashes(pszPath, false);
1584
1585 rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, pszPath, pSplitFn, papszSuffixes, fFlagsDir,
1586 pfnCallback, pvUser1, pvUser2);
1587 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1588 {
1589 if ( rc2 == VINF_CALLBACK_RETURN
1590 && cchCache > 0)
1591 rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache,
1592 pszCacheSubDir, pszUuidMappingSubDir, pSplitFn);
1593 return rc2;
1594 }
1595 }
1596
1597 /* Propagate errors. */
1598 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1599 rcRet = rc2;
1600 }
1601
1602 return rcRet;
1603}
1604
1605
1606/**
1607 * Creating a UUID mapping subdirectory path for use in caches.
1608 *
1609 * @returns IPRT status code.
1610 * @param pszSubDir The output buffer.
1611 * @param cbSubDir The size of the output buffer. (Top dir length +
1612 * slash + UUID string len + extra dash.)
1613 * @param pszTopDir The top level cache directory name. No slashes
1614 * or other directory separators, please.
1615 * @param pUuid The UUID.
1616 */
1617static int rtDbgCfgConstructUuidMappingSubDir(char *pszSubDir, size_t cbSubDir, const char *pszTopDir, PCRTUUID pUuid)
1618{
1619 Assert(!strpbrk(pszTopDir, ":/\\"));
1620
1621 size_t cchTopDir = strlen(pszTopDir);
1622 if (cchTopDir + 1 + 1 + RTUUID_STR_LENGTH + 1 > cbSubDir)
1623 return VERR_BUFFER_OVERFLOW;
1624 memcpy(pszSubDir, pszTopDir, cchTopDir);
1625
1626 pszSubDir += cchTopDir;
1627 *pszSubDir++ = RTPATH_SLASH;
1628 cbSubDir -= cchTopDir + 1;
1629
1630 /* ed5a8336-35c2-4892-9122-21d5572924a3 -> ED5A/8336/35C2/4892/9122/21D5572924A3 */
1631 int rc = RTUuidToStr(pUuid, pszSubDir + 1, cbSubDir - 1); AssertRCReturn(rc, rc);
1632 RTStrToUpper(pszSubDir + 1);
1633 memmove(pszSubDir, pszSubDir + 1, 4);
1634 pszSubDir += 4;
1635 *pszSubDir = RTPATH_SLASH;
1636 pszSubDir += 5;
1637 *pszSubDir = RTPATH_SLASH;
1638 pszSubDir += 5;
1639 *pszSubDir = RTPATH_SLASH;
1640 pszSubDir += 5;
1641 *pszSubDir = RTPATH_SLASH;
1642 pszSubDir += 5;
1643 *pszSubDir = RTPATH_SLASH;
1644
1645 return VINF_SUCCESS;
1646}
1647
1648
1649static int rtDbgCfgOpenBundleFile(RTDBGCFG hDbgCfg, const char *pszImage, const char * const *papszSuffixes,
1650 const char *pszBundleSubDir, PCRTUUID pUuid, const char *pszUuidMapDirName,
1651 const char *pszCacheSuffix, bool fOpenImage,
1652 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1653{
1654 /*
1655 * Bundles are directories, means we can forget about sharing code much
1656 * with the other RTDbgCfgOpenXXX methods. Thus we're duplicating a lot of
1657 * code from rtDbgCfgOpenWithSubDir with .dSYM/.kext/.dylib/.app/.* related
1658 * adjustments, so, a bug found here or there probably means the other
1659 * version needs updating.
1660 */
1661 int rcRet = VINF_SUCCESS;
1662 int rc2;
1663
1664 /*
1665 * Do a little validating first.
1666 */
1667 PRTDBGCFGINT pThis = hDbgCfg;
1668 if (pThis != NIL_RTDBGCFG)
1669 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1670 else
1671 pThis = NULL;
1672 AssertPtrReturn(pszImage, VERR_INVALID_POINTER);
1673 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
1674
1675 /*
1676 * Set up rtDbgCfgOpenWithSubDir and uuid map parameters.
1677 */
1678 uint32_t fFlags = RTDBGCFG_O_EXT_DEBUG_FILE | RT_OPSYS_DARWIN;
1679 const char *pszCacheSubDir = NULL;
1680 char szCacheSubDir[RTUUID_STR_LENGTH];
1681 const char *pszUuidMappingSubDir = NULL;
1682 char szUuidMappingSubDir[RTUUID_STR_LENGTH + 16];
1683 if (pUuid)
1684 {
1685 /* Since Mac debuggers uses UUID mappings, we just the slashing default
1686 UUID string representation instead of stripping dashes like for PDB. */
1687 RTUuidToStr(pUuid, szCacheSubDir, sizeof(szCacheSubDir));
1688 pszCacheSubDir = szCacheSubDir;
1689
1690 rc2 = rtDbgCfgConstructUuidMappingSubDir(szUuidMappingSubDir, sizeof(szUuidMappingSubDir), pszUuidMapDirName, pUuid);
1691 AssertRCReturn(rc2, rc2);
1692 pszUuidMappingSubDir = szUuidMappingSubDir;
1693 }
1694
1695 /*
1696 * Do some guessing as to the way we should parse the filename and whether
1697 * it's case exact or not.
1698 */
1699 bool fDosPath = strchr(pszImage, ':') != NULL
1700 || strchr(pszImage, '\\') != NULL
1701 || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
1702 || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE);
1703 if (fDosPath)
1704 fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
1705
1706 rtDbgCfgLog2(pThis, "Looking for '%s' with %#x flags...\n", pszImage, fFlags);
1707
1708 PRTPATHSPLIT pSplitFn;
1709 rc2 = RTPathSplitA(pszImage, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1710 if (RT_FAILURE(rc2))
1711 return rc2;
1712 AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY);
1713
1714 /*
1715 * Try the image directory first.
1716 */
1717 char szPath[RTPATH_MAX];
1718 if (pSplitFn->cComps > 0)
1719 {
1720 rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
1721 if (fOpenImage && RT_SUCCESS(rc2))
1722 {
1723 rc2 = RTStrCat(szPath, sizeof(szPath), papszSuffixes[0]);
1724 if (RT_SUCCESS(rc2))
1725 rc2 = RTStrCat(szPath, sizeof(szPath), pszBundleSubDir);
1726 if (RT_SUCCESS(rc2))
1727 rc2 = RTPathAppend(szPath, sizeof(szPath), pSplitFn->apszComps[pSplitFn->cComps - 1]);
1728 }
1729 if (RT_SUCCESS(rc2) && RTPathExists(szPath))
1730 {
1731 RTPathChangeToUnixSlashes(szPath, false);
1732 rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath);
1733 rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2);
1734 if (rc2 == VINF_CALLBACK_RETURN)
1735 rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath);
1736 else if (rc2 == VERR_CALLBACK_RETURN)
1737 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath);
1738 else
1739 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath);
1740 }
1741 }
1742 if ( rc2 != VINF_CALLBACK_RETURN
1743 && rc2 != VERR_CALLBACK_RETURN)
1744 {
1745 /*
1746 * Try the current directory (will take cover relative paths
1747 * skipped above).
1748 */
1749 rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
1750 if (RT_FAILURE(rc2))
1751 strcpy(szPath, ".");
1752 RTPathChangeToUnixSlashes(szPath, false);
1753
1754 rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, szPath, pSplitFn, g_apszDSymBundleSuffixes,
1755 fFlags, pfnCallback, pvUser1, pvUser2);
1756 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1757 rcRet = rc2;
1758
1759 if ( rc2 != VINF_CALLBACK_RETURN
1760 && rc2 != VERR_CALLBACK_RETURN
1761 && pThis)
1762 {
1763 rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
1764 if (RT_SUCCESS(rc2))
1765 {
1766 /*
1767 * Run the applicable lists.
1768 */
1769 rc2 = rtDbgCfgTryOpenBundleInList(pThis, &pThis->PathList, pSplitFn, g_apszDSymBundleSuffixes,
1770 pszCacheSubDir, pszCacheSuffix,
1771 pszUuidMappingSubDir, fFlags, szPath,
1772 pfnCallback, pvUser1, pvUser2);
1773 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1774 rcRet = rc2;
1775
1776 RTCritSectRwLeaveShared(&pThis->CritSect);
1777 }
1778 else if (RT_SUCCESS(rcRet))
1779 rcRet = rc2;
1780 }
1781 }
1782
1783 RTPathSplitFree(pSplitFn);
1784 if ( rc2 == VINF_CALLBACK_RETURN
1785 || rc2 == VERR_CALLBACK_RETURN)
1786 rcRet = rc2;
1787 else if (RT_SUCCESS(rcRet))
1788 rcRet = VERR_NOT_FOUND;
1789 return rcRet;
1790}
1791
1792
1793RTDECL(int) RTDbgCfgOpenDsymBundle(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid,
1794 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1795{
1796 return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszDSymBundleSuffixes,
1797 "Contents" RTPATH_SLASH_STR "Resources" RTPATH_SLASH_STR "DWARF",
1798 pUuid, RTDBG_CACHE_UUID_MAP_DIR_DSYMS, RTDBG_CACHE_DSYM_FILE_SUFFIX, false /* fOpenImage */,
1799 pfnCallback, pvUser1, pvUser2);
1800}
1801
1802
1803RTDECL(int) RTDbgCfgOpenMachOImage(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid,
1804 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1805{
1806 return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszBundleSuffixes,
1807 "Contents" RTPATH_SLASH_STR "MacOS",
1808 pUuid, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, NULL /*pszCacheSuffix*/, true /* fOpenImage */,
1809 pfnCallback, pvUser1, pvUser2);
1810}
1811
1812
1813
1814RTDECL(int) RTDbgCfgSetLogCallback(RTDBGCFG hDbgCfg, PFNRTDBGCFGLOG pfnCallback, void *pvUser)
1815{
1816 PRTDBGCFGINT pThis = hDbgCfg;
1817 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1818 AssertPtrNullReturn(pfnCallback, VERR_INVALID_POINTER);
1819
1820 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
1821 if (RT_SUCCESS(rc))
1822 {
1823 if ( pThis->pfnLogCallback == NULL
1824 || pfnCallback == NULL
1825 || pfnCallback == pThis->pfnLogCallback)
1826 {
1827 pThis->pfnLogCallback = NULL;
1828 pThis->pvLogUser = NULL;
1829 ASMCompilerBarrier(); /* paranoia */
1830 pThis->pvLogUser = pvUser;
1831 pThis->pfnLogCallback = pfnCallback;
1832 rc = VINF_SUCCESS;
1833 }
1834 else
1835 rc = VERR_ACCESS_DENIED;
1836 RTCritSectRwLeaveExcl(&pThis->CritSect);
1837 }
1838
1839 return rc;
1840}
1841
1842
1843/**
1844 * Frees a string list.
1845 *
1846 * @param pList The list to free.
1847 */
1848static void rtDbgCfgFreeStrList(PRTLISTANCHOR pList)
1849{
1850 PRTDBGCFGSTR pCur;
1851 PRTDBGCFGSTR pNext;
1852 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
1853 {
1854 RTListNodeRemove(&pCur->ListEntry);
1855 RTMemFree(pCur);
1856 }
1857}
1858
1859
1860/**
1861 * Make changes to a string list, given a semicolon separated input string.
1862 *
1863 * @returns VINF_SUCCESS, VERR_FILENAME_TOO_LONG, VERR_NO_MEMORY
1864 * @param pThis The config instance.
1865 * @param enmOp The change operation.
1866 * @param pszValue The input strings separated by semicolon.
1867 * @param fPaths Indicates that this is a path list and that we
1868 * should look for srv and cache prefixes.
1869 * @param pList The string list anchor.
1870 */
1871static int rtDbgCfgChangeStringList(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, bool fPaths,
1872 PRTLISTANCHOR pList)
1873{
1874 RT_NOREF_PV(pThis); RT_NOREF_PV(fPaths);
1875
1876 if (enmOp == RTDBGCFGOP_SET)
1877 rtDbgCfgFreeStrList(pList);
1878
1879 while (*pszValue)
1880 {
1881 /* Skip separators. */
1882 while (*pszValue == ';')
1883 pszValue++;
1884 if (!*pszValue)
1885 break;
1886
1887 /* Find the end of this path. */
1888 const char *pchPath = pszValue++;
1889 char ch;
1890 while ((ch = *pszValue) && ch != ';')
1891 pszValue++;
1892 size_t cchPath = pszValue - pchPath;
1893 if (cchPath >= UINT16_MAX)
1894 return VERR_FILENAME_TOO_LONG;
1895
1896 if (enmOp == RTDBGCFGOP_REMOVE)
1897 {
1898 /*
1899 * Remove all occurences.
1900 */
1901 PRTDBGCFGSTR pCur;
1902 PRTDBGCFGSTR pNext;
1903 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
1904 {
1905 if ( pCur->cch == cchPath
1906 && !memcmp(pCur->sz, pchPath, cchPath))
1907 {
1908 RTListNodeRemove(&pCur->ListEntry);
1909 RTMemFree(pCur);
1910 }
1911 }
1912 }
1913 else
1914 {
1915 /*
1916 * We're adding a new one.
1917 */
1918 PRTDBGCFGSTR pNew = (PRTDBGCFGSTR)RTMemAlloc(RT_OFFSETOF(RTDBGCFGSTR, sz[cchPath + 1]));
1919 if (!pNew)
1920 return VERR_NO_MEMORY;
1921 pNew->cch = (uint16_t)cchPath;
1922 pNew->fFlags = 0;
1923 memcpy(pNew->sz, pchPath, cchPath);
1924 pNew->sz[cchPath] = '\0';
1925
1926 if (enmOp == RTDBGCFGOP_PREPEND)
1927 RTListPrepend(pList, &pNew->ListEntry);
1928 else
1929 RTListAppend(pList, &pNew->ListEntry);
1930 }
1931 }
1932
1933 return VINF_SUCCESS;
1934}
1935
1936
1937/**
1938 * Make changes to a 64-bit value
1939 *
1940 * @returns VINF_SUCCESS, VERR_DBG_CFG_INVALID_VALUE.
1941 * @param pThis The config instance.
1942 * @param enmOp The change operation.
1943 * @param pszValue The input value.
1944 * @param paMnemonics The mnemonics map for this value.
1945 * @param puValue The value to change.
1946 */
1947static int rtDbgCfgChangeStringU64(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue,
1948 PCRTDBGCFGU64MNEMONIC paMnemonics, uint64_t *puValue)
1949{
1950 RT_NOREF_PV(pThis);
1951
1952 uint64_t uNew = enmOp == RTDBGCFGOP_SET ? 0 : *puValue;
1953 char ch;
1954 while ((ch = *pszValue))
1955 {
1956 /* skip whitespace and separators */
1957 while (RT_C_IS_SPACE(ch) || RT_C_IS_CNTRL(ch) || ch == ';' || ch == ':')
1958 ch = *++pszValue;
1959 if (!ch)
1960 break;
1961
1962 if (RT_C_IS_DIGIT(ch))
1963 {
1964 uint64_t uTmp;
1965 int rc = RTStrToUInt64Ex(pszValue, (char **)&pszValue, 0, &uTmp);
1966 if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG)
1967 return VERR_DBG_CFG_INVALID_VALUE;
1968
1969 if (enmOp != RTDBGCFGOP_REMOVE)
1970 uNew |= uTmp;
1971 else
1972 uNew &= ~uTmp;
1973 }
1974 else
1975 {
1976 /* A mnemonic, find the end of it. */
1977 const char *pszMnemonic = pszValue - 1;
1978 do
1979 ch = *++pszValue;
1980 while (ch && !RT_C_IS_SPACE(ch) && !RT_C_IS_CNTRL(ch) && ch != ';' && ch != ':');
1981 size_t cchMnemonic = pszValue - pszMnemonic;
1982
1983 /* Look it up in the map and apply it. */
1984 unsigned i = 0;
1985 while (paMnemonics[i].pszMnemonic)
1986 {
1987 if ( cchMnemonic == paMnemonics[i].cchMnemonic
1988 && !memcmp(pszMnemonic, paMnemonics[i].pszMnemonic, cchMnemonic))
1989 {
1990 if (paMnemonics[i].fSet ? enmOp != RTDBGCFGOP_REMOVE : enmOp == RTDBGCFGOP_REMOVE)
1991 uNew |= paMnemonics[i].fFlags;
1992 else
1993 uNew &= ~paMnemonics[i].fFlags;
1994 break;
1995 }
1996 i++;
1997 }
1998
1999 if (!paMnemonics[i].pszMnemonic)
2000 return VERR_DBG_CFG_INVALID_VALUE;
2001 }
2002 }
2003
2004 *puValue = uNew;
2005 return VINF_SUCCESS;
2006}
2007
2008
2009RTDECL(int) RTDbgCfgChangeString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, const char *pszValue)
2010{
2011 PRTDBGCFGINT pThis = hDbgCfg;
2012 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2013 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2014 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
2015 if (!pszValue)
2016 pszValue = "";
2017 else
2018 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
2019
2020 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
2021 if (RT_SUCCESS(rc))
2022 {
2023 switch (enmProp)
2024 {
2025 case RTDBGCFGPROP_FLAGS:
2026 rc = rtDbgCfgChangeStringU64(pThis, enmOp, pszValue, g_aDbgCfgFlags, &pThis->fFlags);
2027 break;
2028 case RTDBGCFGPROP_PATH:
2029 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->PathList);
2030 break;
2031 case RTDBGCFGPROP_SUFFIXES:
2032 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, false, &pThis->SuffixList);
2033 break;
2034 case RTDBGCFGPROP_SRC_PATH:
2035 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->SrcPathList);
2036 break;
2037 default:
2038 AssertFailed();
2039 rc = VERR_INTERNAL_ERROR_3;
2040 }
2041
2042 RTCritSectRwLeaveExcl(&pThis->CritSect);
2043 }
2044
2045 return rc;
2046}
2047
2048
2049RTDECL(int) RTDbgCfgChangeUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, uint64_t uValue)
2050{
2051 PRTDBGCFGINT pThis = hDbgCfg;
2052 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2053 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2054 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
2055
2056 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
2057 if (RT_SUCCESS(rc))
2058 {
2059 uint64_t *puValue = NULL;
2060 switch (enmProp)
2061 {
2062 case RTDBGCFGPROP_FLAGS:
2063 puValue = &pThis->fFlags;
2064 break;
2065 default:
2066 rc = VERR_DBG_CFG_NOT_UINT_PROP;
2067 }
2068 if (RT_SUCCESS(rc))
2069 {
2070 switch (enmOp)
2071 {
2072 case RTDBGCFGOP_SET:
2073 *puValue = uValue;
2074 break;
2075 case RTDBGCFGOP_APPEND:
2076 case RTDBGCFGOP_PREPEND:
2077 *puValue |= uValue;
2078 break;
2079 case RTDBGCFGOP_REMOVE:
2080 *puValue &= ~uValue;
2081 break;
2082 default:
2083 AssertFailed();
2084 rc = VERR_INTERNAL_ERROR_2;
2085 }
2086 }
2087
2088 RTCritSectRwLeaveExcl(&pThis->CritSect);
2089 }
2090
2091 return rc;
2092}
2093
2094
2095/**
2096 * Querys a string list as a single string (semicolon separators).
2097 *
2098 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
2099 * @param hDbgCfg The config instance handle.
2100 * @param pList The string list anchor.
2101 * @param pszValue The output buffer.
2102 * @param cbValue The size of the output buffer.
2103 */
2104static int rtDbgCfgQueryStringList(RTDBGCFG hDbgCfg, PRTLISTANCHOR pList,
2105 char *pszValue, size_t cbValue)
2106{
2107 RT_NOREF_PV(hDbgCfg);
2108
2109 /*
2110 * Check the length first.
2111 */
2112 size_t cbReq = 1;
2113 PRTDBGCFGSTR pCur;
2114 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
2115 cbReq += pCur->cch + 1;
2116 if (cbReq > cbValue)
2117 return VERR_BUFFER_OVERFLOW;
2118
2119 /*
2120 * Construct the string list in the buffer.
2121 */
2122 char *psz = pszValue;
2123 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
2124 {
2125 if (psz != pszValue)
2126 *psz++ = ';';
2127 memcpy(psz, pCur->sz, pCur->cch);
2128 psz += pCur->cch;
2129 }
2130 *psz = '\0';
2131
2132 return VINF_SUCCESS;
2133}
2134
2135
2136/**
2137 * Querys the string value of a 64-bit unsigned int.
2138 *
2139 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
2140 * @param hDbgCfg The config instance handle.
2141 * @param uValue The value to query.
2142 * @param paMnemonics The mnemonics map for this value.
2143 * @param pszValue The output buffer.
2144 * @param cbValue The size of the output buffer.
2145 */
2146static int rtDbgCfgQueryStringU64(RTDBGCFG hDbgCfg, uint64_t uValue, PCRTDBGCFGU64MNEMONIC paMnemonics,
2147 char *pszValue, size_t cbValue)
2148{
2149 RT_NOREF_PV(hDbgCfg);
2150
2151 /*
2152 * If no mnemonics, just return the hex value.
2153 */
2154 if (!paMnemonics || paMnemonics[0].pszMnemonic)
2155 {
2156 char szTmp[64];
2157 size_t cch = RTStrPrintf(szTmp, sizeof(szTmp), "%#x", uValue);
2158 if (cch + 1 > cbValue)
2159 return VERR_BUFFER_OVERFLOW;
2160 memcpy(pszValue, szTmp, cbValue);
2161 return VINF_SUCCESS;
2162 }
2163
2164 /*
2165 * Check that there is sufficient buffer space first.
2166 */
2167 size_t cbReq = 1;
2168 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
2169 if ( paMnemonics[i].fSet
2170 ? (paMnemonics[i].fFlags & uValue)
2171 : !(paMnemonics[i].fFlags & uValue))
2172 cbReq += (cbReq != 1) + paMnemonics[i].cchMnemonic;
2173 if (cbReq > cbValue)
2174 return VERR_BUFFER_OVERFLOW;
2175
2176 /*
2177 * Construct the string.
2178 */
2179 char *psz = pszValue;
2180 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
2181 if ( paMnemonics[i].fSet
2182 ? (paMnemonics[i].fFlags & uValue)
2183 : !(paMnemonics[i].fFlags & uValue))
2184 {
2185 if (psz != pszValue)
2186 *psz++ = ' ';
2187 memcpy(psz, paMnemonics[i].pszMnemonic, paMnemonics[i].cchMnemonic);
2188 psz += paMnemonics[i].cchMnemonic;
2189 }
2190 *psz = '\0';
2191 return VINF_SUCCESS;
2192}
2193
2194
2195RTDECL(int) RTDbgCfgQueryString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, char *pszValue, size_t cbValue)
2196{
2197 PRTDBGCFGINT pThis = hDbgCfg;
2198 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2199 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2200 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
2201
2202 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
2203 if (RT_SUCCESS(rc))
2204 {
2205 switch (enmProp)
2206 {
2207 case RTDBGCFGPROP_FLAGS:
2208 rc = rtDbgCfgQueryStringU64(pThis, pThis->fFlags, g_aDbgCfgFlags, pszValue, cbValue);
2209 break;
2210 case RTDBGCFGPROP_PATH:
2211 rc = rtDbgCfgQueryStringList(pThis, &pThis->PathList, pszValue, cbValue);
2212 break;
2213 case RTDBGCFGPROP_SUFFIXES:
2214 rc = rtDbgCfgQueryStringList(pThis, &pThis->SuffixList, pszValue, cbValue);
2215 break;
2216 case RTDBGCFGPROP_SRC_PATH:
2217 rc = rtDbgCfgQueryStringList(pThis, &pThis->SrcPathList, pszValue, cbValue);
2218 break;
2219 default:
2220 AssertFailed();
2221 rc = VERR_INTERNAL_ERROR_3;
2222 }
2223
2224 RTCritSectRwLeaveShared(&pThis->CritSect);
2225 }
2226
2227 return rc;
2228}
2229
2230
2231RTDECL(int) RTDbgCfgQueryUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, uint64_t *puValue)
2232{
2233 PRTDBGCFGINT pThis = hDbgCfg;
2234 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2235 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2236 AssertPtrReturn(puValue, VERR_INVALID_POINTER);
2237
2238 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
2239 if (RT_SUCCESS(rc))
2240 {
2241 switch (enmProp)
2242 {
2243 case RTDBGCFGPROP_FLAGS:
2244 *puValue = pThis->fFlags;
2245 break;
2246 default:
2247 rc = VERR_DBG_CFG_NOT_UINT_PROP;
2248 }
2249
2250 RTCritSectRwLeaveShared(&pThis->CritSect);
2251 }
2252
2253 return rc;
2254}
2255
2256RTDECL(uint32_t) RTDbgCfgRetain(RTDBGCFG hDbgCfg)
2257{
2258 PRTDBGCFGINT pThis = hDbgCfg;
2259 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
2260
2261 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
2262 Assert(cRefs < UINT32_MAX / 2);
2263 return cRefs;
2264}
2265
2266
2267RTDECL(uint32_t) RTDbgCfgRelease(RTDBGCFG hDbgCfg)
2268{
2269 if (hDbgCfg == NIL_RTDBGCFG)
2270 return 0;
2271
2272 PRTDBGCFGINT pThis = hDbgCfg;
2273 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
2274
2275 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
2276 if (!cRefs)
2277 {
2278 /*
2279 * Last reference - free all memory.
2280 */
2281 ASMAtomicWriteU32(&pThis->u32Magic, ~RTDBGCFG_MAGIC);
2282 rtDbgCfgFreeStrList(&pThis->PathList);
2283 rtDbgCfgFreeStrList(&pThis->SuffixList);
2284 rtDbgCfgFreeStrList(&pThis->SrcPathList);
2285#ifdef RT_OS_WINDOWS
2286 rtDbgCfgFreeStrList(&pThis->NtSymbolPathList);
2287 rtDbgCfgFreeStrList(&pThis->NtExecutablePathList);
2288 rtDbgCfgFreeStrList(&pThis->NtSourcePath);
2289#endif
2290 RTCritSectRwDelete(&pThis->CritSect);
2291 RTMemFree(pThis);
2292 }
2293 else
2294 Assert(cRefs < UINT32_MAX / 2);
2295 return cRefs;
2296}
2297
2298
2299RTDECL(int) RTDbgCfgCreate(PRTDBGCFG phDbgCfg, const char *pszEnvVarPrefix, bool fNativePaths)
2300{
2301 /*
2302 * Validate input.
2303 */
2304 AssertPtrReturn(phDbgCfg, VERR_INVALID_POINTER);
2305 if (pszEnvVarPrefix)
2306 {
2307 AssertPtrReturn(pszEnvVarPrefix, VERR_INVALID_POINTER);
2308 AssertReturn(*pszEnvVarPrefix, VERR_INVALID_PARAMETER);
2309 }
2310
2311 /*
2312 * Allocate and initialize a new instance.
2313 */
2314 PRTDBGCFGINT pThis = (PRTDBGCFGINT)RTMemAllocZ(sizeof(*pThis));
2315 if (!pThis)
2316 return VERR_NO_MEMORY;
2317
2318 pThis->u32Magic = RTDBGCFG_MAGIC;
2319 pThis->cRefs = 1;
2320 RTListInit(&pThis->PathList);
2321 RTListInit(&pThis->SuffixList);
2322 RTListInit(&pThis->SrcPathList);
2323#ifdef RT_OS_WINDOWS
2324 RTListInit(&pThis->NtSymbolPathList);
2325 RTListInit(&pThis->NtExecutablePathList);
2326 RTListInit(&pThis->NtSourcePath);
2327#endif
2328
2329 int rc = RTCritSectRwInit(&pThis->CritSect);
2330 if (RT_FAILURE(rc))
2331 {
2332 RTMemFree(pThis);
2333 return rc;
2334 }
2335
2336 /*
2337 * Read configurtion from the environment if requested to do so.
2338 */
2339 if (pszEnvVarPrefix || fNativePaths)
2340 {
2341 const size_t cbEnvVar = 256;
2342 const size_t cbEnvVal = 65536 - cbEnvVar;
2343 char *pszEnvVar = (char *)RTMemTmpAlloc(cbEnvVar + cbEnvVal);
2344 if (pszEnvVar)
2345 {
2346 char *pszEnvVal = pszEnvVar + cbEnvVar;
2347
2348 if (pszEnvVarPrefix)
2349 {
2350 static struct
2351 {
2352 RTDBGCFGPROP enmProp;
2353 const char *pszVar;
2354 } const s_aProps[] =
2355 {
2356 { RTDBGCFGPROP_FLAGS, "FLAGS" },
2357 { RTDBGCFGPROP_PATH, "PATH" },
2358 { RTDBGCFGPROP_SUFFIXES, "SUFFIXES" },
2359 { RTDBGCFGPROP_SRC_PATH, "SRC_PATH" },
2360 };
2361
2362 for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++)
2363 {
2364 size_t cchEnvVar = RTStrPrintf(pszEnvVar, cbEnvVar, "%s_%s", pszEnvVarPrefix, s_aProps[i].pszVar);
2365 if (cchEnvVar >= cbEnvVar - 1)
2366 {
2367 rc = VERR_BUFFER_OVERFLOW;
2368 break;
2369 }
2370
2371 rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, pszEnvVal, cbEnvVal, NULL);
2372 if (RT_SUCCESS(rc))
2373 {
2374 rc = RTDbgCfgChangeString(pThis, s_aProps[i].enmProp, RTDBGCFGOP_SET, pszEnvVal);
2375 if (RT_FAILURE(rc))
2376 break;
2377 }
2378 else if (rc != VERR_ENV_VAR_NOT_FOUND)
2379 break;
2380 else
2381 rc = VINF_SUCCESS;
2382 }
2383 }
2384
2385 /*
2386 * Pick up system specific search paths.
2387 */
2388 if (RT_SUCCESS(rc) && fNativePaths)
2389 {
2390 struct
2391 {
2392 PRTLISTANCHOR pList;
2393 const char *pszVar;
2394 char chSep;
2395 } aNativePaths[] =
2396 {
2397#ifdef RT_OS_WINDOWS
2398 { &pThis->NtExecutablePathList, "_NT_EXECUTABLE_PATH", ';' },
2399 { &pThis->NtSymbolPathList, "_NT_ALT_SYMBOL_PATH", ';' },
2400 { &pThis->NtSymbolPathList, "_NT_SYMBOL_PATH", ';' },
2401 { &pThis->NtSourcePath, "_NT_SOURCE_PATH", ';' },
2402#endif
2403 { NULL, NULL, 0 }
2404 };
2405 for (unsigned i = 0; aNativePaths[i].pList; i++)
2406 {
2407 Assert(aNativePaths[i].chSep == ';'); /* fix when needed */
2408 rc = RTEnvGetEx(RTENV_DEFAULT, aNativePaths[i].pszVar, pszEnvVal, cbEnvVal, NULL);
2409 if (RT_SUCCESS(rc))
2410 {
2411 rc = rtDbgCfgChangeStringList(pThis, RTDBGCFGOP_APPEND, pszEnvVal, true, aNativePaths[i].pList);
2412 if (RT_FAILURE(rc))
2413 break;
2414 }
2415 else if (rc != VERR_ENV_VAR_NOT_FOUND)
2416 break;
2417 else
2418 rc = VINF_SUCCESS;
2419 }
2420 }
2421 RTMemTmpFree(pszEnvVar);
2422 }
2423 else
2424 rc = VERR_NO_TMP_MEMORY;
2425 if (RT_FAILURE(rc))
2426 {
2427 /*
2428 * Error, bail out.
2429 */
2430 RTDbgCfgRelease(pThis);
2431 return rc;
2432 }
2433 }
2434
2435 /*
2436 * Returns successfully.
2437 */
2438 *phDbgCfg = pThis;
2439
2440 return VINF_SUCCESS;
2441}
2442
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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