drfront_status_t drfront_set_client_symbol_search_path(const char *symdir, bool ignore_env, OUT char *symsrv_path, size_t symsrv_path_sz) { char app_symsrv_path[MAX_SYMSRV_PATH]; TCHAR wapp_symsrv_path[MAX_SYMSRV_PATH]; char tmp_srv_path[MAX_SYMSRV_PATH]; char tmp_symsrv_path[MAX_SYMSRV_PATH]; char *cur; char *end; size_t sofar; ssize_t len; bool has_srv; bool dir_exists; bool has_ms_symsrv; drfront_status_t sc; static const char ms_symsrv[] = "http://msdl.microsoft.com/download/symbols"; if (sym_set_path_func == NULL) return DRFRONT_ERROR_LIB_UNSUPPORTED; /* If the user set a non-empty _NT_SYMBOL_PATH, then we use that. * If not, we use symdir/symbols path and make sure it exists. */ if (ignore_env || drfront_get_env_var("_NT_SYMBOL_PATH", tmp_symsrv_path, BUFFER_SIZE_ELEMENTS(tmp_symsrv_path)) != DRFRONT_SUCCESS || strlen(tmp_symsrv_path) == 0) { char pdb_dir[MAXIMUM_PATH]; _snprintf(pdb_dir, BUFFER_SIZE_ELEMENTS(pdb_dir), "%s/symbols", symdir); NULL_TERMINATE_BUFFER(pdb_dir); drfront_string_replace_character(pdb_dir, '/', '\\'); /* canonicalize */ sc = drfront_create_dir(pdb_dir); if ((sc != DRFRONT_SUCCESS && sc != DRFRONT_ERROR_FILE_EXISTS) || drfront_access(pdb_dir, DRFRONT_READ, &dir_exists) != DRFRONT_SUCCESS || !dir_exists) { DO_DEBUG(DL_WARN, printf("Failed to create directory for symbols: %s\n", pdb_dir); );
int _tmain(int argc, TCHAR *targv[]) { char **argv; char *process = NULL; char *dr_root = NULL; char *drheapstat_root = NULL; char default_dr_root[MAXIMUM_PATH]; char default_drheapstat_root[MAXIMUM_PATH]; char client_path[MAXIMUM_PATH]; char client_ops[MAX_DR_CMDLINE]; size_t cliops_sofar = 0; /* for BUFPRINT to client_ops */ char dr_ops[MAX_DR_CMDLINE]; size_t drops_sofar = 0; /* for BUFPRINT to dr_ops */ ssize_t len; /* shared by all BUFPRINT */ char logdir[MAXIMUM_PATH]; bool have_logdir = false; bool use_root_for_logdir; #ifdef WINDOWS char *pidfile = NULL; #endif #ifndef MACOS /* XXX i#1286: implement nudge on MacOS */ process_id_t nudge_pid = 0; #endif bool use_dr_debug = false; bool use_drheapstat_debug = false; char *app_name; char full_app_name[MAXIMUM_PATH]; char **app_argv; int errcode; void *inject_data; int i; char *c; char buf[MAXIMUM_PATH]; process_id_t pid; bool exit0 = false; bool dr_logdir_specified = false; bool doubledash_present = false; drfront_status_t sc; bool res; #ifndef X64 bool is64, is32; #endif dr_standalone_init(); #if defined(WINDOWS) && !defined(_UNICODE) # error _UNICODE must be defined #else /* Convert to UTF-8 if necessary */ sc = drfront_convert_args((const TCHAR **)targv, &argv, argc); if (sc != DRFRONT_SUCCESS) fatal("failed to process args: %d\n", sc); #endif /* Default root: we assume this exe is <root>/bin/drheapstat.exe */ get_full_path(argv[0], buf, BUFFER_SIZE_ELEMENTS(buf)); c = buf + strlen(buf) - 1; while (*c != DIRSEP && *c != ALT_DIRSEP && c > buf) c--; _snprintf(c+1, BUFFER_SIZE_ELEMENTS(buf) - (c+1-buf), "../dynamorio"); NULL_TERMINATE_BUFFER(buf); get_absolute_path(buf, default_dr_root, BUFFER_SIZE_ELEMENTS(default_dr_root)); NULL_TERMINATE_BUFFER(default_dr_root); dr_root = default_dr_root; /* assuming we're in bin/ (mainly due to CPack NSIS limitations) */ _snprintf(c+1, BUFFER_SIZE_ELEMENTS(buf) - (c+1-buf), ".."); NULL_TERMINATE_BUFFER(buf); get_absolute_path(buf, default_drheapstat_root, BUFFER_SIZE_ELEMENTS(default_drheapstat_root)); NULL_TERMINATE_BUFFER(default_drheapstat_root); drheapstat_root = default_drheapstat_root; string_replace_character(drheapstat_root, ALT_DIRSEP, DIRSEP); /* canonicalize */ BUFPRINT(dr_ops, BUFFER_SIZE_ELEMENTS(dr_ops), drops_sofar, len, "%s ", DEFAULT_DR_OPS); client_ops[0] = '\0'; /* default logdir */ if (drfront_appdata_logdir(drheapstat_root, "Dr. Heapstat", &use_root_for_logdir, logdir, BUFFER_SIZE_ELEMENTS(logdir)) == DRFRONT_SUCCESS && !use_root_for_logdir) { if ((dr_create_dir(logdir) || dr_directory_exists(logdir)) && file_is_writable(logdir)) have_logdir = true; } if (!have_logdir) { _snprintf(logdir, BUFFER_SIZE_ELEMENTS(logdir), "%s%cdrheapstat%clogs", drheapstat_root, DIRSEP, DIRSEP); NULL_TERMINATE_BUFFER(logdir); if (!file_is_writable(logdir)) { _snprintf(logdir, BUFFER_SIZE_ELEMENTS(logdir), "%s%clogs", drheapstat_root, DIRSEP); NULL_TERMINATE_BUFFER(logdir); if (file_is_writable(logdir)) have_logdir = true; } else have_logdir = true; } else have_logdir = true; if (!have_logdir) { /* try logs in cur dir */ get_absolute_path("./logs", logdir, BUFFER_SIZE_ELEMENTS(logdir)); NULL_TERMINATE_BUFFER(logdir); if (!file_is_writable(logdir)) { /* try cur dir */ get_absolute_path(".", logdir, BUFFER_SIZE_ELEMENTS(logdir)); NULL_TERMINATE_BUFFER(logdir); } } /* parse command line */ /* FIXME PR 487993: use optionsx.h to construct this parsing code */ for (i=1; i<argc; i++) { if (strcmp(argv[i], "--") == 0) { doubledash_present = true; break; } } for (i=1; i<argc; i++) { /* note that we pass unknown args to client, until -- */ if (strcmp(argv[i], "--") == 0) { i++; break; } /* drag-and-drop does not include "--" so we try to identify the app. * we explicitly parse -logdir and -suppress, and all the other * client ops that take args take numbers so this should be safe. */ else if (argv[i][0] != '-' && !doubledash_present && ends_in_exe(argv[i])) { /* leave i alone: this is the app itself */ break; } else if (strcmp(argv[i], "-v") == 0) { verbose = true; continue; } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { print_usage(true/*full*/); exit(0); } else if (strcmp(argv[i], "-dr_debug") == 0) { use_dr_debug = true; continue; } else if (strcmp(argv[i], "-dr_release") == 0) { use_dr_debug = false; continue; } else if (strcmp(argv[i], "-debug") == 0) { use_drheapstat_debug = true; continue; } else if (strcmp(argv[i], "-release") == 0) { use_drheapstat_debug = false; continue; } else if (!strcmp(argv[i], "-version")) { #if defined(BUILD_NUMBER) && defined(VERSION_NUMBER) printf("Dr. Heapstat version %s -- build %d\n", VERSION_STRING, BUILD_NUMBER); #elif defined(BUILD_NUMBER) printf("Dr. Heapstat custom build %d -- %s\n", BUILD_NUMBER, __DATE__); #elif defined(VERSION_NUMBER) printf("Dr. Heapstat version %s -- custom build %s, %s\n", VERSION_STRING, __DATE__, __TIME__); #else printf("Dr. Heapstat custom build -- %s, %s\n", __DATE__, __TIME__); #endif exit(0); } else if (strcmp(argv[i], "-dr") == 0) { if (i >= argc - 1) usage("invalid arguments"); dr_root = argv[++i]; } else if (strcmp(argv[i], "-drheapstat") == 0) { if (i >= argc - 1) usage("invalid arguments"); drheapstat_root = argv[++i]; } else if (strcmp(argv[i], "-follow_children") == 0 || strcmp(argv[i], "-no_follow_children") == 0) { BUFPRINT(dr_ops, BUFFER_SIZE_ELEMENTS(dr_ops), drops_sofar, len, "%s ", argv[i]); } #ifndef MACOS /* XXX i#1286: implement nudge on MacOS */ else if (strcmp(argv[i], "-nudge") == 0) { if (i >= argc - 1) usage("invalid arguments"); nudge_pid = strtoul(argv[++i], NULL, 10); } #endif else if (strcmp(argv[i], "-dr_ops") == 0) { if (i >= argc - 1) usage("invalid arguments"); /* Slight risk of some other option containing "-logdir " but * -dr_ops is really only be used by Dr. Memory developers anyway. */ if (strstr(argv[i+1], "-logdir ") != NULL) dr_logdir_specified = true; BUFPRINT(dr_ops, BUFFER_SIZE_ELEMENTS(dr_ops), drops_sofar, len, "%s ", argv[++i]); } #ifdef WINDOWS else if (strcmp(argv[i], "-pid_file") == 0) { if (i >= argc - 1) usage("invalid arguments"); pidfile = argv[++i]; } #endif else if (strcmp(argv[i], "-logdir") == 0) { if (i >= argc - 1) usage("invalid arguments"); /* make absolute */ get_absolute_path(argv[++i], logdir, BUFFER_SIZE_ELEMENTS(logdir)); NULL_TERMINATE_BUFFER(logdir); /* added to client ops below */ } else if (strcmp(argv[i], "-exit0") == 0) { exit0 = true; } /* FIXME i#1653: add support for options the old perl frontend supported */ else { /* pass to client */ BUFPRINT(client_ops, BUFFER_SIZE_ELEMENTS(client_ops), cliops_sofar, len, "`%s` ", argv[i]); } } #ifndef MACOS /* XXX i#1286: implement nudge on MacOS */ if (nudge_pid != 0) { if (i < argc) usage("%s", "-nudge does not take an app to run"); /* could also complain about other client or app specific ops */ res = dr_nudge_pid(nudge_pid, CLIENT_ID, NUDGE_LEAK_SCAN, INFINITE); if (res != DR_SUCCESS) { fatal("error nudging %d%s", nudge_pid, (res == DR_NUDGE_PID_NOT_INJECTED) ? ": no such Dr. Heapstat process" : ""); assert(false); /* shouldn't get here */ } exit(0); } #endif if (i >= argc) usage("%s", "no app specified"); app_name = argv[i]; get_full_path(app_name, full_app_name, BUFFER_SIZE_ELEMENTS(full_app_name)); if (full_app_name[0] != '\0') app_name = full_app_name; info("targeting application: \"%s\"", app_name); /* XXX: See drmemory/frontend.c for notes about our argv processing. */ app_argv = &argv[i]; if (verbose) { int j; c = buf; for (j = 0; app_argv[j] != NULL; j++) { c += _snprintf(c, BUFFER_SIZE_ELEMENTS(buf) - (c - buf), "\"%s\" ", app_argv[j]); } NULL_TERMINATE_BUFFER(buf); assert(c - buf < BUFFER_SIZE_ELEMENTS(buf)); info("app cmdline: %s", buf); } #ifndef X64 /* XXX: See drmemory/frontend.c for notes about 64-bit support. */ if (drfront_is_64bit_app(app_name, &is64, &is32) == DRFRONT_SUCCESS && is64 && !is32) { fatal("This Dr. Heapstat release does not support 64-bit applications."); goto error; /* actually won't get here */ } #endif if (!file_is_readable(dr_root)) { fatal("invalid -dr %s", dr_root); goto error; /* actually won't get here */ } if (dr_root != default_dr_root) { /* Workaround for DRi#1082 where DR root path can't have ".." */ get_absolute_path(dr_root, default_dr_root, BUFFER_SIZE_ELEMENTS(default_dr_root)); NULL_TERMINATE_BUFFER(default_dr_root); dr_root = default_dr_root; } _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s/"LIB_ARCH"/%s/%s", dr_root, use_dr_debug ? "debug" : "release", DR_LIB_NAME); NULL_TERMINATE_BUFFER(buf); if (!file_is_readable(buf)) { /* support debug build w/ integrated debug DR build and so no release */ if (!use_dr_debug) { _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s/"LIB_ARCH"/%s/%s", dr_root, "debug", DR_LIB_NAME); NULL_TERMINATE_BUFFER(buf); if (!file_is_readable(buf)) { fatal("cannot find DynamoRIO library %s", buf); goto error; /* actually won't get here */ } warn("using debug DynamoRIO since release not found"); use_dr_debug = true; } } /* once we have 64-bit we'll need to address the NSIS "bin/" requirement */ _snprintf(client_path, BUFFER_SIZE_ELEMENTS(client_path), "%s%c"BIN_ARCH"%c%s%c%s", drheapstat_root, DIRSEP, DIRSEP, use_drheapstat_debug ? "debug" : "release", DIRSEP, DRHEAPSTAT_LIB_NAME); NULL_TERMINATE_BUFFER(client_path); if (!file_is_readable(client_path)) { if (!use_drheapstat_debug) { _snprintf(client_path, BUFFER_SIZE_ELEMENTS(client_path), "%s%c"BIN_ARCH"%c%s%c%s", drheapstat_root, DIRSEP, DIRSEP, "debug", DIRSEP, DRHEAPSTAT_LIB_NAME); NULL_TERMINATE_BUFFER(client_path); if (!file_is_readable(client_path)) { fatal("invalid -drheapstat_root: cannot find %s", client_path); goto error; /* actually won't get here */ } /* try to avoid warning for devs running from build dir */ _snprintf(buf, BUFFER_SIZE_ELEMENTS(client_path), "%s%cCMakeCache.txt", drheapstat_root, DIRSEP); NULL_TERMINATE_BUFFER(buf); if (!file_is_readable(buf)) warn("using debug Dr. Heapstat since release not found"); use_drheapstat_debug = true; } } drfront_string_replace_character(logdir, ALT_DIRSEP, DIRSEP); /* canonicalize */ if (!file_is_writable(logdir)) { fatal("invalid -logdir: cannot find/write %s", logdir); goto error; /* actually won't get here */ } info("logdir is \"%s\"", logdir); BUFPRINT(client_ops, BUFFER_SIZE_ELEMENTS(client_ops), cliops_sofar, len, "-logdir `%s` ", logdir); /* Put DR logs inside drheapstat logdir */ if (!dr_logdir_specified) { _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s%cdynamorio", logdir, DIRSEP); NULL_TERMINATE_BUFFER(buf); if (!dr_directory_exists(buf)) { if (!dr_create_dir(buf)) { /* check again in case of a race */ if (!dr_directory_exists(buf)) { fatal("cannot create %s", buf); goto error; /* actually won't get here */ } } } BUFPRINT(dr_ops, BUFFER_SIZE_ELEMENTS(dr_ops), drops_sofar, len, "-logdir `%s` ", buf); } #ifdef UNIX errcode = dr_inject_prepare_to_exec(app_name, (const char **)app_argv, &inject_data); #else errcode = dr_inject_process_create(app_name, (const char **)app_argv, &inject_data); #endif /* Mismatch is just a warning */ if (errcode != 0 && errcode != WARN_IMAGE_MACHINE_TYPE_MISMATCH_EXE) { #ifdef WINDOWS int sofar = #endif _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "failed to create process for \"%s\": ", app_name); #ifdef WINDOWS if (sofar > 0) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) buf + sofar, BUFFER_SIZE_ELEMENTS(buf) - sofar*sizeof(char), NULL); } NULL_TERMINATE_BUFFER(buf); #endif fatal("%s", buf); goto error; /* actually won't get here */ } pid = dr_inject_get_process_id(inject_data); #ifdef WINDOWS if (pidfile != NULL) write_pid_to_file(pidfile, pid); #endif process = dr_inject_get_image_name(inject_data); /* we don't care if this app is already registered for DR b/c our * this-pid config will override */ info("configuring %s pid=%d dr_ops=\"%s\"", process, pid, dr_ops); if (dr_register_process(process, pid, false/*local*/, dr_root, DR_MODE_CODE_MANIPULATION, use_dr_debug, DR_PLATFORM_DEFAULT, dr_ops) != DR_SUCCESS) { fatal("failed to register DynamoRIO configuration"); goto error; /* actually won't get here */ } info("configuring client \"%s\" ops=\"%s\"", client_path, client_ops); if (dr_register_client(process, pid, false/*local*/, DR_PLATFORM_DEFAULT, CLIENT_ID, 0, client_path, client_ops) != DR_SUCCESS) { fatal("failed to register DynamoRIO client configuration"); goto error; /* actually won't get here */ } if (!dr_inject_process_inject(inject_data, false/*!force*/, NULL)) { fatal("unable to inject"); goto error; /* actually won't get here */ } dr_inject_process_run(inject_data); #ifdef UNIX fatal("Failed to exec application"); #else info("waiting for app to exit..."); errcode = WaitForSingleObject(dr_inject_get_process_handle(inject_data), INFINITE); if (errcode != WAIT_OBJECT_0) info("failed to wait for app: %d\n", errcode); #endif errcode = dr_inject_process_exit(inject_data, false/*don't kill process*/); goto cleanup; error: dr_inject_process_exit(inject_data, false); errcode = 1; cleanup: sc = drfront_cleanup_args(argv, argc); if (sc != DRFRONT_SUCCESS) fatal("drfront_cleanup_args failed: %d\n", sc); return (exit0 ? 0 : errcode); }