VirtualBox

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

Last change on this file since 80585 was 76589, checked in by vboxsync, 6 years ago

IPRT: Attempted to address some the more obvious shortcomings of RTPathCalcRelative. Had to add a parameter that clearifies whether the from path is a file (VHD usage) or directory (rest).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.1 KB
Line 
1/* $Id: RTDbgSymCache.cpp 76589 2019-01-01 08:56:07Z vboxsync $ */
2/** @file
3 * IPRT - Debug Symbol Cache Utility.
4 */
5
6/*
7 * Copyright (C) 2013-2019 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/err.h>
36#include <iprt/file.h>
37#include <iprt/formats/mach-o.h>
38#include <iprt/getopt.h>
39#include <iprt/initterm.h>
40#include <iprt/ldr.h>
41#include <iprt/message.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/stream.h>
45#include <iprt/string.h>
46#include <iprt/uuid.h>
47#include <iprt/vfs.h>
48#include <iprt/zip.h>
49
50
51/*********************************************************************************************************************************
52* Structures and Typedefs *
53*********************************************************************************************************************************/
54/**
55 * Cache file type.
56 */
57typedef enum RTDBGSYMCACHEFILETYPE
58{
59 RTDBGSYMCACHEFILETYPE_INVALID,
60 RTDBGSYMCACHEFILETYPE_DIR,
61 RTDBGSYMCACHEFILETYPE_DIR_FILTER,
62 RTDBGSYMCACHEFILETYPE_DEBUG_FILE,
63 RTDBGSYMCACHEFILETYPE_IMAGE_FILE,
64 RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE,
65 RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE,
66 RTDBGSYMCACHEFILETYPE_IGNORE
67} RTDBGSYMCACHEFILETYPE;
68
69
70/**
71 * Configuration for the 'add' command.
72 */
73typedef struct RTDBGSYMCACHEADDCFG
74{
75 bool fRecursive;
76 bool fOverwriteOnConflict;
77 const char *pszFilter;
78 const char *pszCache;
79} RTDBGSYMCACHEADDCFG;
80/** Pointer to a read only 'add' config. */
81typedef RTDBGSYMCACHEADDCFG const *PCRTDBGSYMCACHEADDCFG;
82
83
84/*********************************************************************************************************************************
85* Global Variables *
86*********************************************************************************************************************************/
87/** Bundle suffixes. */
88static const char * const g_apszBundleSuffixes[] =
89{
90 ".kext",
91 ".app",
92 ".framework", /** @todo framework is different. */
93 ".component",
94 ".action",
95 ".caction",
96 ".bundle",
97 ".sourcebundle",
98 ".plugin",
99 ".ppp",
100 ".menu",
101 ".monitorpanel",
102 ".scripting",
103 ".prefPane",
104 ".qlgenerator",
105 ".brailledriver",
106 ".saver",
107 ".SpeechVoice",
108 ".SpeechRecognizer",
109 ".SpeechSynthesizer",
110 ".mdimporter",
111 ".spreporter",
112 ".xpc",
113 NULL
114};
115
116/** Debug bundle suffixes. (Same as above + .dSYM) */
117static const char * const g_apszDSymBundleSuffixes[] =
118{
119 ".kext.dSYM",
120 ".app.dSYM",
121 ".framework.dSYM",
122 ".component.dSYM",
123 ".action.dSYM",
124 ".caction.dSYM",
125 ".bundle.dSYM",
126 ".sourcebundle.dSYM",
127 ".menu.dSYM",
128 ".plugin.dSYM",
129 ".ppp.dSYM",
130 ".monitorpanel.dSYM",
131 ".scripting.dSYM",
132 ".prefPane.dSYM",
133 ".qlgenerator.dSYM",
134 ".brailledriver.dSYM",
135 ".saver.dSYM",
136 ".SpeechVoice.dSYM",
137 ".SpeechRecognizer.dSYM",
138 ".SpeechSynthesizer.dSYM",
139 ".mdimporter.dSYM",
140 ".spreporter.dSYM",
141 ".xpc.dSYM",
142 ".dSYM",
143 NULL
144};
145
146
147/*********************************************************************************************************************************
148* Internal Functions *
149*********************************************************************************************************************************/
150static int rtDbgSymCacheAddDirWorker(char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg);
151
152
153
154/**
155 * Display the version of the cache program.
156 *
157 * @returns exit code.
158 */
159static RTEXITCODE rtDbgSymCacheVersion(void)
160{
161 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
162 return RTEXITCODE_SUCCESS;
163}
164
165
166/**
167 * Shows the usage of the cache program.
168 *
169 * @returns Exit code.
170 * @param pszArg0 Program name.
171 * @param pszCommand Command selector, NULL if all.
172 */
173static RTEXITCODE rtDbgSymCacheUsage(const char *pszArg0, const char *pszCommand)
174{
175 if (!pszCommand || !strcmp(pszCommand, "add"))
176 RTPrintf("Usage: %s add [-Rno] <cache-root-dir> <file1> [fileN..]\n", pszArg0);
177 return RTEXITCODE_SUCCESS;
178}
179
180
181/**
182 * Creates a UUID mapping for the file.
183 *
184 * @returns IPRT status code.
185 * @param pszCacheFile The path to the file in the cache.
186 * @param pFileUuid The UUID of the file.
187 * @param pszUuidMapDir The UUID map subdirectory in the cache, if this is
188 * wanted, otherwise NULL.
189 * @param pCfg The configuration.
190 */
191static int rtDbgSymCacheAddCreateUuidMapping(const char *pszCacheFile, PRTUUID pFileUuid,
192 const char *pszUuidMapDir, PCRTDBGSYMCACHEADDCFG pCfg)
193{
194 /*
195 * Create the UUID map entry first, deep.
196 */
197 char szMapPath[RTPATH_MAX];
198 int rc = RTPathJoin(szMapPath, sizeof(szMapPath) - sizeof("/xxxx/yyyy/xxxx/yyyy/xxxx/zzzzzzzzzzzz") + 1,
199 pCfg->pszCache, pszUuidMapDir);
200 if (RT_FAILURE(rc))
201 return RTMsgErrorRc(rc, "Error constructing UUID map path (RTPathJoin): %Rrc", rc);
202
203 size_t cch = strlen(szMapPath);
204 szMapPath[cch] = '-';
205
206 rc = RTUuidToStr(pFileUuid, &szMapPath[cch + 2], sizeof(szMapPath) - cch);
207 if (RT_FAILURE(rc))
208 return RTMsgErrorRc(rc, "Error constructing UUID map path (RTUuidToStr): %Rrc", rc);
209
210 /* Uppercase the whole lot. */
211 RTStrToUpper(&szMapPath[cch + 2]);
212
213 /* Split the first dword in two. */
214 szMapPath[cch + 1] = szMapPath[cch + 2];
215 szMapPath[cch + 2] = szMapPath[cch + 3];
216 szMapPath[cch + 3] = szMapPath[cch + 4];
217 szMapPath[cch + 4] = szMapPath[cch + 5];
218 szMapPath[cch + 5] = '-';
219
220 /*
221 * Create the directories in the path.
222 */
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, false /*fFromFile*/, 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 RT_NOREF_PV(pCfg);
579 return RTMsgErrorRc(VERR_NOT_IMPLEMENTED,
580 "'%s' is an OS X image file, did you point me to a file inside a .dSYM or .sym file?",
581 pszPath);
582}
583
584
585/**
586 * Worker for rtDbgSymCacheAddDebugFile that adds PDBs to the cace.
587 *
588 * @returns IPRT status code
589 * @param pszPath The path to the PDB file.
590 * @param pCfg The configuration.
591 * @param hFile Handle to the file.
592 */
593static int rtDbgSymCacheAddDebugPdb(const char *pszPath, PCRTDBGSYMCACHEADDCFG pCfg, RTFILE hFile)
594{
595 RT_NOREF2(pCfg, hFile);
596 return RTMsgErrorRc(VERR_NOT_IMPLEMENTED, "PDB support not implemented: '%s'", pszPath);
597}
598
599
600/**
601 * Adds a debug file to the cache.
602 *
603 * @returns IPRT status code
604 * @param pszPath The path to the debug file in question.
605 * @param pCfg The configuration.
606 */
607static int rtDbgSymCacheAddDebugFile(const char *pszPath, PCRTDBGSYMCACHEADDCFG pCfg)
608{
609 /*
610 * Need to extract an identifier of sorts here in order to put them in
611 * the right place in the cache. Currently only implemnted for Mach-O
612 * files since these use executable containers.
613 *
614 * We take a look at the file header in hope to figure out what to do
615 * with the file.
616 */
617 RTFILE hFile;
618 int rc = RTFileOpen(&hFile, pszPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
619 if (RT_FAILURE(rc))
620 return RTMsgErrorRc(rc, "Error opening '%s': %Rrc", pszPath, rc);
621
622 union
623 {
624 uint64_t au64[16];
625 uint32_t au32[16];
626 uint16_t au16[32];
627 uint8_t ab[64];
628 } uBuf;
629 rc = RTFileRead(hFile, &uBuf, sizeof(uBuf), NULL);
630 if (RT_SUCCESS(rc))
631 {
632 /*
633 * Look for magics and call workers.
634 */
635 if (!memcmp(uBuf.ab, RT_STR_TUPLE("Microsoft C/C++ MSF 7.00")))
636 rc = rtDbgSymCacheAddDebugPdb(pszPath, pCfg, hFile);
637 else if ( uBuf.au32[0] == IMAGE_FAT_SIGNATURE
638 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE_OE
639 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE
640 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE
641 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE_OE
642 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE_OE)
643 rc = rtDbgSymCacheAddDebugMachO(pszPath, pCfg);
644 else
645 rc = RTMsgErrorRc(VERR_INVALID_MAGIC, "Unsupported debug file '%s' magic: %#010x", pszPath, uBuf.au32[0]);
646 }
647 else
648 rc = RTMsgErrorRc(rc, "Error reading '%s': %Rrc", pszPath, rc);
649
650 /* close the file. */
651 int rc2 = RTFileClose(hFile);
652 if (RT_FAILURE(rc2))
653 {
654 RTMsgError("Error closing '%s': %Rrc", pszPath, rc2);
655 if (RT_SUCCESS(rc))
656 rc = rc2;
657 }
658 return rc;
659}
660
661
662/**
663 * Constructs the path to the file instide the bundle that we're keen on.
664 *
665 * @returns IPRT status code.
666 * @param pszPath Path to the bundle on input, on successful
667 * return it's the path to the desired file. This
668 * a RTPATH_MAX size buffer.
669 * @param cchPath The length of the path up to the bundle name.
670 * @param cchName The length of the bundle name.
671 * @param pszSubDir The bundle subdirectory the file lives in.
672 * @param papszSuffixes Pointer to an array of bundle suffixes.
673 */
674static int rtDbgSymCacheConstructBundlePath(char *pszPath, size_t cchPath, size_t cchName, const char *pszSubDir,
675 const char * const *papszSuffixes)
676{
677 /*
678 * Calc the name without the bundle extension.
679 */
680 size_t const cchOrgName = cchName;
681 const char *pszEnd = &pszPath[cchPath + cchName];
682 for (unsigned i = 0; papszSuffixes[i]; i++)
683 {
684 Assert(papszSuffixes[i][0] == '.');
685 size_t cchSuff = strlen(papszSuffixes[i]);
686 if ( cchSuff < cchName
687 && !memcmp(&pszEnd[-(ssize_t)cchSuff], papszSuffixes[i], cchSuff))
688 {
689 cchName -= cchSuff;
690 break;
691 }
692 }
693
694 /*
695 * Check the immediate directory first, in case it's layed out like
696 * IOPCIFamily.kext.
697 */
698 int rc = RTPathAppendEx(pszPath, RTPATH_MAX, &pszPath[cchPath], cchName);
699 if (RT_FAILURE(rc) || !RTFileExists(pszPath))
700 {
701 /*
702 * Not there, ok then try the given subdirectory + name.
703 */
704 pszPath[cchPath + cchOrgName] = '\0';
705 rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
706 if (RT_SUCCESS(rc))
707 rc = RTPathAppendEx(pszPath, RTPATH_MAX, &pszPath[cchPath], cchName);
708 if (RT_FAILURE(rc))
709 {
710 pszPath[cchPath + cchOrgName] = '\0';
711 return RTMsgErrorRc(rc, "Error constructing image bundle path for '%s': %Rrc", pszPath, rc);
712 }
713 }
714
715 return VINF_SUCCESS;
716}
717
718
719/**
720 * Adds a image bundle of some sort.
721 *
722 * @returns IPRT status code.
723 * @param pszPath Path to the bundle. This a RTPATH_MAX size
724 * buffer that we can write to when creating the
725 * path to the file inside the bundle that we're
726 * interested in.
727 * @param cchPath The length of the path up to the bundle name.
728 * @param cchName The length of the bundle name.
729 * @param pDirEntry The directory entry buffer, for handling bundle
730 * within bundle recursion.
731 * @param pCfg The configuration.
732 */
733static int rtDbgSymCacheAddImageBundle(char *pszPath, size_t cchPath, size_t cchName,
734 PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg)
735{
736 /*
737 * Assuming these are kexts or simple applications, we only add the image
738 * file itself to the cache. No Info.plist or other files.
739 */
740 /** @todo consider looking for Frameworks and handling framework bundles. */
741 int rc = rtDbgSymCacheConstructBundlePath(pszPath, cchPath, cchName, "Contents/MacOS/", g_apszBundleSuffixes);
742 if (RT_SUCCESS(rc))
743 rc = rtDbgSymCacheAddImageFile(pszPath, NULL, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, pCfg);
744
745 /*
746 * Look for plugins and other sub-bundles.
747 */
748 if (pCfg->fRecursive)
749 {
750 static char const * const s_apszSubBundleDirs[] =
751 {
752 "Contents/Plugins/",
753 /** @todo Frameworks ++ */
754 };
755 for (uint32_t i = 0; i < RT_ELEMENTS(s_apszSubBundleDirs); i++)
756 {
757 pszPath[cchPath + cchName] = '\0';
758 int rc2 = RTPathAppend(pszPath, RTPATH_MAX - 1, s_apszSubBundleDirs[i]);
759 if (RT_SUCCESS(rc2))
760 {
761 if (RTDirExists(pszPath))
762 {
763 size_t cchPath2 = strlen(pszPath);
764 if (!RTPATH_IS_SLASH(pszPath[cchPath2 - 1]))
765 {
766 pszPath[cchPath2++] = RTPATH_SLASH;
767 pszPath[cchPath2] = '\0';
768 }
769 rc2 = rtDbgSymCacheAddDirWorker(pszPath, cchPath2, pDirEntry, pCfg);
770 }
771 }
772 else
773 {
774 pszPath[cchPath + cchName] = '\0';
775 RTMsgError("Error constructing bundle subdir path for '%s' + '%s': %Rrc", pszPath, s_apszSubBundleDirs[i], rc);
776 }
777 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
778 rc = rc2;
779 }
780 }
781
782 return rc;
783}
784
785
786/**
787 * Adds a debug bundle.
788 *
789 * @returns IPRT status code.
790 * @param pszPath Path to the bundle. This a RTPATH_MAX size
791 * buffer that we can write to when creating the
792 * path to the file inside the bundle that we're
793 * interested in.
794 * @param cchPath The length of the path up to the bundle name.
795 * @param cchName The length of the bundle name.
796 * @param pCfg The configuration.
797 */
798static int rtDbgSymCacheAddDebugBundle(char *pszPath, size_t cchPath, size_t cchName, PCRTDBGSYMCACHEADDCFG pCfg)
799{
800 /*
801 * The current policy is not to add the whole .dSYM (or .sym) bundle, but
802 * rather just the dwarf image instide it. The <UUID>.plist and Info.plist
803 * files generally doesn't contain much extra information that's really
804 * necessary, I hope. At least this is what the uuidmap example in the
805 * lldb hints at (it links to the dwarf file, not the .dSYM dir).
806 *
807 * To avoid confusion with a .dSYM bundle, as well as collision with the
808 * image file, we use .dwarf suffix for the file.
809 *
810 * For details on the uuid map see rtDbgSymCacheAddImageFile as well as
811 * http://lldb.llvm.org/symbols.html .
812 *
813 * ASSUMES bundles contains Mach-O DWARF files.
814 */
815 int rc = rtDbgSymCacheConstructBundlePath(pszPath, cchPath, cchName, "Contents/Resources/DWARF/", g_apszDSymBundleSuffixes);
816 if (RT_SUCCESS(rc))
817 rc = rtDbgSymCacheAddImageFile(pszPath, RTDBG_CACHE_DSYM_FILE_SUFFIX, RTDBG_CACHE_UUID_MAP_DIR_DSYMS, pCfg);
818 return rc;
819}
820
821
822/**
823 * Figure the type of a file/dir based on path and FS object info.
824 *
825 * @returns The type.
826 * @param pszPath The path to the file/dir.
827 * @param pObjInfo The object information, symlinks followed.
828 */
829static RTDBGSYMCACHEFILETYPE rtDbgSymCacheFigureType2(const char *pszPath, PCRTFSOBJINFO pObjInfo)
830{
831 const char *pszName = RTPathFilename(pszPath);
832 const char *pszExt = RTPathSuffix(pszName);
833 if (pszExt)
834 pszExt++;
835 else
836 pszExt = "";
837
838 if ( RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode)
839 || (pObjInfo->Attr.fMode & RTFS_DOS_DIRECTORY)) /** @todo OS X samba reports reparse points in /Volumes/ that we cannot resolve. */
840 {
841 /* Skip directories shouldn't bother with. */
842 if ( !RTStrICmp(pszName, ".Trashes")
843 || !RTStrICmp(pszName, ".$RESCYCLE.BIN")
844 || !RTStrICmp(pszName, "System.kext") /* Usually only plugins here, so skip it. */
845 )
846 return RTDBGSYMCACHEFILETYPE_IGNORE;
847
848 /* Directories can also be bundles on the mac. */
849 if (!RTStrICmp(pszExt, "dSYM"))
850 return RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE;
851
852 for (unsigned i = 0; i < RT_ELEMENTS(g_apszBundleSuffixes) - 1; i++)
853 if (!RTStrICmp(pszExt, &g_apszBundleSuffixes[i][1]))
854 return RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE;
855
856 return RTDBGSYMCACHEFILETYPE_DIR;
857 }
858
859 if (!RTFS_IS_FILE(pObjInfo->Attr.fMode))
860 return RTDBGSYMCACHEFILETYPE_INVALID;
861
862 /* Select image vs debug info based on extension. */
863 if ( !RTStrICmp(pszExt, "pdb")
864 || !RTStrICmp(pszExt, "dbg")
865 || !RTStrICmp(pszExt, "sym")
866 || !RTStrICmp(pszExt, "dwo")
867 || !RTStrICmp(pszExt, "dwp")
868 || !RTStrICmp(pszExt, "debug")
869 || !RTStrICmp(pszExt, "dsym")
870 || !RTStrICmp(pszExt, "dwarf")
871 || !RTStrICmp(pszExt, "map")
872 || !RTStrICmp(pszExt, "cv"))
873 return RTDBGSYMCACHEFILETYPE_DEBUG_FILE;
874
875 /* Filter out a bunch of files which obviously shouldn't be images. */
876 if ( !RTStrICmp(pszExt, "txt")
877 || !RTStrICmp(pszExt, "html")
878 || !RTStrICmp(pszExt, "htm")
879 || !RTStrICmp(pszExt, "rtf")
880 || !RTStrICmp(pszExt, "zip")
881 || !RTStrICmp(pszExt, "doc")
882 || !RTStrICmp(pszExt, "gz")
883 || !RTStrICmp(pszExt, "bz2")
884 || !RTStrICmp(pszExt, "xz")
885 || !RTStrICmp(pszExt, "kmk")
886 || !RTStrICmp(pszExt, "c")
887 || !RTStrICmp(pszExt, "cpp")
888 || !RTStrICmp(pszExt, "h")
889 || !RTStrICmp(pszExt, "m")
890 || !RTStrICmp(pszExt, "mm")
891 || !RTStrICmp(pszExt, "asm")
892 || !RTStrICmp(pszExt, "S")
893 || !RTStrICmp(pszExt, "inc")
894 || !RTStrICmp(pszExt, "sh")
895 )
896 return RTDBGSYMCACHEFILETYPE_IGNORE;
897 if ( !RTStrICmp(pszName, "Makefile")
898 || !RTStrICmp(pszName, "GNUmakefile")
899 || !RTStrICmp(pszName, "createsymbolfiles")
900 || !RTStrICmp(pszName, "kgmacros")
901 )
902 return RTDBGSYMCACHEFILETYPE_IGNORE;
903
904 return RTDBGSYMCACHEFILETYPE_IMAGE_FILE;
905}
906
907
908/**
909 * Figure file type based on name, will stat the file/dir.
910 *
911 * @returns File type.
912 * @param pszPath The path to the file/dir to figure.
913 */
914static RTDBGSYMCACHEFILETYPE rtDbgSymCacheFigureType(const char *pszPath)
915{
916 const char *pszName = RTPathFilename(pszPath);
917
918 /* Trailing slash. */
919 if (!pszName)
920 return RTDBGSYMCACHEFILETYPE_DIR;
921
922 /* Wildcard means listing directory and filtering. */
923 if (strpbrk(pszName, "?*"))
924 return RTDBGSYMCACHEFILETYPE_DIR_FILTER;
925
926 /* Get object info, following links. */
927 RTFSOBJINFO ObjInfo;
928 int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
929 if (RT_FAILURE(rc))
930 return RTDBGSYMCACHEFILETYPE_INVALID;
931 return rtDbgSymCacheFigureType2(pszPath, &ObjInfo);
932}
933
934
935/**
936 * Recursive worker for rtDbgSymCacheAddDir, for minimal stack wasting.
937 *
938 * @returns IPRT status code (fully bitched).
939 * @param pszPath Pointer to a RTPATH_MAX size buffer containing
940 * the path to the current directory ending with a
941 * slash.
942 * @param cchPath The size of the current directory path.
943 * @param pDirEntry Pointer to the RTDIRENTRYEX structure to use.
944 * @param pCfg The configuration.
945 */
946static int rtDbgSymCacheAddDirWorker(char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg)
947{
948 /*
949 * Open the directory.
950 */
951 RTDIR hDir;
952 int rc, rc2;
953 if (pCfg->pszFilter)
954 {
955 rc = RTStrCopy(&pszPath[cchPath], RTPATH_MAX - cchPath, pCfg->pszFilter);
956 if (RT_FAILURE(rc))
957 {
958 pszPath[cchPath] = '\0';
959 return RTMsgErrorRc(rc, "Filename too long (%Rrc): '%s" RTPATH_SLASH_STR "%s'", rc, pszPath, pCfg->pszFilter);
960 }
961 rc = RTDirOpenFiltered(&hDir, pszPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
962 }
963 else
964 rc = RTDirOpen(&hDir, pszPath);
965 if (RT_FAILURE(rc))
966 return RTMsgErrorRc(rc, "RTDirOpen%s failed on '%s': %Rrc", pCfg->pszFilter ? "Filtered" : "", pszPath, rc);
967
968 /*
969 * Enumerate the files.
970 */
971 for (;;)
972 {
973 rc2 = RTDirReadEx(hDir, pDirEntry, NULL, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
974 if (RT_FAILURE(rc2))
975 {
976 pszPath[cchPath] = '\0';
977 if (rc2 != VERR_NO_MORE_FILES)
978 {
979 RTMsgError("RTDirReadEx failed in '%s': %Rrc\n", pszPath, rc2);
980 rc = rc2;
981 }
982 break;
983 }
984
985 /* Skip dot and dot-dot. */
986 if (RTDirEntryExIsStdDotLink(pDirEntry))
987 continue;
988
989 /* Construct a full path. */
990 rc = RTStrCopy(&pszPath[cchPath], RTPATH_MAX, pDirEntry->szName);
991 if (RT_FAILURE(rc))
992 {
993 pszPath[cchPath] = '\0';
994 RTMsgError("File name too long in '%s': '%s' (%Rrc)", pszPath, pDirEntry->szName, rc);
995 break;
996 }
997
998 switch (rtDbgSymCacheFigureType2(pszPath, &pDirEntry->Info))
999 {
1000 case RTDBGSYMCACHEFILETYPE_DIR:
1001 if (!pCfg->fRecursive)
1002 RTMsgInfo("Skipping directory '%s'...", pszPath);
1003 else
1004 {
1005 if (cchPath + pDirEntry->cbName + 3 <= RTPATH_MAX)
1006 {
1007 pszPath[cchPath + pDirEntry->cbName] = RTPATH_SLASH;
1008 pszPath[cchPath + pDirEntry->cbName + 1] = '\0';
1009 rc2 = rtDbgSymCacheAddDirWorker(pszPath, cchPath + pDirEntry->cbName + 1, pDirEntry, pCfg);
1010 }
1011 else
1012 {
1013 RTMsgError("File name too long in '%s': '%s' (%Rrc)", pszPath, pDirEntry->szName, rc);
1014 rc2 = VERR_FILENAME_TOO_LONG;
1015 }
1016 }
1017 break;
1018
1019 case RTDBGSYMCACHEFILETYPE_DEBUG_FILE:
1020 rc2 = rtDbgSymCacheAddDebugFile(pszPath, pCfg);
1021 break;
1022
1023 case RTDBGSYMCACHEFILETYPE_IMAGE_FILE:
1024 rc2 = rtDbgSymCacheAddImageFile(pszPath, NULL /*pszExtraSuff*/, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, pCfg);
1025 break;
1026
1027 case RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE:
1028 rc2 = rtDbgSymCacheAddDebugBundle(pszPath, cchPath, pDirEntry->cbName, pCfg);
1029 break;
1030
1031 case RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE:
1032 rc2 = rtDbgSymCacheAddImageBundle(pszPath, cchPath, pDirEntry->cbName, pDirEntry, pCfg);
1033 break;
1034
1035 case RTDBGSYMCACHEFILETYPE_DIR_FILTER:
1036 case RTDBGSYMCACHEFILETYPE_INVALID:
1037 rc2 = RTMsgErrorRc(VERR_INTERNAL_ERROR_2, "Invalid: '%s'", pszPath);
1038 break;
1039
1040 case RTDBGSYMCACHEFILETYPE_IGNORE:
1041 rc2 = VINF_SUCCESS;
1042 break;
1043 }
1044
1045 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1046 rc = rc2;
1047 }
1048
1049 /*
1050 * Clean up.
1051 */
1052 rc2 = RTDirClose(hDir);
1053 if (RT_FAILURE(rc2))
1054 {
1055 RTMsgError("RTDirClose failed in '%s': %Rrc", pszPath, rc);
1056 rc = rc2;
1057 }
1058 return rc;
1059}
1060
1061
1062/**
1063 * Adds a directory.
1064 *
1065 * @returns IPRT status code (fully bitched).
1066 * @param pszPath The directory path.
1067 * @param pCfg The configuration.
1068 */
1069static int rtDbgSymCacheAddDir(const char *pszPath, PCRTDBGSYMCACHEADDCFG pCfg)
1070{
1071 /*
1072 * Set up the path buffer, stripping any filter.
1073 */
1074 char szPath[RTPATH_MAX];
1075 int rc = RTStrCopy(szPath, sizeof(szPath) - 2, pszPath);
1076 if (RT_FAILURE(rc))
1077 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path too long: '%s'", pszPath);
1078
1079 size_t cchPath = strlen(pszPath);
1080 if (!cchPath)
1081 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path empty: '%s'", pszPath);
1082
1083 if (pCfg->pszFilter)
1084 szPath[cchPath - strlen(pCfg->pszFilter)] = '\0';
1085 cchPath = RTPathStripTrailingSlash(szPath);
1086 if (!RTPATH_IS_SEP(pszPath[cchPath - 1]))
1087 {
1088 szPath[cchPath++] = RTPATH_SLASH;
1089 szPath[cchPath] = '\0';
1090 }
1091
1092 /*
1093 * Let the worker do the rest.
1094 */
1095 RTDIRENTRYEX DirEntry;
1096 return rtDbgSymCacheAddDirWorker(szPath, cchPath, &DirEntry, pCfg);
1097}
1098
1099
1100/**
1101 * Adds a file or directory.
1102 *
1103 * @returns Program exit code.
1104 * @param pszPath The user supplied path to the file or directory.
1105 * @param pszCache The path to the cache.
1106 * @param fRecursive Whether to process directories recursively.
1107 * @param fOverwriteOnConflict Whether to overwrite existing cache entry on
1108 * conflict, or just leave it.
1109 */
1110static RTEXITCODE rtDbgSymCacheAddFileOrDir(const char *pszPath, const char *pszCache, bool fRecursive,
1111 bool fOverwriteOnConflict)
1112{
1113 RT_NOREF1(fOverwriteOnConflict);
1114 RTDBGSYMCACHEADDCFG Cfg;
1115 Cfg.fRecursive = fRecursive;
1116 Cfg.pszCache = pszCache;
1117 Cfg.pszFilter = NULL;
1118
1119 int rc;
1120 RTDBGSYMCACHEFILETYPE enmType = rtDbgSymCacheFigureType(pszPath);
1121 switch (enmType)
1122 {
1123 default:
1124 case RTDBGSYMCACHEFILETYPE_INVALID:
1125 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid: '%s'", pszPath);
1126
1127 case RTDBGSYMCACHEFILETYPE_DIR_FILTER:
1128 Cfg.pszFilter = RTPathFilename(pszPath);
1129 RT_FALL_THRU();
1130 case RTDBGSYMCACHEFILETYPE_DIR:
1131 rc = rtDbgSymCacheAddDir(pszPath, &Cfg);
1132 break;
1133
1134 case RTDBGSYMCACHEFILETYPE_DEBUG_FILE:
1135 rc = rtDbgSymCacheAddDebugFile(pszPath, &Cfg);
1136 break;
1137
1138 case RTDBGSYMCACHEFILETYPE_IMAGE_FILE:
1139 rc = rtDbgSymCacheAddImageFile(pszPath, NULL /*pszExtraSuff*/, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, &Cfg);
1140 break;
1141
1142 case RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE:
1143 case RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE:
1144 {
1145 size_t cchPath = strlen(pszPath);
1146 size_t cchFilename = strlen(RTPathFilename(pszPath));
1147 char szPathBuf[RTPATH_MAX];
1148 if (cchPath < sizeof(szPathBuf))
1149 {
1150 memcpy(szPathBuf, pszPath, cchPath + 1);
1151 if (enmType == RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE)
1152 rc = rtDbgSymCacheAddDebugBundle(szPathBuf, cchPath - cchFilename, cchFilename, &Cfg);
1153 else
1154 {
1155 RTDIRENTRYEX DirEntry;
1156 rc = rtDbgSymCacheAddImageBundle(szPathBuf, cchPath - cchFilename, cchFilename, &DirEntry, &Cfg);
1157 }
1158 }
1159 else
1160 rc = RTMsgErrorRc(VERR_FILENAME_TOO_LONG, "Filename too long: '%s'", pszPath);
1161 break;
1162 }
1163
1164 case RTDBGSYMCACHEFILETYPE_IGNORE:
1165 rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid file: '%s'", pszPath);
1166 break;
1167 }
1168 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1169}
1170
1171
1172/**
1173 * Handles the 'add' command.
1174 *
1175 * @returns Program exit code.
1176 * @param pszArg0 The program name.
1177 * @param cArgs The number of arguments to the 'add' command.
1178 * @param papszArgs The argument vector, starting after 'add'.
1179 */
1180static RTEXITCODE rtDbgSymCacheCmdAdd(const char *pszArg0, int cArgs, char **papszArgs)
1181{
1182 /*
1183 * Parse the command line.
1184 */
1185 static RTGETOPTDEF const s_aOptions[] =
1186 {
1187 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
1188 { "--no-recursive", 'n', RTGETOPT_REQ_NOTHING },
1189 { "--overwrite-on-conflict", 'o', RTGETOPT_REQ_NOTHING },
1190 };
1191
1192 const char *pszCache = NULL;
1193 bool fRecursive = false;
1194 bool fOverwriteOnConflict = false;
1195
1196 RTGETOPTSTATE State;
1197 int rc = RTGetOptInit(&State, cArgs, papszArgs, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1198 if (RT_FAILURE(rc))
1199 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc);
1200
1201 //uint32_t cAdded = 0;
1202 RTGETOPTUNION ValueUnion;
1203 int chOpt;
1204 while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0)
1205 {
1206 switch (chOpt)
1207 {
1208 case 'R':
1209 fRecursive = true;
1210 break;
1211
1212 case 'n':
1213 fRecursive = false;
1214 break;
1215
1216 case 'o':
1217 fOverwriteOnConflict = true;
1218 break;
1219
1220 case VINF_GETOPT_NOT_OPTION:
1221 /* The first non-option is a cache directory. */
1222 if (!pszCache)
1223 {
1224 pszCache = ValueUnion.psz;
1225 if (!RTPathExists(pszCache))
1226 {
1227 rc = RTDirCreate(pszCache, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
1228 if (RT_FAILURE(rc))
1229 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Error creating cache directory '%s': %Rrc", pszCache, rc);
1230 }
1231 else if (!RTDirExists(pszCache))
1232 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Specified cache directory is not a directory: '%s'", pszCache);
1233 }
1234 /* Subsequent non-options are files to be added to the cache. */
1235 else
1236 {
1237 RTEXITCODE rcExit = rtDbgSymCacheAddFileOrDir(ValueUnion.psz, pszCache, fRecursive, fOverwriteOnConflict);
1238 if (rcExit != RTEXITCODE_FAILURE)
1239 return rcExit;
1240 }
1241 break;
1242
1243 case 'h':
1244 return rtDbgSymCacheUsage(pszArg0, "add");
1245 case 'V':
1246 return rtDbgSymCacheVersion();
1247 default:
1248 return RTGetOptPrintError(chOpt, &ValueUnion);
1249 }
1250 }
1251
1252 if (!pszCache)
1253 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No cache directory or files to add were specified.");
1254 return RTEXITCODE_SUCCESS;
1255}
1256
1257
1258int main(int argc, char **argv)
1259{
1260 int rc = RTR3InitExe(argc, &argv, 0);
1261 if (RT_FAILURE(rc))
1262 return RTMsgInitFailure(rc);
1263
1264 /*
1265 * Switch on the command.
1266 */
1267 RTEXITCODE rcExit = RTEXITCODE_SYNTAX;
1268 if (argc < 2)
1269 rtDbgSymCacheUsage(argv[0], NULL);
1270 else if (!strcmp(argv[1], "add"))
1271 rcExit = rtDbgSymCacheCmdAdd(argv[0], argc - 2, argv + 2);
1272 else if ( !strcmp(argv[1], "-h")
1273 || !strcmp(argv[1], "-?")
1274 || !strcmp(argv[1], "--help"))
1275 rcExit = rtDbgSymCacheUsage(argv[0], NULL);
1276 else if ( !strcmp(argv[1], "-V")
1277 || !strcmp(argv[1], "--version"))
1278 rcExit = rtDbgSymCacheVersion();
1279 else
1280 RTMsgError("Unknown command: '%s'", argv[1]);
1281
1282 return rcExit;
1283}
1284
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