VirtualBox

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

Last change on this file since 67979 was 62724, checked in by vboxsync, 8 years ago

IPRT/testcases: warnings

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