summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Worth <cworth@cworth.org>2011-10-19 18:07:43 -0700
committerCarl Worth <cworth@cworth.org>2011-10-19 18:07:43 -0700
commitfd29289b841ffecc341123c41d48a7da53d6153d (patch)
tree5820c40d83a227cd9b6b5306d0b5933c2092409b
parent746d2de0f6d2d0f161254bdbf4b2b279c3ba8287 (diff)
apitrace: Add a new "apitrace trace" command.HEADmaster
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.cpp157
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)
{