VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTDbgSymCache.cpp@ 83078

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

RTDbgSymCache: Added way to specify filename to use in the cache. Corrected UUID symlinks.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 49.3 KB
 
1/* $Id: RTDbgSymCache.cpp 83078 2020-02-15 02:20:17Z vboxsync $ */
2/** @file
3 * IPRT - Debug Symbol Cache Utility.
4 */
5
6/*
7 * Copyright (C) 2013-2020 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#include <iprt/zip.h>
32
33#include <iprt/buildconfig.h>
34#include <iprt/dbg.h>
35#include <iprt/err.h>
36#include <iprt/file.h>
37#include <iprt/formats/mach-o.h>
38#include <iprt/getopt.h>
39#include <iprt/initterm.h>
40#include <iprt/ldr.h>
41#include <iprt/message.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/stream.h>
45#include <iprt/string.h>
46#include <iprt/uuid.h>
47#include <iprt/vfs.h>
48#include <iprt/zip.h>
49
50
51/*********************************************************************************************************************************
52* Structures and Typedefs *
53*********************************************************************************************************************************/
54/**
55 * Cache file type.
56 */
57typedef enum RTDBGSYMCACHEFILETYPE
58{
59 RTDBGSYMCACHEFILETYPE_INVALID,
60 RTDBGSYMCACHEFILETYPE_DIR,
61 RTDBGSYMCACHEFILETYPE_DIR_FILTER,
62 RTDBGSYMCACHEFILETYPE_DEBUG_FILE,
63 RTDBGSYMCACHEFILETYPE_IMAGE_FILE,
64 RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE,
65 RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE,
66 RTDBGSYMCACHEFILETYPE_IGNORE
67} RTDBGSYMCACHEFILETYPE;
68
69
70/**
71 * Configuration for the 'add' command.
72 */
73typedef struct RTDBGSYMCACHEADDCFG
74{
75 bool fRecursive;
76 bool fOverwriteOnConflict;
77 const char *pszFilter;
78 const char *pszCache;
79} RTDBGSYMCACHEADDCFG;
80/** Pointer to a read only 'add' config. */
81typedef RTDBGSYMCACHEADDCFG const *PCRTDBGSYMCACHEADDCFG;
82
83
84/*********************************************************************************************************************************
85* Global Variables *
86*********************************************************************************************************************************/
87/** Bundle suffixes. */
88static const char * const g_apszBundleSuffixes[] =
89{
90 ".kext",
91 ".app",
92 ".framework", /** @todo framework is different. */
93 ".component",
94 ".action",
95 ".caction",
96 ".bundle",
97 ".sourcebundle",
98 ".plugin",
99 ".ppp",
100 ".menu",
101 ".monitorpanel",
102 ".scripting",
103 ".prefPane",
104 ".qlgenerator",
105 ".brailledriver",
106 ".saver",
107 ".SpeechVoice",
108 ".SpeechRecognizer",
109 ".SpeechSynthesizer",
110 ".mdimporter",
111 ".spreporter",
112 ".xpc",
113 NULL
114};
115
116/** Debug bundle suffixes. (Same as above + .dSYM) */
117static const char * const g_apszDSymBundleSuffixes[] =
118{
119 ".kext.dSYM",
120 ".app.dSYM",
121 ".framework.dSYM",
122 ".component.dSYM",
123 ".action.dSYM",
124 ".caction.dSYM",
125 ".bundle.dSYM",
126 ".sourcebundle.dSYM",
127 ".menu.dSYM",
128 ".plugin.dSYM",
129 ".ppp.dSYM",
130 ".monitorpanel.dSYM",
131 ".scripting.dSYM",
132 ".prefPane.dSYM",
133 ".qlgenerator.dSYM",
134 ".brailledriver.dSYM",
135 ".saver.dSYM",
136 ".SpeechVoice.dSYM",
137 ".SpeechRecognizer.dSYM",
138 ".SpeechSynthesizer.dSYM",
139 ".mdimporter.dSYM",
140 ".spreporter.dSYM",
141 ".xpc.dSYM",
142 ".dSYM",
143 NULL
144};
145
146
147/*********************************************************************************************************************************
148* Internal Functions *
149*********************************************************************************************************************************/
150static int rtDbgSymCacheAddDirWorker(char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg);
151
152
153
154/**
155 * Display the version of the cache program.
156 *
157 * @returns exit code.
158 */
159static RTEXITCODE rtDbgSymCacheVersion(void)
160{
161 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
162 return RTEXITCODE_SUCCESS;
163}
164
165
166/**
167 * Shows the usage of the cache program.
168 *
169 * @returns Exit code.
170 * @param pszArg0 Program name.
171 * @param pszCommand Command selector, NULL if all.
172 */
173static RTEXITCODE rtDbgSymCacheUsage(const char *pszArg0, const char *pszCommand)
174{
175 if (!pszCommand || !strcmp(pszCommand, "add"))
176 RTPrintf("Usage: %s add [-Rno] <cache-root-dir> <file1[=cache-name]> [fileN..]\n"
177 "\n"
178 "Options:\n"
179 " -R, --recursive\n"
180 " Process directory arguments recursively.\n"
181 " -n, --no-recursive\n"
182 " No recursion. (default)\n"
183 " -o, --overwrite-on-conflict\n"
184 " Overwrite existing cache entry.\n"
185 , RTPathFilename(pszArg0));
186 return RTEXITCODE_SUCCESS;
187}
188
189
190/**
191 * Creates a UUID mapping for the file.
192 *
193 * @returns IPRT status code.
194 * @param pszCacheFile The path to the file in the cache.
195 * @param pFileUuid The UUID of the file.
196 * @param pszUuidMapDir The UUID map subdirectory in the cache, if this is
197 * wanted, otherwise NULL.
198 * @param pCfg The configuration.
199 */
200static int rtDbgSymCacheAddCreateUuidMapping(const char *pszCacheFile, PRTUUID pFileUuid,
201 const char *pszUuidMapDir, PCRTDBGSYMCACHEADDCFG pCfg)
202{
203 /*
204 * Create the UUID map entry first, deep.
205 */
206 char szMapPath[RTPATH_MAX];
207 int rc = RTPathJoin(szMapPath, sizeof(szMapPath) - sizeof("/xxxx/yyyy/xxxx/yyyy/xxxx/zzzzzzzzzzzz") + 1,
208 pCfg->pszCache, pszUuidMapDir);
209 if (RT_FAILURE(rc))
210 return RTMsgErrorRc(rc, "Error constructing UUID map path (RTPathJoin): %Rrc", rc);
211
212 size_t cch = strlen(szMapPath);
213 szMapPath[cch] = '-';
214
215 rc = RTUuidToStr(pFileUuid, &szMapPath[cch + 2], sizeof(szMapPath) - cch);
216 if (RT_FAILURE(rc))
217 return RTMsgErrorRc(rc, "Error constructing UUID map path (RTUuidToStr): %Rrc", rc);
218
219 /* Uppercase the whole lot. */
220 RTStrToUpper(&szMapPath[cch + 2]);
221
222 /* Split the first dword in two. */
223 szMapPath[cch + 1] = szMapPath[cch + 2];
224 szMapPath[cch + 2] = szMapPath[cch + 3];
225 szMapPath[cch + 3] = szMapPath[cch + 4];
226 szMapPath[cch + 4] = szMapPath[cch + 5];
227 szMapPath[cch + 5] = '-';
228
229 /*
230 * Create the directories in the path.
231 */
232 for (unsigned i = 0; i < 6; i++, cch += 5)
233 {
234 Assert(szMapPath[cch] == '-');
235 szMapPath[cch] = '\0';
236 if (!RTDirExists(szMapPath))
237 {
238 rc = RTDirCreate(szMapPath, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
239 if (RT_FAILURE(rc))
240 return RTMsgErrorRc(rc, "RTDirCreate failed on '%s' (UUID map path): %Rrc", szMapPath, rc);
241 }
242 szMapPath[cch] = RTPATH_SLASH;
243 }
244 cch -= 5;
245
246 /*
247 * Calculate a relative path from there to the actual file.
248 */
249 char szLinkTarget[RTPATH_MAX];
250 szMapPath[cch] = '\0';
251 rc = RTPathCalcRelative(szLinkTarget, sizeof(szLinkTarget), szMapPath, false /*fFromFile*/, pszCacheFile);
252 szMapPath[cch] = RTPATH_SLASH;
253 if (RT_FAILURE(rc))
254 return RTMsgErrorRc(rc, "Failed to calculate relative path from '%s' to '%s': %Rrc", szMapPath, pszCacheFile, rc);
255
256 /*
257 * If there is already a link there, check if it matches or whether
258 * perhaps it's target doesn't exist.
259 */
260 RTFSOBJINFO ObjInfo;
261 rc = RTPathQueryInfoEx(szMapPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
262 if (RT_SUCCESS(rc))
263 {
264 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
265 {
266 rc = RTPathQueryInfoEx(szMapPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
267 if (RT_SUCCESS(rc))
268 {
269 char *pszCurTarget = NULL;
270 rc = RTSymlinkReadA(szMapPath, &pszCurTarget);
271 if (RT_FAILURE(rc))
272 return RTMsgErrorRc(rc, "UUID map: failed to read existing symlink '%s': %Rrc", szMapPath, rc);
273 if (RTPathCompare(pszCurTarget, szLinkTarget) == 0)
274 RTMsgInfo("UUID map: existing link '%s' has the same target ('%s').", szMapPath, pszCurTarget);
275 else
276 {
277 RTMsgError("UUID map: Existing mapping '%s' pointing to '%s' insted of '%s'",
278 szMapPath, pszCurTarget, szLinkTarget);
279 rc = VERR_ALREADY_EXISTS;
280 }
281 RTStrFree(pszCurTarget);
282 return rc;
283 }
284 else
285 RTMsgInfo("UUID map: replacing dangling link '%s'", szMapPath);
286 RTSymlinkDelete(szMapPath, 0 /*fFlags*/);
287 }
288 else if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
289 return RTMsgErrorRc(VERR_IS_A_FILE,
290 "UUID map: found file at '%s', expect symbolic link or nothing.", szMapPath);
291 else if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
292 return RTMsgErrorRc(VERR_IS_A_DIRECTORY,
293 "UUID map: found directory at '%s', expect symbolic link or nothing.", szMapPath);
294 else
295 return RTMsgErrorRc(VERR_NOT_SYMLINK,
296 "UUID map: Expected symbolic link or nothing at '%s', found: fMode=%#x",
297 szMapPath, ObjInfo.Attr.fMode);
298 }
299
300 /*
301 * Create the symbolic link.
302 */
303 rc = RTSymlinkCreate(szMapPath, szLinkTarget, RTSYMLINKTYPE_FILE, 0);
304 if (RT_FAILURE(rc))
305 return RTMsgErrorRc(rc, "Failed to create UUID map symlink '%s' to '%s': %Rrc", szMapPath, szLinkTarget, rc);
306 RTMsgInfo("UUID map: %s => %s", szMapPath, szLinkTarget);
307 return VINF_SUCCESS;
308}
309
310
311/**
312 * Adds a file to the cache.
313 *
314 * @returns IPRT status code.
315 * @param pszSrcPath Path to the source file.
316 * @param pszDstName The name of the destionation file (no path stuff).
317 * @param pszExtraSuff Optional extra suffix. Mach-O dSYM hack.
318 * @param pszDstSubDir The subdirectory to file it under. This is the
319 * stringification of a relatively unique identifier of
320 * the file in question.
321 * @param pAddToUuidMap Optional file UUID that is used to create a UUID map
322 * entry.
323 * @param pszUuidMapDir The UUID map subdirectory in the cache, if this is
324 * wanted, otherwise NULL.
325 * @param pCfg The configuration.
326 */
327static int rtDbgSymCacheAddOneFile(const char *pszSrcPath, const char *pszDstName, const char *pszExtraStuff,
328 const char *pszDstSubDir, PRTUUID pAddToUuidMap, const char *pszUuidMapDir,
329 PCRTDBGSYMCACHEADDCFG pCfg)
330{
331 /*
332 * Build and create the destination path, step by step.
333 */
334 char szDstPath[RTPATH_MAX];
335 int rc = RTPathJoin(szDstPath, sizeof(szDstPath), pCfg->pszCache, pszDstName);
336 if (RT_FAILURE(rc))
337 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
338
339 if (!RTDirExists(szDstPath))
340 {
341 rc = RTDirCreate(szDstPath, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
342 if (RT_FAILURE(rc))
343 return RTMsgErrorRc(rc, "Error creating '%s': %Rrc", szDstPath, rc);
344 }
345
346 rc = RTPathAppend(szDstPath, sizeof(szDstPath), pszDstSubDir);
347 if (RT_FAILURE(rc))
348 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
349
350 if (!RTDirExists(szDstPath))
351 {
352 rc = RTDirCreate(szDstPath, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
353 if (RT_FAILURE(rc))
354 return RTMsgErrorRc(rc, "Error creating '%s': %Rrc", szDstPath, rc);
355 }
356
357 rc = RTPathAppend(szDstPath, sizeof(szDstPath), pszDstName);
358 if (RT_FAILURE(rc))
359 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
360 if (pszExtraStuff)
361 {
362 rc = RTStrCat(szDstPath, sizeof(szDstPath), pszExtraStuff);
363 if (RT_FAILURE(rc))
364 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
365 }
366
367 /*
368 * If the file exists, we compare the two and throws an error if the doesn't match.
369 */
370 if (RTPathExists(szDstPath))
371 {
372 rc = RTFileCompare(pszSrcPath, szDstPath);
373 if (RT_SUCCESS(rc))
374 {
375 RTMsgInfo("%s is already in the cache.", pszSrcPath);
376 if (pAddToUuidMap && pszUuidMapDir)
377 return rtDbgSymCacheAddCreateUuidMapping(szDstPath, pAddToUuidMap, pszUuidMapDir, pCfg);
378 return VINF_SUCCESS;
379 }
380 if (rc == VERR_NOT_EQUAL)
381 RTMsgInfo("Cache conflict with existing entry '%s' when inserting '%s'.", szDstPath, pszSrcPath);
382 else
383 RTMsgInfo("Error comparing '%s' with '%s': %Rrc", pszSrcPath, szDstPath, rc);
384 if (!pCfg->fOverwriteOnConflict)
385 return rc;
386 }
387
388 /*
389 * The file doesn't exist or we should overwrite it,
390 */
391 RTMsgInfo("Copying '%s' to '%s'...", pszSrcPath, szDstPath);
392 rc = RTFileCopy(pszSrcPath, szDstPath);
393 if (RT_FAILURE(rc))
394 return RTMsgErrorRc(rc, "Error copying '%s' to '%s': %Rrc", pszSrcPath, szDstPath, rc);
395 if (pAddToUuidMap && pszUuidMapDir)
396 return rtDbgSymCacheAddCreateUuidMapping(szDstPath, pAddToUuidMap, pszUuidMapDir, pCfg);
397 return VINF_SUCCESS;
398}
399
400
401/**
402 * Worker that add the image file to the right place.
403 *
404 * @returns IPRT status code.
405 * @param pszPath Path to the image file.
406 * @param pszDstName Add to the cache under this name. Typically the
407 * filename part of @a pszPath.
408 * @param pCfg Configuration data.
409 * @param hLdrMod Image handle.
410 * @param pszExtraSuff Optional extra suffix. Mach-O dSYM hack.
411 * @param pszUuidMapDir Optional UUID map cache directory if the image
412 * should be mapped by UUID.
413 * The map is a Mac OS X debug feature supported by
414 * the two native debuggers gdb and lldb. Look for
415 * descriptions of DBGFileMappedPaths in the
416 * com.apple.DebugSymbols in the user defaults.
417 */
418static int rtDbgSymCacheAddImageFileWorker(const char *pszPath, const char *pszDstName, PCRTDBGSYMCACHEADDCFG pCfg,
419 RTLDRMOD hLdrMod, const char *pszExtrSuff, const char *pszUuidMapDir)
420{
421 /*
422 * Determine which subdirectory to put the files in.
423 */
424 RTUUID Uuid;
425 PRTUUID pUuid = NULL;
426 int rc;
427 char szSubDir[48];
428 RTLDRFMT enmFmt = RTLdrGetFormat(hLdrMod);
429 switch (enmFmt)
430 {
431 case RTLDRFMT_MACHO:
432 {
433 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_UUID, &Uuid, sizeof(Uuid));
434 if (RT_FAILURE(rc))
435 return RTMsgErrorRc(rc, "Error quering image UUID from image '%s': %Rrc", pszPath, rc);
436
437 rc = RTUuidToStr(&Uuid, szSubDir, sizeof(szSubDir));
438 if (RT_FAILURE(rc))
439 return RTMsgErrorRc(rc, "Error convering UUID for image '%s' to string: %Rrc", pszPath, rc);
440 pUuid = &Uuid;
441 break;
442 }
443
444 case RTLDRFMT_PE:
445 {
446 uint32_t uTimestamp;
447 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp, sizeof(uTimestamp));
448 if (RT_FAILURE(rc))
449 return RTMsgErrorRc(rc, "Error quering timestamp from image '%s': %Rrc", pszPath, rc);
450
451 size_t cbImage = RTLdrSize(hLdrMod);
452 if (cbImage == ~(size_t)0)
453 return RTMsgErrorRc(rc, "Error quering size of image '%s': %Rrc", pszPath, rc);
454
455 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
456 break;
457 }
458
459 case RTLDRFMT_AOUT:
460 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Caching of a.out image has not yet been implemented: %s", pszPath);
461 case RTLDRFMT_ELF:
462 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Caching of ELF image has not yet been implemented: %s", pszPath);
463 case RTLDRFMT_LX:
464 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Caching of LX image has not yet been implemented: %s", pszPath);
465 default:
466 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Unknown loader format for '%s': %d", pszPath, enmFmt);
467 }
468
469 /*
470 * Now add it.
471 */
472 return rtDbgSymCacheAddOneFile(pszPath, pszDstName, pszExtrSuff, szSubDir, pUuid, pszUuidMapDir, pCfg);
473}
474
475
476/**
477 * Adds what we think is an image file to the cache.
478 *
479 * @returns IPRT status code.
480 * @param pszPath Path to the image file.
481 * @param pszDstName Add to the cache under this name. Typically the
482 * filename part of @a pszPath.
483 * @param pszExtraSuff Optional extra suffix. Mach-O dSYM hack.
484 * @param pszUuidMapDir The UUID map subdirectory in the cache, if this is
485 * wanted, otherwise NULL.
486 * @param pCfg Configuration data.
487 */
488static int rtDbgSymCacheAddImageFile(const char *pszPath, const char *pszDstName, const char *pszExtraSuff,
489 const char *pszUuidMapDir, PCRTDBGSYMCACHEADDCFG pCfg)
490{
491 RTERRINFOSTATIC ErrInfo;
492
493 /*
494 * Use the loader to open the alleged image file. We need to open it with
495 * arch set to amd64 and x86_32 in order to handle FAT images from the mac
496 * guys (we should actually enumerate archs, but that's currently not
497 * implemented nor necessary for our current use).
498 */
499 /* Open it as AMD64. */
500 RTLDRMOD hLdrMod64;
501 int rc = RTLdrOpenEx(pszPath, RTLDR_O_FOR_DEBUG, RTLDRARCH_AMD64, &hLdrMod64, RTErrInfoInitStatic(&ErrInfo));
502 if (RT_FAILURE(rc))
503 {
504 if (rc != VERR_LDR_ARCH_MISMATCH)
505 {
506 if (rc != VERR_INVALID_EXE_SIGNATURE)
507 return RTMsgErrorRc(rc, "RTLdrOpen failed opening '%s' [arch=amd64]: %Rrc%s%s", pszPath, rc,
508 RTErrInfoIsSet(&ErrInfo.Core) ? " - " : "",
509 RTErrInfoIsSet(&ErrInfo.Core) ? ErrInfo.Core.pszMsg : "");
510
511 RTMsgInfo("Skipping '%s', no a recognizable image file...", pszPath);
512 return VINF_SUCCESS;
513 }
514 hLdrMod64 = NIL_RTLDRMOD;
515 }
516
517 /* Open it as X86. */
518 RTLDRMOD hLdrMod32;
519 rc = RTLdrOpenEx(pszPath, RTLDR_O_FOR_DEBUG, RTLDRARCH_X86_32, &hLdrMod32, RTErrInfoInitStatic(&ErrInfo));
520 if (RT_FAILURE(rc))
521 {
522 if (rc != VERR_LDR_ARCH_MISMATCH)
523 {
524 RTLdrClose(hLdrMod64);
525 return RTMsgErrorRc(rc, "RTLdrOpen failed opening '%s' [arch=x86]: %Rrc%s%s", pszPath, rc,
526 RTErrInfoIsSet(&ErrInfo.Core) ? " - " : "",
527 RTErrInfoIsSet(&ErrInfo.Core) ? ErrInfo.Core.pszMsg : "");
528 }
529 hLdrMod32 = NIL_RTLDRMOD;
530 }
531
532 /*
533 * Add the file.
534 */
535 if (hLdrMod32 == NIL_RTLDRMOD)
536 rc = rtDbgSymCacheAddImageFileWorker(pszPath, pszDstName, pCfg, hLdrMod64, pszExtraSuff, pszUuidMapDir);
537 else if (hLdrMod64 == NIL_RTLDRMOD)
538 rc = rtDbgSymCacheAddImageFileWorker(pszPath, pszDstName, pCfg, hLdrMod32, pszExtraSuff, pszUuidMapDir);
539 else
540 {
541 /*
542 * Do we need to add it once or twice?
543 */
544 RTLDRFMT enmFmt = RTLdrGetFormat(hLdrMod32);
545 bool fSame = enmFmt == RTLdrGetFormat(hLdrMod64);
546 if (fSame && enmFmt == RTLDRFMT_MACHO)
547 {
548 RTUUID Uuid32, Uuid64;
549 int rc32 = RTLdrQueryProp(hLdrMod32, RTLDRPROP_UUID, &Uuid32, sizeof(Uuid32));
550 int rc64 = RTLdrQueryProp(hLdrMod64, RTLDRPROP_UUID, &Uuid64, sizeof(Uuid64));
551 fSame = RT_SUCCESS(rc32) == RT_SUCCESS(rc64);
552 if (fSame && RT_SUCCESS(rc32))
553 fSame = RTUuidCompare(&Uuid32, &Uuid64) == 0;
554 }
555 else if (fSame && enmFmt == RTLDRFMT_PE)
556 {
557 fSame = RTLdrSize(hLdrMod32) == RTLdrSize(hLdrMod64);
558 if (fSame)
559 {
560 uint32_t uTimestamp32, uTimestamp64;
561 int rc32 = RTLdrQueryProp(hLdrMod32, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp32, sizeof(uTimestamp32));
562 int rc64 = RTLdrQueryProp(hLdrMod64, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp64, sizeof(uTimestamp64));
563 fSame = RT_SUCCESS(rc32) == RT_SUCCESS(rc64);
564 if (fSame && RT_SUCCESS(rc32))
565 fSame = uTimestamp32 == uTimestamp64;
566 }
567 }
568
569 rc = rtDbgSymCacheAddImageFileWorker(pszPath, pszDstName, pCfg, hLdrMod64, pszExtraSuff, pszUuidMapDir);
570 if (!fSame)
571 {
572 /** @todo should symlink or hardlink this second copy. */
573 int rc2 = rtDbgSymCacheAddImageFileWorker(pszPath, pszDstName, pCfg, hLdrMod32, pszExtraSuff, pszUuidMapDir);
574 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
575 rc = rc2;
576 }
577 }
578
579 RTLdrClose(hLdrMod32);
580 RTLdrClose(hLdrMod64);
581 return VINF_SUCCESS;
582}
583
584
585/**
586 * Worker for rtDbgSymCacheAddDebugFile that adds a Mach-O debug file to the
587 * cache.
588 *
589 * @returns IPRT status code
590 * @param pszPath The path to the PDB file.
591 * @param pszDstName Add to the cache under this name. Typically the
592 * filename part of @a pszPath.
593 * @param pCfg The configuration.
594 * @param hFile Handle to the file.
595 */
596static int rtDbgSymCacheAddDebugMachO(const char *pszPath, const char *pszDstName, PCRTDBGSYMCACHEADDCFG pCfg)
597{
598 /* This shouldn't happen, figure out what to do if it does. */
599 RT_NOREF(pCfg, pszDstName);
600 return RTMsgErrorRc(VERR_NOT_IMPLEMENTED,
601 "'%s' is an OS X image file, did you point me to a file inside a .dSYM or .sym file?",
602 pszPath);
603}
604
605
606/**
607 * Worker for rtDbgSymCacheAddDebugFile that adds PDBs to the cace.
608 *
609 * @returns IPRT status code
610 * @param pszPath The path to the PDB file.
611 * @param pszDstName Add to the cache under this name. Typically the
612 * filename part of @a pszPath.
613 * @param pCfg The configuration.
614 * @param hFile Handle to the file.
615 */
616static int rtDbgSymCacheAddDebugPdb(const char *pszPath, const char *pszDstName, PCRTDBGSYMCACHEADDCFG pCfg, RTFILE hFile)
617{
618 RT_NOREF(pCfg, hFile, pszDstName);
619 return RTMsgErrorRc(VERR_NOT_IMPLEMENTED, "PDB support not implemented: '%s'", pszPath);
620}
621
622
623/**
624 * Adds a debug file to the cache.
625 *
626 * @returns IPRT status code
627 * @param pszPath The path to the debug file in question.
628 * @param pszDstName Add to the cache under this name. Typically the
629 * filename part of @a pszPath.
630 * @param pCfg The configuration.
631 */
632static int rtDbgSymCacheAddDebugFile(const char *pszPath, const char *pszDstName, PCRTDBGSYMCACHEADDCFG pCfg)
633{
634 /*
635 * Need to extract an identifier of sorts here in order to put them in
636 * the right place in the cache. Currently only implemnted for Mach-O
637 * files since these use executable containers.
638 *
639 * We take a look at the file header in hope to figure out what to do
640 * with the file.
641 */
642 RTFILE hFile;
643 int rc = RTFileOpen(&hFile, pszPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
644 if (RT_FAILURE(rc))
645 return RTMsgErrorRc(rc, "Error opening '%s': %Rrc", pszPath, rc);
646
647 union
648 {
649 uint64_t au64[16];
650 uint32_t au32[16];
651 uint16_t au16[32];
652 uint8_t ab[64];
653 } uBuf;
654 rc = RTFileRead(hFile, &uBuf, sizeof(uBuf), NULL);
655 if (RT_SUCCESS(rc))
656 {
657 /*
658 * Look for magics and call workers.
659 */
660 if (!memcmp(uBuf.ab, RT_STR_TUPLE("Microsoft C/C++ MSF 7.00")))
661 rc = rtDbgSymCacheAddDebugPdb(pszPath, pszDstName, pCfg, hFile);
662 else if ( uBuf.au32[0] == IMAGE_FAT_SIGNATURE
663 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE_OE
664 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE
665 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE
666 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE_OE
667 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE_OE)
668 rc = rtDbgSymCacheAddDebugMachO(pszPath, pszDstName, pCfg);
669 else
670 rc = RTMsgErrorRc(VERR_INVALID_MAGIC, "Unsupported debug file '%s' magic: %#010x", pszPath, uBuf.au32[0]);
671 }
672 else
673 rc = RTMsgErrorRc(rc, "Error reading '%s': %Rrc", pszPath, rc);
674
675 /* close the file. */
676 int rc2 = RTFileClose(hFile);
677 if (RT_FAILURE(rc2))
678 {
679 RTMsgError("Error closing '%s': %Rrc", pszPath, rc2);
680 if (RT_SUCCESS(rc))
681 rc = rc2;
682 }
683 return rc;
684}
685
686
687/**
688 * Constructs the path to the file instide the bundle that we're keen on.
689 *
690 * @returns IPRT status code.
691 * @param pszPath Path to the bundle on input, on successful
692 * return it's the path to the desired file. This
693 * a RTPATH_MAX size buffer.
694 * @param cchPath The length of the path up to the bundle name.
695 * @param cchName The length of the bundle name.
696 * @param pszSubDir The bundle subdirectory the file lives in.
697 * @param papszSuffixes Pointer to an array of bundle suffixes.
698 */
699static int rtDbgSymCacheConstructBundlePath(char *pszPath, size_t cchPath, size_t cchName, const char *pszSubDir,
700 const char * const *papszSuffixes)
701{
702 /*
703 * Calc the name without the bundle extension.
704 */
705 size_t const cchOrgName = cchName;
706 const char *pszEnd = &pszPath[cchPath + cchName];
707 for (unsigned i = 0; papszSuffixes[i]; i++)
708 {
709 Assert(papszSuffixes[i][0] == '.');
710 size_t cchSuff = strlen(papszSuffixes[i]);
711 if ( cchSuff < cchName
712 && !memcmp(&pszEnd[-(ssize_t)cchSuff], papszSuffixes[i], cchSuff))
713 {
714 cchName -= cchSuff;
715 break;
716 }
717 }
718
719 /*
720 * Check the immediate directory first, in case it's layed out like
721 * IOPCIFamily.kext.
722 */
723 int rc = RTPathAppendEx(pszPath, RTPATH_MAX, &pszPath[cchPath], cchName);
724 if (RT_FAILURE(rc) || !RTFileExists(pszPath))
725 {
726 /*
727 * Not there, ok then try the given subdirectory + name.
728 */
729 pszPath[cchPath + cchOrgName] = '\0';
730 rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
731 if (RT_SUCCESS(rc))
732 rc = RTPathAppendEx(pszPath, RTPATH_MAX, &pszPath[cchPath], cchName);
733 if (RT_FAILURE(rc))
734 {
735 pszPath[cchPath + cchOrgName] = '\0';
736 return RTMsgErrorRc(rc, "Error constructing image bundle path for '%s': %Rrc", pszPath, rc);
737 }
738 }
739
740 return VINF_SUCCESS;
741}
742
743
744/**
745 * Adds a image bundle of some sort.
746 *
747 * @returns IPRT status code.
748 * @param pszPath Path to the bundle. This a RTPATH_MAX size
749 * buffer that we can write to when creating the
750 * path to the file inside the bundle that we're
751 * interested in.
752 * @param cchPath The length of the path up to the bundle name.
753 * @param cchName The length of the bundle name.
754 * @param pszDstName Add to the cache under this name, NULL if not
755 * specified.
756 * @param pDirEntry The directory entry buffer, for handling bundle
757 * within bundle recursion.
758 * @param pCfg The configuration.
759 */
760static int rtDbgSymCacheAddImageBundle(char *pszPath, size_t cchPath, size_t cchName, const char *pszDstName,
761 PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg)
762{
763 /*
764 * Assuming these are kexts or simple applications, we only add the image
765 * file itself to the cache. No Info.plist or other files.
766 */
767 /** @todo consider looking for Frameworks and handling framework bundles. */
768 int rc = rtDbgSymCacheConstructBundlePath(pszPath, cchPath, cchName, "Contents/MacOS/", g_apszBundleSuffixes);
769 if (RT_SUCCESS(rc))
770 {
771 if (!pszDstName)
772 pszDstName = RTPathFilename(pszPath);
773 rc = rtDbgSymCacheAddImageFile(pszPath, pszDstName, NULL, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, pCfg);
774 }
775
776 /*
777 * Look for plugins and other sub-bundles.
778 */
779 if (pCfg->fRecursive)
780 {
781 static char const * const s_apszSubBundleDirs[] =
782 {
783 "Contents/Plugins/",
784 /** @todo Frameworks ++ */
785 };
786 for (uint32_t i = 0; i < RT_ELEMENTS(s_apszSubBundleDirs); i++)
787 {
788 pszPath[cchPath + cchName] = '\0';
789 int rc2 = RTPathAppend(pszPath, RTPATH_MAX - 1, s_apszSubBundleDirs[i]);
790 if (RT_SUCCESS(rc2))
791 {
792 if (RTDirExists(pszPath))
793 {
794 size_t cchPath2 = strlen(pszPath);
795 if (!RTPATH_IS_SLASH(pszPath[cchPath2 - 1]))
796 {
797 pszPath[cchPath2++] = RTPATH_SLASH;
798 pszPath[cchPath2] = '\0';
799 }
800 rc2 = rtDbgSymCacheAddDirWorker(pszPath, cchPath2, pDirEntry, pCfg);
801 }
802 }
803 else
804 {
805 pszPath[cchPath + cchName] = '\0';
806 RTMsgError("Error constructing bundle subdir path for '%s' + '%s': %Rrc", pszPath, s_apszSubBundleDirs[i], rc);
807 }
808 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
809 rc = rc2;
810 }
811 }
812
813 return rc;
814}
815
816
817/**
818 * Adds a debug bundle.
819 *
820 * @returns IPRT status code.
821 * @param pszPath Path to the bundle. This a RTPATH_MAX size
822 * buffer that we can write to when creating the
823 * path to the file inside the bundle that we're
824 * interested in.
825 * @param cchPath The length of the path up to the bundle name.
826 * @param cchName The length of the bundle name.
827 * @param pszDstName Add to the cache under this name, NULL if not
828 * specified.
829 * @param pCfg The configuration.
830 */
831static int rtDbgSymCacheAddDebugBundle(char *pszPath, size_t cchPath, size_t cchName, const char *pszDstName,
832 PCRTDBGSYMCACHEADDCFG pCfg)
833{
834 /*
835 * The current policy is not to add the whole .dSYM (or .sym) bundle, but
836 * rather just the dwarf image instide it. The <UUID>.plist and Info.plist
837 * files generally doesn't contain much extra information that's really
838 * necessary, I hope. At least this is what the uuidmap example in the
839 * lldb hints at (it links to the dwarf file, not the .dSYM dir).
840 *
841 * To avoid confusion with a .dSYM bundle, as well as collision with the
842 * image file, we use .dwarf suffix for the file.
843 *
844 * For details on the uuid map see rtDbgSymCacheAddImageFile as well as
845 * http://lldb.llvm.org/symbols.html .
846 *
847 * ASSUMES bundles contains Mach-O DWARF files.
848 */
849 int rc = rtDbgSymCacheConstructBundlePath(pszPath, cchPath, cchName, "Contents/Resources/DWARF/", g_apszDSymBundleSuffixes);
850 if (RT_SUCCESS(rc))
851 {
852 if (!pszDstName)
853 pszDstName = RTPathFilename(pszPath);
854 rc = rtDbgSymCacheAddImageFile(pszPath, pszDstName, RTDBG_CACHE_DSYM_FILE_SUFFIX, RTDBG_CACHE_UUID_MAP_DIR_DSYMS, pCfg);
855 }
856 return rc;
857}
858
859
860/**
861 * Figure the type of a file/dir based on path and FS object info.
862 *
863 * @returns The type.
864 * @param pszPath The path to the file/dir.
865 * @param pObjInfo The object information, symlinks followed.
866 */
867static RTDBGSYMCACHEFILETYPE rtDbgSymCacheFigureType2(const char *pszPath, PCRTFSOBJINFO pObjInfo)
868{
869 const char *pszName = RTPathFilename(pszPath);
870 const char *pszExt = RTPathSuffix(pszName);
871 if (pszExt)
872 pszExt++;
873 else
874 pszExt = "";
875
876 if ( RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode)
877 || (pObjInfo->Attr.fMode & RTFS_DOS_DIRECTORY)) /** @todo OS X samba reports reparse points in /Volumes/ that we cannot resolve. */
878 {
879 /* Skip directories shouldn't bother with. */
880 if ( !RTStrICmp(pszName, ".Trashes")
881 || !RTStrICmp(pszName, ".$RESCYCLE.BIN")
882 || !RTStrICmp(pszName, "System.kext") /* Usually only plugins here, so skip it. */
883 )
884 return RTDBGSYMCACHEFILETYPE_IGNORE;
885
886 /* Directories can also be bundles on the mac. */
887 if (!RTStrICmp(pszExt, "dSYM"))
888 return RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE;
889
890 for (unsigned i = 0; i < RT_ELEMENTS(g_apszBundleSuffixes) - 1; i++)
891 if (!RTStrICmp(pszExt, &g_apszBundleSuffixes[i][1]))
892 return RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE;
893
894 return RTDBGSYMCACHEFILETYPE_DIR;
895 }
896
897 if (!RTFS_IS_FILE(pObjInfo->Attr.fMode))
898 return RTDBGSYMCACHEFILETYPE_INVALID;
899
900 /* Select image vs debug info based on extension. */
901 if ( !RTStrICmp(pszExt, "pdb")
902 || !RTStrICmp(pszExt, "dbg")
903 || !RTStrICmp(pszExt, "sym")
904 || !RTStrICmp(pszExt, "dwo")
905 || !RTStrICmp(pszExt, "dwp")
906 || !RTStrICmp(pszExt, "debug")
907 || !RTStrICmp(pszExt, "dsym")
908 || !RTStrICmp(pszExt, "dwarf")
909 || !RTStrICmp(pszExt, "map")
910 || !RTStrICmp(pszExt, "cv"))
911 return RTDBGSYMCACHEFILETYPE_DEBUG_FILE;
912
913 /* Filter out a bunch of files which obviously shouldn't be images. */
914 if ( !RTStrICmp(pszExt, "txt")
915 || !RTStrICmp(pszExt, "html")
916 || !RTStrICmp(pszExt, "htm")
917 || !RTStrICmp(pszExt, "rtf")
918 || !RTStrICmp(pszExt, "zip")
919 || !RTStrICmp(pszExt, "doc")
920 || !RTStrICmp(pszExt, "gz")
921 || !RTStrICmp(pszExt, "bz2")
922 || !RTStrICmp(pszExt, "xz")
923 || !RTStrICmp(pszExt, "kmk")
924 || !RTStrICmp(pszExt, "c")
925 || !RTStrICmp(pszExt, "cpp")
926 || !RTStrICmp(pszExt, "h")
927 || !RTStrICmp(pszExt, "m")
928 || !RTStrICmp(pszExt, "mm")
929 || !RTStrICmp(pszExt, "asm")
930 || !RTStrICmp(pszExt, "S")
931 || !RTStrICmp(pszExt, "inc")
932 || !RTStrICmp(pszExt, "sh")
933 )
934 return RTDBGSYMCACHEFILETYPE_IGNORE;
935 if ( !RTStrICmp(pszName, "Makefile")
936 || !RTStrICmp(pszName, "GNUmakefile")
937 || !RTStrICmp(pszName, "createsymbolfiles")
938 || !RTStrICmp(pszName, "kgmacros")
939 )
940 return RTDBGSYMCACHEFILETYPE_IGNORE;
941
942 return RTDBGSYMCACHEFILETYPE_IMAGE_FILE;
943}
944
945
946/**
947 * Figure file type based on name, will stat the file/dir.
948 *
949 * @returns File type.
950 * @param pszPath The path to the file/dir to figure.
951 */
952static RTDBGSYMCACHEFILETYPE rtDbgSymCacheFigureType(const char *pszPath)
953{
954 const char *pszName = RTPathFilename(pszPath);
955
956 /* Trailing slash. */
957 if (!pszName)
958 return RTDBGSYMCACHEFILETYPE_DIR;
959
960 /* Wildcard means listing directory and filtering. */
961 if (strpbrk(pszName, "?*"))
962 return RTDBGSYMCACHEFILETYPE_DIR_FILTER;
963
964 /* Get object info, following links. */
965 RTFSOBJINFO ObjInfo;
966 int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
967 if (RT_FAILURE(rc))
968 return RTDBGSYMCACHEFILETYPE_INVALID;
969 return rtDbgSymCacheFigureType2(pszPath, &ObjInfo);
970}
971
972
973/**
974 * Recursive worker for rtDbgSymCacheAddDir, for minimal stack wasting.
975 *
976 * @returns IPRT status code (fully bitched).
977 * @param pszPath Pointer to a RTPATH_MAX size buffer containing
978 * the path to the current directory ending with a
979 * slash.
980 * @param cchPath The size of the current directory path.
981 * @param pDirEntry Pointer to the RTDIRENTRYEX structure to use.
982 * @param pCfg The configuration.
983 */
984static int rtDbgSymCacheAddDirWorker(char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg)
985{
986 /*
987 * Open the directory.
988 */
989 RTDIR hDir;
990 int rc, rc2;
991 if (pCfg->pszFilter)
992 {
993 rc = RTStrCopy(&pszPath[cchPath], RTPATH_MAX - cchPath, pCfg->pszFilter);
994 if (RT_FAILURE(rc))
995 {
996 pszPath[cchPath] = '\0';
997 return RTMsgErrorRc(rc, "Filename too long (%Rrc): '%s" RTPATH_SLASH_STR "%s'", rc, pszPath, pCfg->pszFilter);
998 }
999 rc = RTDirOpenFiltered(&hDir, pszPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
1000 }
1001 else
1002 rc = RTDirOpen(&hDir, pszPath);
1003 if (RT_FAILURE(rc))
1004 return RTMsgErrorRc(rc, "RTDirOpen%s failed on '%s': %Rrc", pCfg->pszFilter ? "Filtered" : "", pszPath, rc);
1005
1006 /*
1007 * Enumerate the files.
1008 */
1009 for (;;)
1010 {
1011 rc2 = RTDirReadEx(hDir, pDirEntry, NULL, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
1012 if (RT_FAILURE(rc2))
1013 {
1014 pszPath[cchPath] = '\0';
1015 if (rc2 != VERR_NO_MORE_FILES)
1016 {
1017 RTMsgError("RTDirReadEx failed in '%s': %Rrc\n", pszPath, rc2);
1018 rc = rc2;
1019 }
1020 break;
1021 }
1022
1023 /* Skip dot and dot-dot. */
1024 if (RTDirEntryExIsStdDotLink(pDirEntry))
1025 continue;
1026
1027 /* Construct a full path. */
1028 rc = RTStrCopy(&pszPath[cchPath], RTPATH_MAX, pDirEntry->szName);
1029 if (RT_FAILURE(rc))
1030 {
1031 pszPath[cchPath] = '\0';
1032 RTMsgError("File name too long in '%s': '%s' (%Rrc)", pszPath, pDirEntry->szName, rc);
1033 break;
1034 }
1035
1036 switch (rtDbgSymCacheFigureType2(pszPath, &pDirEntry->Info))
1037 {
1038 case RTDBGSYMCACHEFILETYPE_DIR:
1039 if (!pCfg->fRecursive)
1040 RTMsgInfo("Skipping directory '%s'...", pszPath);
1041 else
1042 {
1043 if (cchPath + pDirEntry->cbName + 3 <= RTPATH_MAX)
1044 {
1045 pszPath[cchPath + pDirEntry->cbName] = RTPATH_SLASH;
1046 pszPath[cchPath + pDirEntry->cbName + 1] = '\0';
1047 rc2 = rtDbgSymCacheAddDirWorker(pszPath, cchPath + pDirEntry->cbName + 1, pDirEntry, pCfg);
1048 }
1049 else
1050 {
1051 RTMsgError("File name too long in '%s': '%s' (%Rrc)", pszPath, pDirEntry->szName, rc);
1052 rc2 = VERR_FILENAME_TOO_LONG;
1053 }
1054 }
1055 break;
1056
1057 case RTDBGSYMCACHEFILETYPE_DEBUG_FILE:
1058 rc2 = rtDbgSymCacheAddDebugFile(pszPath, pDirEntry->szName, pCfg);
1059 break;
1060
1061 case RTDBGSYMCACHEFILETYPE_IMAGE_FILE:
1062 rc2 = rtDbgSymCacheAddImageFile(pszPath, pDirEntry->szName, NULL /*pszExtraSuff*/, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, pCfg);
1063 break;
1064
1065 case RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE:
1066 rc2 = rtDbgSymCacheAddDebugBundle(pszPath, cchPath, pDirEntry->cbName, NULL /*pszDstName*/, pCfg);
1067 break;
1068
1069 case RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE:
1070 rc2 = rtDbgSymCacheAddImageBundle(pszPath, cchPath, pDirEntry->cbName, NULL /*pszDstName*/, pDirEntry, pCfg);
1071 break;
1072
1073 case RTDBGSYMCACHEFILETYPE_DIR_FILTER:
1074 case RTDBGSYMCACHEFILETYPE_INVALID:
1075 rc2 = RTMsgErrorRc(VERR_INTERNAL_ERROR_2, "Invalid: '%s'", pszPath);
1076 break;
1077
1078 case RTDBGSYMCACHEFILETYPE_IGNORE:
1079 rc2 = VINF_SUCCESS;
1080 break;
1081 }
1082
1083 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1084 rc = rc2;
1085 }
1086
1087 /*
1088 * Clean up.
1089 */
1090 rc2 = RTDirClose(hDir);
1091 if (RT_FAILURE(rc2))
1092 {
1093 RTMsgError("RTDirClose failed in '%s': %Rrc", pszPath, rc);
1094 rc = rc2;
1095 }
1096 return rc;
1097}
1098
1099
1100/**
1101 * Adds a directory.
1102 *
1103 * @returns IPRT status code (fully bitched).
1104 * @param pszPath The directory path.
1105 * @param pCfg The configuration.
1106 */
1107static int rtDbgSymCacheAddDir(const char *pszPath, PCRTDBGSYMCACHEADDCFG pCfg)
1108{
1109 /*
1110 * Set up the path buffer, stripping any filter.
1111 */
1112 char szPath[RTPATH_MAX];
1113 int rc = RTStrCopy(szPath, sizeof(szPath) - 2, pszPath);
1114 if (RT_FAILURE(rc))
1115 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path too long: '%s'", pszPath);
1116
1117 size_t cchPath = strlen(pszPath);
1118 if (!cchPath)
1119 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path empty: '%s'", pszPath);
1120
1121 if (pCfg->pszFilter)
1122 szPath[cchPath - strlen(pCfg->pszFilter)] = '\0';
1123 cchPath = RTPathStripTrailingSlash(szPath);
1124 if (!RTPATH_IS_SEP(pszPath[cchPath - 1]))
1125 {
1126 szPath[cchPath++] = RTPATH_SLASH;
1127 szPath[cchPath] = '\0';
1128 }
1129
1130 /*
1131 * Let the worker do the rest.
1132 */
1133 RTDIRENTRYEX DirEntry;
1134 return rtDbgSymCacheAddDirWorker(szPath, cchPath, &DirEntry, pCfg);
1135}
1136
1137
1138/**
1139 * Adds a file or directory.
1140 *
1141 * @returns Program exit code.
1142 * @param pszPath The user supplied path to the file or directory.
1143 * @param pszCache The path to the cache.
1144 * @param fRecursive Whether to process directories recursively.
1145 * @param fOverwriteOnConflict Whether to overwrite existing cache entry on
1146 * conflict, or just leave it.
1147 */
1148static RTEXITCODE rtDbgSymCacheAddFileOrDir(const char *pszPath, const char *pszCache, bool fRecursive,
1149 bool fOverwriteOnConflict)
1150{
1151 RT_NOREF1(fOverwriteOnConflict);
1152 RTDBGSYMCACHEADDCFG Cfg;
1153 Cfg.fRecursive = fRecursive;
1154 Cfg.pszCache = pszCache;
1155 Cfg.pszFilter = NULL;
1156
1157 /* If the filename contains an equal ('=') char, treat the left as the file
1158 to add tne right part as the name to add it under (handy for kernels). */
1159 char *pszFree = NULL;
1160 const char *pszDstName = RTPathFilename(pszPath);
1161 const char *pszEqual = pszDstName ? strchr(pszDstName, '=') : NULL;
1162 if (pszEqual)
1163 {
1164 pszPath = pszFree = RTStrDupN(pszPath, pszEqual - pszPath);
1165 if (!pszFree)
1166 return RTMsgErrorExitFailure("out of memory!\n");
1167 pszDstName = pszEqual + 1;
1168 if (!*pszDstName)
1169 return RTMsgErrorExitFailure("add-as filename is empty!\n");
1170 }
1171
1172 int rc;
1173 RTDBGSYMCACHEFILETYPE enmType = rtDbgSymCacheFigureType(pszPath);
1174 switch (enmType)
1175 {
1176 default:
1177 case RTDBGSYMCACHEFILETYPE_INVALID:
1178 rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid: '%s'", pszPath);
1179 break;
1180
1181 case RTDBGSYMCACHEFILETYPE_DIR_FILTER:
1182 Cfg.pszFilter = RTPathFilename(pszPath);
1183 RT_FALL_THRU();
1184 case RTDBGSYMCACHEFILETYPE_DIR:
1185 if (!pszEqual)
1186 rc = rtDbgSymCacheAddDir(pszPath, &Cfg);
1187 else
1188 rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "Add-as filename is not applicable to directories!");
1189 break;
1190
1191 case RTDBGSYMCACHEFILETYPE_DEBUG_FILE:
1192 rc = rtDbgSymCacheAddDebugFile(pszPath, pszDstName, &Cfg);
1193 break;
1194
1195 case RTDBGSYMCACHEFILETYPE_IMAGE_FILE:
1196 rc = rtDbgSymCacheAddImageFile(pszPath, pszDstName, NULL /*pszExtraSuff*/, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, &Cfg);
1197 break;
1198
1199 case RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE:
1200 case RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE:
1201 {
1202 size_t cchPath = strlen(pszPath);
1203 size_t cchFilename = strlen(RTPathFilename(pszPath));
1204 char szPathBuf[RTPATH_MAX];
1205 if (cchPath < sizeof(szPathBuf))
1206 {
1207 memcpy(szPathBuf, pszPath, cchPath + 1);
1208 if (enmType == RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE)
1209 rc = rtDbgSymCacheAddDebugBundle(szPathBuf, cchPath - cchFilename, cchFilename,
1210 pszEqual ? pszDstName : NULL, &Cfg);
1211 else
1212 {
1213 RTDIRENTRYEX DirEntry;
1214 rc = rtDbgSymCacheAddImageBundle(szPathBuf, cchPath - cchFilename, cchFilename,
1215 pszEqual ? pszDstName : NULL, &DirEntry, &Cfg);
1216 }
1217 }
1218 else
1219 rc = RTMsgErrorRc(VERR_FILENAME_TOO_LONG, "Filename too long: '%s'", pszPath);
1220 break;
1221 }
1222
1223 case RTDBGSYMCACHEFILETYPE_IGNORE:
1224 rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid file: '%s'", pszPath);
1225 break;
1226 }
1227
1228 RTStrFree(pszFree);
1229 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1230}
1231
1232
1233/**
1234 * Handles the 'add' command.
1235 *
1236 * @returns Program exit code.
1237 * @param pszArg0 The program name.
1238 * @param cArgs The number of arguments to the 'add' command.
1239 * @param papszArgs The argument vector, starting after 'add'.
1240 */
1241static RTEXITCODE rtDbgSymCacheCmdAdd(const char *pszArg0, int cArgs, char **papszArgs)
1242{
1243 /*
1244 * Parse the command line.
1245 */
1246 static RTGETOPTDEF const s_aOptions[] =
1247 {
1248 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
1249 { "--no-recursive", 'n', RTGETOPT_REQ_NOTHING },
1250 { "--overwrite-on-conflict", 'o', RTGETOPT_REQ_NOTHING },
1251 };
1252
1253 const char *pszCache = NULL;
1254 bool fRecursive = false;
1255 bool fOverwriteOnConflict = false;
1256
1257 RTGETOPTSTATE State;
1258 int rc = RTGetOptInit(&State, cArgs, papszArgs, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1259 if (RT_FAILURE(rc))
1260 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc);
1261
1262 //uint32_t cAdded = 0;
1263 RTGETOPTUNION ValueUnion;
1264 int chOpt;
1265 while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0)
1266 {
1267 switch (chOpt)
1268 {
1269 case 'R':
1270 fRecursive = true;
1271 break;
1272
1273 case 'n':
1274 fRecursive = false;
1275 break;
1276
1277 case 'o':
1278 fOverwriteOnConflict = true;
1279 break;
1280
1281 case VINF_GETOPT_NOT_OPTION:
1282 /* The first non-option is a cache directory. */
1283 if (!pszCache)
1284 {
1285 pszCache = ValueUnion.psz;
1286 if (!RTPathExists(pszCache))
1287 {
1288 rc = RTDirCreate(pszCache, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
1289 if (RT_FAILURE(rc))
1290 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Error creating cache directory '%s': %Rrc", pszCache, rc);
1291 }
1292 else if (!RTDirExists(pszCache))
1293 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Specified cache directory is not a directory: '%s'", pszCache);
1294 }
1295 /* Subsequent non-options are files to be added to the cache. */
1296 else
1297 {
1298 RTEXITCODE rcExit = rtDbgSymCacheAddFileOrDir(ValueUnion.psz, pszCache, fRecursive, fOverwriteOnConflict);
1299 if (rcExit != RTEXITCODE_FAILURE)
1300 return rcExit;
1301 }
1302 break;
1303
1304 case 'h':
1305 return rtDbgSymCacheUsage(pszArg0, "add");
1306 case 'V':
1307 return rtDbgSymCacheVersion();
1308 default:
1309 return RTGetOptPrintError(chOpt, &ValueUnion);
1310 }
1311 }
1312
1313 if (!pszCache)
1314 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No cache directory or files to add were specified.");
1315 return RTEXITCODE_SUCCESS;
1316}
1317
1318
1319int main(int argc, char **argv)
1320{
1321 int rc = RTR3InitExe(argc, &argv, 0);
1322 if (RT_FAILURE(rc))
1323 return RTMsgInitFailure(rc);
1324
1325 /*
1326 * Switch on the command.
1327 */
1328 RTEXITCODE rcExit = RTEXITCODE_SYNTAX;
1329 if (argc < 2)
1330 rtDbgSymCacheUsage(argv[0], NULL);
1331 else if (!strcmp(argv[1], "add"))
1332 rcExit = rtDbgSymCacheCmdAdd(argv[0], argc - 2, argv + 2);
1333 else if ( !strcmp(argv[1], "-h")
1334 || !strcmp(argv[1], "-?")
1335 || !strcmp(argv[1], "--help"))
1336 rcExit = rtDbgSymCacheUsage(argv[0], NULL);
1337 else if ( !strcmp(argv[1], "-V")
1338 || !strcmp(argv[1], "--version"))
1339 rcExit = rtDbgSymCacheVersion();
1340 else
1341 RTMsgError("Unknown command: '%s'", argv[1]);
1342
1343 return rcExit;
1344}
1345
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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