VirtualBox

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

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

First part of loading .dSYM bundles.

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