diff options
author | Carl Worth <cworth@cworth.org> | 2011-10-19 18:07:43 -0700 |
---|---|---|
committer | Carl Worth <cworth@cworth.org> | 2011-10-19 18:07:43 -0700 |
commit | fd29289b841ffecc341123c41d48a7da53d6153d (patch) | |
tree | 5820c40d83a227cd9b6b5306d0b5933c2092409b | |
parent | 746d2de0f6d2d0f161254bdbf4b2b279c3ba8287 (diff) |
Currently, this command works only on Linux as it functions by setting
the LD_PRELOAD environment variable. (It also does some Linux-specific
path manipulations assuming that '/' is the path separator.)
It finds the glxtrace.so file by looking first in the configured
directory for private binarie (<prefix>/lib/apitrace) and if that's
not present, by looking in the directory from which apitrace was run
(as best as can be guessed by examining argv[0] and the PATH
environment variable).
-rw-r--r-- | apitrace.cpp | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/apitrace.cpp b/apitrace.cpp index 4a4ebea..d180e72 100644 --- a/apitrace.cpp +++ b/apitrace.cpp @@ -56,9 +56,19 @@ typedef struct command { } command_t; static int +apitrace_trace_command(int argc, char *argv[], int first_command_arg); + +static int apitrace_help_command(int argc, char *argv[], int first_command_arg); static command_t commands[] = { + { "trace", apitrace_trace_command, + "<program> [<args> ...]", + "Trace execution of <program>, writing to <program>.trace", + "\tThe given program will be executed with the given arguments.\n" + "\tDuring execution, all OpenGL calls will be captured to a trace\n" + "\tfile named <program>.trace. That trace file can then be used\n" + "\twith other apitrace utilities for replay or analysis." }, { "help", apitrace_help_command, "[<command>]", "Print detailed help for the given command.", @@ -128,6 +138,153 @@ apitrace_help_command(int argc, char *argv[], int first_arg_command) return 1; } +/* Returns whether path exists as a regular file. */ +static int +_executable_exists(const char *path) +{ + struct stat st; + int err; + + err = stat(path, &st); + if (err) + return 0; + + if (! S_ISREG(st.st_mode)) + return 0; + + return 1; +} + +/* Given an relative 'path', return a newly allocated string giving + * the absolute version of path. + * + * Will return 'path' itself if it is already absolute, or if it is + * NULL or an empty string. */ +static char * +_make_absolute(void *ctx, char *path) +{ + char *cwd, *absolute; + + if (path == NULL || *path == '\0' || *path == '/') + return path; + + cwd = get_current_dir_name(); + + absolute = talloc_asprintf(ctx, "%s/%s", cwd, path); + + free (cwd); + + return absolute; +} + +/* Given argv0 determine the directory from which the program was + * run. If argv0 contains a '/' the directory is simply read from the + * string itself. Otherwise, this function searches for the executable + * by examining each directory within the PATH environment variable. + * + * Returns an empty string if argv0 is NULL or an empty string, or if + * a path cannot be resolved. + * + * Note: This function may return a relative directory. Call + * _make_absolute on the result if you need an absolute directory. + */ +char * +_find_argv0_directory(void *ctx, const char *argv0) +{ + const char *last_slash, *colon, *path; + + if (argv0 == NULL || argv0[0] == '\0') + return talloc_strdup(ctx, ""); + + last_slash = argv0 + strlen(argv0); + + while (last_slash > argv0 && *last_slash != '/') + last_slash--; + + /* Contains a '/'. Copy argv0 to just before final '/'. */ + if (last_slash > argv0) + return talloc_strndup(ctx, argv0, last_slash - argv0); + + /* Contains no '/'. Search PATH. */ + path = getenv("PATH"); + while (1) { + char *dir; + + if (*path == '\0') + break; + + colon = strchr(path, ':'); + if (colon == NULL) + colon = path + strlen(path); + + dir = talloc_strndup(ctx, path, colon - path); + if (dir && *dir) { + char *complete = talloc_asprintf(ctx, "%s/%s", dir, argv0); + if (_executable_exists(complete)) { + talloc_free(complete); + return dir; + } + } + talloc_free(dir); + + path = colon; + while (*path == ':') + path++; + } + + return talloc_strdup(ctx, ""); +} + +/* Find 'executable' by looking first in the apitrace private bin + * directory (for example /usr/lib/apitrace), and then in the same + * directory from which the current process was likely executed as + * determined by 'argv0' and the PATH environment variable. */ +static char * +_find_private_bin(void *ctx, const char *executable, const char *argv0) +{ + char *path, *dir; + + /* First look in our private bin directory. */ + path = talloc_asprintf(ctx, "%s/%s", APITRACE_PRIVATE_BIN_DIR, executable); + + if (_executable_exists(path)) + return path; + + talloc_free(path); + + /* Otherwise, look in the directory from which this executable was run. */ + dir = _find_argv0_directory(ctx, argv0); + path = talloc_asprintf(ctx, "%s/%s",dir, executable); + talloc_free(dir); + + if (_executable_exists(path)) + return path; + + std::cerr << "Error: Cannot find executable " << executable << ".\n"; + exit (1); +} + +static int +apitrace_trace_command(int argc, char *argv[], int first_arg_command) +{ + char *path; + void *ctx = talloc_new(NULL); + + path = _find_private_bin(ctx, "glxtrace.so", argv[0]); + + path = _make_absolute (ctx, path); + + setenv("LD_PRELOAD", path, 1); + + talloc_free(ctx); + + execvp(argv[first_arg_command], argv + first_arg_command); + + std::cerr << "Error: Failed to execvp " << argv[first_arg_command] << "\n"; + + return 1; +} + int main(int argc, char **argv) { |