VirtualBox

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

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

(C) 2016

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