blob: 7d9fcd9753f60a75ba50d9e47903c702d2f15a3b [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2010-2016 Google, Inc. All rights reserved.
* Copyright (c) 2010 VMware, Inc. All rights reserved.
* **********************************************************/
/* Dr. Memory: the memory debugger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License, and no later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* front end for drstrace tool */
/* For unicode support, we must use wide-char versions of main(), argv[],
* and all of the windows library routines like GetEnvironmentVariable(),
* _access(), and GetFullPathName(). Yet we must use UTF-8 for the DR
* API routines. We could go two routes: one is to have everything be UTF-16
* and only convert before calling DR routines; the other is to have everything
* be UTF-8 and convert when calling Windows routines. We pick the latter
* because we expect to port this to Linux.
*/
#ifdef WINDOWS
# define UNICODE
# define _UNICODE
#endif
#include "dr_api.h" /* for the types */
#include "dr_inject.h"
#include "dr_config.h"
#include "dr_frontend.h"
#include "utils.h"
#include <assert.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
/* XXX DRi#1079: can we put even more into drfrontendlib? DR location, client
* lib location, DR and client lib debug vs release, DR and client options, etc.
*/
#define MAX_DR_CMDLINE (MAXIMUM_PATH*6)
#define MAX_APP_CMDLINE 4096
#define BIN32_ARCH "bin"
#define BIN64_ARCH "bin64"
#define LIB32_ARCH "lib32"
#define LIB64_ARCH "lib64"
/* As this is a Windows tool, we tune it for startup and not steady-state perf.
* -fast_client_decode relies on drmgr, drx, and drsyscall support.
*/
/* FIXME i#1876: -fast_client_decode is causing app crashes on big Java apps.
* We disable for now but it would be nice to fix the bug and re-enable.
*/
#define DEFAULT_DR_OPS "-disable_traces -nop_initial_bblock"
#define CLIENT_ID 0
#define prefix ""
static bool verbose;
static bool quiet;
static bool results_to_stderr = true;
static bool no_resfile; /* no results file expected */
static bool top_stats;
#define fatal(msg, ...) do { \
fprintf(stderr, "ERROR: " msg "\n", ##__VA_ARGS__); \
fflush(stderr); \
exit(1); \
} while (0)
#define warn(msg, ...) do { \
if (!quiet) { \
fprintf(stderr, "WARNING: " msg "\n", ##__VA_ARGS__); \
fflush(stderr); \
} \
} while (0)
#define info(msg, ...) do { \
if (verbose) { \
fprintf(stderr, "INFO: " msg "\n", ##__VA_ARGS__); \
fflush(stderr); \
} \
} while (0)
static void
print_usage(void)
{
fprintf(stderr, "usage: drstrace [options] -- <app and args to run>\n");
/* XXX: have an optionsx.h? Shared frontend lib (i#1079) could help
* solve that by providing usage and options parsing for its provided
* options.
*/
fprintf(stderr, "-logdir <dir> Specify log directory where system call data\n");
fprintf(stderr, " will be written, in a separate file per process.\n");
fprintf(stderr, " The default value is \".\" (current dir).\n");
fprintf(stderr, " If set to \"-\", data for all processes are\n");
fprintf(stderr, " printed to stderr (warning: this can be slow).\n");
fprintf(stderr, "-symcache_path <path> Specify absolute path where symbol data\n");
fprintf(stderr, " should be cached. If not set, _NT_SYMBOL_PATH\n");
fprintf(stderr, " environment variable will be used, if set; else\n");
fprintf(stderr, " a local directory will be used.\n");
fprintf(stderr, "-[no_]load_symbols Enables or disables loading of symbols over\n");
fprintf(stderr, " the network. This option is enabled by default.\n");
fprintf(stderr, "-no_follow_children Do not trace child processes (overrides\n");
fprintf(stderr, " the default, which is to trace all children).\n");
fprintf(stderr, "-version Print version number.\n");
fprintf(stderr, "-verbose <N> Change verbosity (default 1).\n");
}
#define usage(msg, ...) do { \
fprintf(stderr, "\n"); \
fprintf(stderr, "ERROR: " msg "\n\n", ##__VA_ARGS__); \
print_usage(); \
exit(1); \
} while (0)
#undef BUFPRINT /* XXX: we could redefine ASSERT to use utils.h BUFPRINT here */
/* must use dr_snprintf here to support %S converting UTF-16<->UTF-8 */
#define BUFPRINT(buf, bufsz, sofar, len, ...) do { \
drfront_status_t sc = drfront_bufprint(buf, bufsz, &(sofar), &(len), ##__VA_ARGS__); \
if (sc != DRFRONT_SUCCESS) \
fatal("drfront_bufprint failed: %d\n", sc); \
} while (0)
/* always null-terminates */
static void
char_to_tchar(const char *str, TCHAR *wbuf, size_t wbuflen/*# elements*/)
{
drfront_status_t sc = drfront_char_to_tchar(str, wbuf, wbuflen);
if (sc != DRFRONT_SUCCESS)
fatal("drfront_char_to_tchar failed: %d\n", sc);
}
/* Replace occurences of old_char with new_char in str. Typically used to
* canonicalize Windows paths into using forward slashes.
*/
void
string_replace_character(char *str, char old_char, char new_char)
{
while (*str != '\0') {
if (*str == old_char) {
*str = new_char;
}
str++;
}
}
static bool
ends_in_exe(const char *s)
{
/* really we want caseless strstr */
size_t len = strlen(s);
return (len > 4 && s[len-4] == '.' &&
(s[len-3] == 'E' || s[len-3] == 'e') &&
(s[len-2] == 'X' || s[len-2] == 'x') &&
(s[len-1] == 'E' || s[len-1] == 'e'));
}
static bool
file_is_readable(char *path)
{
bool ret = false;
return (drfront_access(path, DRFRONT_READ, &ret) == DRFRONT_SUCCESS && ret);
}
static void
get_absolute_path(const char *src, char *buf, size_t buflen/*# elements*/)
{
drfront_status_t sc = drfront_get_absolute_path(src, buf, buflen);
if (sc != DRFRONT_SUCCESS)
fatal("drfront_get_absolute_path failed: %d\n", sc);
}
static void
get_full_path(const char *app, char *buf, size_t buflen/*# elements*/)
{
drfront_status_t sc = drfront_get_app_full_path(app, buf, buflen);
if (sc != DRFRONT_SUCCESS)
fatal("drfront_get_app_full_path failed: %d\n", sc);
}
int
_tmain(int argc, TCHAR *targv[])
{
char **argv;
char *process = NULL;
char *dr_root = NULL;
char *drstrace_root = NULL;
char default_dr_root[MAXIMUM_PATH];
char default_drstrace_root[MAXIMUM_PATH];
char client_path[MAXIMUM_PATH];
const char *bin_arch = IF_X64_ELSE(BIN64_ARCH, BIN32_ARCH);
const char *lib_arch = IF_X64_ELSE(LIB64_ARCH, LIB32_ARCH);
char client_ops[MAX_DR_CMDLINE];
size_t cliops_sofar = 0; /* for BUFPRINT to client_ops */
char dr_ops[MAX_DR_CMDLINE];
char sym_path[MAXIMUM_PATH];
char symsrv_path[MAXIMUM_PATH];
char pdb_path[MAXIMUM_PATH];
char dr_logdir[MAXIMUM_PATH];
char symdll_path[MAXIMUM_PATH];
size_t drops_sofar = 0; /* for BUFPRINT to dr_ops */
ssize_t len; /* shared by all BUFPRINT */
bool use_dr_debug = false;
bool use_drstrace_debug = false;
bool dr_logdir_specified = false;
bool sym_path_specified = 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;
time_t start_time, end_time;
drfront_status_t sc;
bool res;
bool is64, is32;
bool load_symbols = true;
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/drstrace.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_drstrace_root,
BUFFER_SIZE_ELEMENTS(default_drstrace_root));
NULL_TERMINATE_BUFFER(default_drstrace_root);
drstrace_root = default_drstrace_root;
string_replace_character(drstrace_root, ALT_DIRSEP, DIRSEP); /* canonicalize */
BUFPRINT(dr_ops, BUFFER_SIZE_ELEMENTS(dr_ops),
drops_sofar, len, "%s ", DEFAULT_DR_OPS);
client_ops[0] = '\0';
/* parse command line */
/* FIXME PR 487993: use optionsx.h to construct this parsing code */
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. */
else if (argv[i][0] != '-' && 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], "-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_drstrace_debug = true;
continue;
}
else if (strcmp(argv[i], "-release") == 0) {
use_drstrace_debug = false;
continue;
}
else if (!strcmp(argv[i], "-version")) {
#if defined(BUILD_NUMBER) && defined(VERSION_NUMBER)
printf("Dr. Memory drstrace version %s -- build %d\n",
VERSION_STRING, BUILD_NUMBER);
#elif defined(BUILD_NUMBER)
printf("Dr. Memory drstrace custom build %d -- %s\n", BUILD_NUMBER, __DATE__);
#elif defined(VERSION_NUMBER)
printf("Dr. Memory drstrace version %s -- custom build %s, %s\n",
VERSION_STRING, __DATE__, __TIME__);
#else
printf("Dr. Memory drstrace 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], "-drstrace") == 0) {
if (i >= argc - 1)
usage("invalid arguments");
drstrace_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]);
}
else if (strcmp(argv[i], "-dr_ops") == 0) {
if (i >= argc - 1)
usage("invalid arguments");
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]);
}
else if (strcmp(argv[i], "-exit0") == 0) {
exit0 = true;
}
else if (strcmp(argv[i], "-symcache_path") == 0) {
_snprintf(sym_path, BUFFER_SIZE_ELEMENTS(sym_path),
"%s", argv[++i]);
NULL_TERMINATE_BUFFER(sym_path);
string_replace_character(sym_path, '\\', '/');
sym_path_specified = true;
}
else if (strcmp(argv[i], "-load_symbols") == 0) {
load_symbols = true;
}
else if (strcmp(argv[i], "-no_load_symbols") == 0) {
load_symbols = false;
}
else {
/* pass to client */
BUFPRINT(client_ops, BUFFER_SIZE_ELEMENTS(client_ops),
cliops_sofar, len, "`%s` ", argv[i]);
}
}
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);
/* Cross-arch injection (i#1506) */
if (drfront_is_64bit_app(app_name, &is64, &is32) == DRFRONT_SUCCESS &&
IF_X64_ELSE(!is64, is64 && !is32)) {
/* While I'd love to just set bin_arch and lib_arch differently,
* drinjectlib doesn't support cross-arch injection (DRi#803).
* Thus, to provide single-front-end support, we launch the other
* frontend.
*/
char *orig_argv0 = argv[0];
_snprintf(buf, BUFFER_SIZE_ELEMENTS(buf),
"%s%c%s%cdrstrace.exe", drstrace_root, DIRSEP,
IF_X64_ELSE(BIN32_ARCH, BIN64_ARCH), DIRSEP);
NULL_TERMINATE_BUFFER(buf);
if (!file_is_readable(buf)) {
fatal("unable to find frontend %s to match target app bitwidth: "
"is this an incomplete installation?", buf);
}
argv[0] = buf;
info("launching frontend %s to match target app bitwidth", buf);
/* XXX DRi#943: this lib routine currently doesn't handle int18n */
errcode = dr_inject_process_create(buf, argv, &inject_data);
/* Mismatch is just a warning */
if (errcode == 0 || errcode == WARN_IMAGE_MACHINE_TYPE_MISMATCH_EXE) {
dr_inject_process_run(inject_data);
/* If we don't wait, the prompt comes back, which is confusing */
info("waiting for other frontend...");
errcode = WaitForSingleObject(dr_inject_get_process_handle(inject_data),
INFINITE);
if (errcode != WAIT_OBJECT_0)
info("failed to wait for frontend: %d\n", errcode);
dr_inject_process_exit(inject_data, false);
argv[0] = orig_argv0;
goto cleanup;
} else {
fatal("unable to launch frontend to match target app bitwidth: code=%d",
errcode);
}
}
/* note that we want target app name as part of cmd line
* (FYI: if we were using WinMain, the pzsCmdLine passed in
* does not have our own app name in it)
* it's easier to construct than to call GetCommandLine() and then
* remove our own args.
*/
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);
}
if (!file_is_readable(dr_root)) {
fatal("invalid -dr_root %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/%s/%s/dynamorio.dll", dr_root, lib_arch,
use_dr_debug ? "debug" : "release");
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/%s/%s/dynamorio.dll", dr_root, lib_arch, "debug");
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;
}
}
_snprintf(client_path, BUFFER_SIZE_ELEMENTS(client_path),
"%s%c%s%c%s%cdrstracelib.dll", drstrace_root, DIRSEP, bin_arch, DIRSEP,
use_drstrace_debug ? "debug" : "release", DIRSEP);
NULL_TERMINATE_BUFFER(client_path);
if (!file_is_readable(client_path)) {
if (!use_drstrace_debug) {
_snprintf(client_path, BUFFER_SIZE_ELEMENTS(client_path),
"%s%c%s%c%s%cdrstracelib.dll", drstrace_root,
DIRSEP, bin_arch, DIRSEP, "debug", DIRSEP);
NULL_TERMINATE_BUFFER(client_path);
if (!file_is_readable(client_path)) {
fatal("invalid -drstrace_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(buf),
"%s%cCMakeCache.txt", drstrace_root, DIRSEP);
NULL_TERMINATE_BUFFER(buf);
if (!file_is_readable(buf))
warn("using debug Dr. Memory since release not found");
use_drstrace_debug = true;
}
}
/* If we're installed into Program Files, we have to pick a different
* DR logdir to avoid popup msgs (i#1499). We don't want to use the
* same logdir as for drstrace as the latter supports "-" (stderr).
*/
if (!dr_logdir_specified) { /* don't override user-specified DR logdir */
bool use_root;
/* XXX: ideally we would share DrMem's "/dynamorio/" subdir, but that would
* require creating both subdirs separately if drstrace is run before drmem.
* See comment below as well.
*/
if (drfront_appdata_logdir(dr_root, "Dr. Memory", &use_root,
dr_logdir, BUFFER_SIZE_ELEMENTS(dr_logdir)) !=
DRFRONT_SUCCESS ||
use_root ||
(!dr_create_dir(dr_logdir) && !dr_directory_exists(dr_logdir))) {
/* A similar situation: we'd prefer to share DrMem's local install
* "drmemory/logs/dynamorio" but that gets complex to support both
* in a package and in a dev's local build dir. Since DR logs
* are going to be rare w/ drstrace (only for debugging the tool
* itself) we just go w/ simplicity.
*/
get_absolute_path(".", dr_logdir, BUFFER_SIZE_ELEMENTS(dr_logdir));
}
BUFPRINT(dr_ops, BUFFER_SIZE_ELEMENTS(dr_ops),
drops_sofar, len, "-logdir `%s` ", dr_logdir);
}
if (!sym_path_specified) {
/* Default location for local symbol cache: if our install dir is
* writable, we want logs/symbols/. Else just symbols/ inside
* AppData Dr. Memory dir.
* drfront_set_client_symbol_search_path() adds symbols/ for us.
*/
bool use_root;
sc = drfront_appdata_logdir(drstrace_root, "Dr. Memory", &use_root,
sym_path, BUFFER_SIZE_ELEMENTS(sym_path));
if (sc == DRFRONT_SUCCESS) {
if (use_root) {
_snprintf(sym_path, BUFFER_SIZE_ELEMENTS(sym_path),
"%s%clogs", drstrace_root, DIRSEP);
NULL_TERMINATE_BUFFER(sym_path);
}
if (!dr_create_dir(sym_path) && !dr_directory_exists(sym_path))
sc = DRFRONT_ERROR;
}
if (sc != DRFRONT_SUCCESS) {
get_absolute_path(".", sym_path, BUFFER_SIZE_ELEMENTS(sym_path));
}
}
/* fetch wintypes.pdb (if not exists) to symcache_path */
if (drfront_sym_init(NULL, "dbghelp.dll") == DRFRONT_SUCCESS) {
sc = drfront_set_client_symbol_search_path
(sym_path, sym_path_specified,
symsrv_path, BUFFER_SIZE_ELEMENTS(symsrv_path));
if (sc == DRFRONT_SUCCESS) {
/* symfetch.dll is in our dir */
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), "%s", SYMBOL_DLL_NAME);
NULL_TERMINATE_BUFFER(buf);
get_absolute_path(buf, symdll_path, BUFFER_SIZE_ELEMENTS(symdll_path));
NULL_TERMINATE_BUFFER(symdll_path);
/* before we add the MS symsrv, see whether we have local symbols */
sc = drfront_fetch_module_symbols(symdll_path, pdb_path,
BUFFER_SIZE_ELEMENTS(pdb_path));
if (sc != DRFRONT_SUCCESS && load_symbols) {
warn("fetching symbol information (procedure may take some time).");
sc = drfront_set_symbol_search_path(symsrv_path);
/* We use a special fake dll to obtain symbolic info from MS Symbol
* server. PTAL i#1540 for details.
*/
if (sc == DRFRONT_SUCCESS) {
sc = drfront_fetch_module_symbols(symdll_path, pdb_path,
BUFFER_SIZE_ELEMENTS(pdb_path));
if (sc == DRFRONT_SUCCESS)
info("symbol file successfully fetched");
}
}
if (sc == DRFRONT_SUCCESS) {
/* pass to client */
BUFPRINT(client_ops, BUFFER_SIZE_ELEMENTS(client_ops),
cliops_sofar, len, "-symcache_path `%s` ", pdb_path);
} else if (!load_symbols) {
warn("symbol fetching was disabled via -no_load_symbols.");
} else {
warn("symbol fetching failed. Symbol lookup will be disabled.");
}
} else {
warn("failed to set symbol search path. Symbol lookup will be disabled.");
}
} else {
warn("symbol initialization error. Symbol lookup will be disabled.");
}
/* 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));
info("DynamoRIO configuration directory is %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);
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 */
}
if (top_stats)
start_time = time(NULL);
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
if (top_stats) {
double wallclock;
end_time = time(NULL);
wallclock = difftime(end_time, start_time);
dr_inject_print_stats(inject_data, (int) wallclock, true/*time*/, true/*mem*/);
}
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);
}