コード例 #1
0
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);
}
コード例 #2
0
ファイル: launcher.cpp プロジェクト: FirstBlue/dynamorio
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;
}