int _tmain(int argc, const TCHAR *targv[]) { char **argv; char *app_name = NULL; char full_app_name[MAXIMUM_PATH]; char **app_argv = NULL; int app_idx = 1; int errcode = 1; void *inject_data = NULL; char buf[MAXIMUM_PATH]; drfront_status_t sc; bool is64, is32; analyzer_t *analyzer = NULL; std::string tracer_ops; #ifdef UNIX pid_t child = 0; #endif bool have_trace_file; #if defined(WINDOWS) && !defined(_UNICODE) # error _UNICODE must be defined #else /* Convert to UTF-8 */ sc = drfront_convert_args(targv, &argv, argc); if (sc != DRFRONT_SUCCESS) FATAL_ERROR("failed to process args: %d\n", sc); #endif // This frontend exists mainly because we have a standalone application // to launch, the analyzer. We are not currently looking for a polished // tool launcher independent of drrun. Thus, we skip all the logic around // default root and client directories and assume we were invoked from // a pre-configured .drrun file with proper paths. std::string parse_err; if (!droption_parser_t::parse_argv(DROPTION_SCOPE_FRONTEND, argc, (const char **) argv, &parse_err, &app_idx)) { // We try to support no "--" separating the app if (argv[app_idx] != NULL && argv[app_idx][0] != '-') { // Treat as the app name } else { FATAL_ERROR("Usage error: %s\nUsage:\n%s", parse_err.c_str(), droption_parser_t::usage_short(DROPTION_SCOPE_ALL).c_str()); } } have_trace_file = !op_infile.get_value().empty() || !op_indir.get_value().empty(); if (!have_trace_file) { if (app_idx >= argc) { FATAL_ERROR("Usage error: no application specified\nUsage:\n%s", droption_parser_t::usage_short(DROPTION_SCOPE_ALL).c_str()); } app_name = argv[app_idx]; 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; NOTIFY(1, "INFO", "targeting application: \"%s\"", app_name); if (!file_is_readable(full_app_name)) { FATAL_ERROR("cannot find application %s", full_app_name); } if (drfront_is_64bit_app(app_name, &is64, &is32) == DRFRONT_SUCCESS && IF_X64_ELSE(!is64, is64 && !is32)) { /* FIXME i#1703: since drinjectlib doesn't support cross-arch * injection (DRi#803), we need to launch the other frontend. */ FATAL_ERROR("application has bitwidth unsupported by this launcher"); } app_argv = &argv[app_idx]; if (!file_is_readable(op_tracer.get_value().c_str())) { FATAL_ERROR("tracer library %s is unreadable", op_tracer.get_value().c_str()); } if (!file_is_readable(op_dr_root.get_value().c_str())) { FATAL_ERROR("invalid -dr_root %s", op_dr_root.get_value().c_str()); } } if (op_offline.get_value() && !have_trace_file) { // Initial sanity check: may still be unwritable by this user, but this // serves as at least an existence check. if (!file_is_writable(op_outdir.get_value().c_str())) { FATAL_ERROR("invalid -outdir %s", op_outdir.get_value().c_str()); } } else { analyzer = new analyzer_multi_t; if (!*analyzer) { FATAL_ERROR("failed to initialize analyzer"); } } tracer_ops = op_tracer_ops.get_value(); if (!have_trace_file) { /* i#1638: fall back to temp dirs if there's no HOME/USERPROFILE set */ dr_get_config_dir(false/*local*/, true/*use temp*/, buf, BUFFER_SIZE_ELEMENTS(buf)); NOTIFY(1, "INFO", "DynamoRIO configuration directory is %s", buf); #ifdef UNIX if (op_offline.get_value()) child = 0; else child = fork(); if (child < 0) { FATAL_ERROR("failed to fork"); assert(false); // won't get here } else if (child == 0) { /* child, or offline where we exec this process */ if (!configure_application(app_name, app_argv, tracer_ops, &inject_data) || !dr_inject_process_inject(inject_data, false/*!force*/, NULL)) { FATAL_ERROR("unable to inject"); assert(false); // won't get here } FATAL_ERROR("failed to exec application"); } /* parent */ #else if (!configure_application(app_name, app_argv, tracer_ops, &inject_data) || !dr_inject_process_inject(inject_data, false/*!force*/, NULL)) { FATAL_ERROR("unable to inject"); } dr_inject_process_run(inject_data); #endif } if (!op_offline.get_value() || have_trace_file) { if (!analyzer->run()) { FATAL_ERROR("failed to run analyzer"); } } if (!have_trace_file) { #ifdef WINDOWS NOTIFY(1, "INFO", "waiting for app to exit..."); errcode = WaitForSingleObject(dr_inject_get_process_handle(inject_data), INFINITE); if (errcode != WAIT_OBJECT_0) NOTIFY(1, "INFO", "failed to wait for app: %d\n", errcode); errcode = dr_inject_process_exit(inject_data, false/*don't kill process*/); #else # ifndef NDEBUG pid_t result = # endif waitpid(child, &errcode, 0); assert(result == child); #endif if (!op_offline.get_value()) { // Skipping for offline to match UNIX. // XXX: we may want a prefix on our output std::cerr << "---- <application exited with code " << errcode << "> ----" << std::endl; } } else errcode = 0; analyzer->print_stats(); // release analyzer's space delete analyzer; sc = drfront_cleanup_args(argv, argc); if (sc != DRFRONT_SUCCESS) FATAL_ERROR("drfront_cleanup_args failed: %d\n", sc); return errcode; }
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); }
static bool check_architecture(const char *dll, char **argv) { bool is_64bit, also_32bit; if (drfront_is_64bit_app(dll, &is_64bit, &also_32bit) != DRFRONT_SUCCESS) { printf("ERROR: unable to get the architecture infomation of" " the target module %s\n", dll); return false; } if (IF_X64_ELSE(!is_64bit, is_64bit && !also_32bit)) { char *orig_argv0 = argv[0]; char root[MAXIMUM_PATH]; char buf[MAXIMUM_PATH]; char *basename; int errcode; void *inject_data; bool is_readable; if (drfront_get_app_full_path(argv[0], root, BUFFER_SIZE_ELEMENTS(root)) != DRFRONT_SUCCESS) { printf("ERROR: unable to get base dir of %s\n", argv[0]); return false; } basename = root + strlen(root) - 1; while (*basename != DIRSEP && *basename != ALT_DIRSEP && basename > root) basename--; if (basename <= root) { printf("ERROR: unable to get base dir of %s\n", argv[0]); return false; } *basename = '\0'; basename++; _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf) , "%s%c..%c%s%c%s", root, DIRSEP, DIRSEP, IF_X64_ELSE("bin", "bin64"), DIRSEP, basename); NULL_TERMINATE_BUFFER(buf); if (drfront_access(buf, DRFRONT_READ, &is_readable) != DRFRONT_SUCCESS || !is_readable) { printf("ERROR: unable to find frontend %s to match target file bitwidth: " "is this an incomplete installation?\n", buf); } argv[0] = buf; #ifdef UNIX errcode = dr_inject_prepare_to_exec(buf, (const char **)argv, &inject_data); if (errcode == 0 || errcode == WARN_IMAGE_MACHINE_TYPE_MISMATCH_EXE) dr_inject_process_run(inject_data); /* shouldn't return */ printf("ERROR (%d): unable to launch frontend to match target file bitwidth\n", errcode); argv[0] = orig_argv0; return false; #else errcode = dr_inject_process_create(buf, argv, &inject_data); if (errcode == 0 || errcode == WARN_IMAGE_MACHINE_TYPE_MISMATCH_EXE) { dr_inject_process_run(inject_data); /* Wait for the child so user's shell prompt doesn't come back early */ errcode = WaitForSingleObject(dr_inject_get_process_handle(inject_data), INFINITE); if (errcode != WAIT_OBJECT_0) printf("WARNING: failed to wait for cross-arch frontend\n"); dr_inject_process_exit(inject_data, false); argv[0] = orig_argv0; return false; } else { printf("ERROR (%d): unable to launch frontend to match target file bitwidth\n", errcode); argv[0] = orig_argv0; return false; } #endif } return true; }