VirtualBox

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

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

baka fixes.

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

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