]> git.cworth.org Git - fips/commitdiff
Start wrapping OpenGL, and print periodic FPS value to stdout.
authorCarl Worth <cworth@cworth.org>
Tue, 23 Apr 2013 21:17:33 +0000 (14:17 -0700)
committerCarl Worth <cworth@cworth.org>
Tue, 23 Apr 2013 21:17:33 +0000 (14:17 -0700)
In addition to the fips binary, we now also compile a libfips.so library
and LD_PRELOAD that before executing the program specified on the command-
line.

The libfips.so library wraps OpenGL calls of interest for purpose of
instrumentation. So far, the only call wrapped is glXSwapBuffers and
the only instrumentation is to compute and print out a frames-per-second
value every 60 frames.

.gitignore
Makefile.local
configure
execute.c
execute.h
fips.c
glxwrap.c [new file with mode: 0644]
libfips.sym [new file with mode: 0644]

index 5a5d08a268e610d086cca04ee2310d8d44784544..65bec7717e07367d8663f656f5507fcafb421ea9 100644 (file)
@@ -1,6 +1,8 @@
 .deps
 .first-build-message
 Makefile.config
+config.h
 fips
+libfips.so
 *.o
 *~
index 52a02ff0b5ee131e3cd008ad6b92a545900c3f34..1287f49ce3b2e252dbe60763b2a0d0f2c0f58922 100644 (file)
@@ -5,16 +5,15 @@ include Makefile.release
 # Smash together user's values with our extra values
 FINAL_CFLAGS = -DFIPS_VERSION=$(VERSION) $(CFLAGS) $(WARN_CFLAGS) $(CONFIGURE_CFLAGS) $(extra_cflags)
 FINAL_FIPS_LDFLAGS = $(LDFLAGS) $(AS_NEEDED_LDFLAGS) $(TALLOC_LDFLAGS)
+FINAL_LIBFIPS_LDFLAGS = $(LDFLAGS) -ldl
 FINAL_FIPS_LINKER = CC
 ifneq ($(LINKER_RESOLVES_LIBRARY_DEPENDENCIES),1)
 FINAL_FIPS_LDFLAGS += $(CONFIGURE_LDFLAGS)
 endif
-ifeq ($(LIBDIR_IN_LDCONFIG),0)
-FINAL_FIPS_LDFLAGS += $(RPATH_LDFLAGS)
-endif
 
 .PHONY: all
-all: fips
+all: fips libfips.so
+
 ifeq ($(MAKECMDGOALS),)
 ifeq ($(shell cat .first-build-message 2>/dev/null),)
        @FIPS_FIRST_BUILD=1 $(MAKE) --no-print-directory all
@@ -59,6 +58,8 @@ clean:
 distclean: clean
        rm -rf $(DISTCLEAN)
 
+# Main program, fips
+
 fips_srcs = \
        execute.c \
        fips.c
@@ -66,20 +67,35 @@ fips_srcs = \
 fips_modules = $(fips_srcs:.c=.o)
 
 fips: $(fips_modules)
-       $(call quiet,$(FINAL_FIPS_LINKER) $(CFLAGS)) $^ $(FINAL_FIPS_LDFLAGS) -o $@
+       $(call quiet,$(FINAL_FIPS_LINKER) $(CFLAGS)) $(FINAL_CFLAGS) $^ $(FINAL_FIPS_LDFLAGS) -o $@
+
+# GL-wrapper library, libfips
+LIBRARY_LINK_FLAGS = -shared -Wl,--version-script=libfips.sym,--no-undefined
+
+extra_cflags += -I$(srcdir) -fPIC
+
+libfips_srcs = \
+       glxwrap.c
+
+libfips_modules = $(libfips_srcs:.c=.o)
+
+libfips.so: $(libfips_modules) libfips.sym
+       $(call quiet,$(FINAL_FIPS_LINKER) $(CFLAGS)) $(FINAL_CFLAGS) $(libfips_modules) $(FINAL_LIBFIPS_LDFLAGS) $(LIBRARY_LINK_FLAGS) -o $@
 
 .PHONY: install
 install: all
-       mkdir -p $(DESTDIR)$(prefix)/bin/
-       install fips $(DESTDIR)$(prefix)/bin/fips
+       mkdir -p $(DESTDIR)$(bindir)
+       install fips $(DESTDIR)$(bindir)/fips
+       mkdir -p $(DESTDIR)$(libdir)/fips
+       install -m0644 libfips.so $(DESTDIR)$(libdir)/fips/libfips.so
 ifeq ($(MAKECMDGOALS), install)
        @echo ""
        @echo "Fips is now installed to $(DESTDIR)$(prefix)"
        @echo ""
 endif
 
-SRCS  := $(SRCS) $(fips_srcs)
-CLEAN := $(CLEAN) fips $(fips_modules)
+SRCS  := $(SRCS) $(fips_srcs) $(libfips_srcs)
+CLEAN := $(CLEAN) fips $(fips_modules) $(libfips_modules)
 
 DISTCLEAN := $(DISTCLEAN) .first-build-message Makefile.config
 
index 91797ce86eedce8895de98550ef3139793ec08f6..6c8b186d4da8b25b3f48d4d2c4178962d9f90cff 100755 (executable)
--- a/configure
+++ b/configure
@@ -42,7 +42,6 @@ LDFLAGS=${LDFLAGS:-}
 # Set the defaults for values the user can specify with command-line
 # options.
 PREFIX=/usr/local
-LIBDIR=
 
 usage ()
 {
@@ -82,8 +81,8 @@ specify an installation prefix other than $PREFIX using
 
 Fine tuning of some installation directories is available:
 
+       --bindir=DIR            Install executables to DIR [PREFIX/bin]
        --libdir=DIR            Install libraries to DIR [PREFIX/lib]
-       --includedir=DIR        Install header files to DIR [PREFIX/include]
        --mandir=DIR            Install man pages to DIR [PREFIX/share/man]
        --sysconfdir=DIR        Read-only single-machine data [PREFIX/etc]
 
@@ -102,6 +101,36 @@ configure-script calling conventions, but don't do anything yet:
 EOF
 }
 
+# Given two absolute paths ("from" and "to"), compute a relative path
+# from "from" to "to". For example:
+#
+#      relative_path /foo/bar/baz /foo/qux -> ../../qux
+relative_path ()
+{
+    if [ $# -ne 2 ] ; then
+       echo "Internal error: relative_path requires exactly 2 arguments"
+       exit 1;
+    fi
+
+    from="$1"
+    to="$2"
+
+    # Handle trivial case up-front
+    if [ "$from" = "$to" ] ; then
+       echo ""
+    else
+       shared="$from"
+       relative=""
+
+       while [ "${to#$shared}" = "$to" ] && [ "$shared" != "." ] ; do
+           shared="$(dirname $shared)"
+           relative="..${relative:+/${relative}}"
+       done
+
+       echo "${relative:-.}${to#$shared}"
+    fi
+}
+
 # Parse command-line options
 for option; do
     if [ "${option}" = '--help' ] ; then
@@ -109,10 +138,10 @@ for option; do
        exit 0
     elif [ "${option%%=*}" = '--prefix' ] ; then
        PREFIX="${option#*=}"
+    elif [ "${option%%=*}" = '--bindir' ] ; then
+       BINDIR="${option#*=}"
     elif [ "${option%%=*}" = '--libdir' ] ; then
        LIBDIR="${option#*=}"
-    elif [ "${option%%=*}" = '--includedir' ] ; then
-       INCLUDEDIR="${option#*=}"
     elif [ "${option%%=*}" = '--mandir' ] ; then
        MANDIR="${option#*=}"
     elif [ "${option%%=*}" = '--sysconfdir' ] ; then
@@ -142,17 +171,6 @@ for option; do
     fi
 done
 
-# We set this value early, (rather than just while printing the
-# Makefile.config file later like most values), because we need to
-# actually investigate this value compared to the ldconfig_paths value
-# below.
-if [ -z "$LIBDIR" ] ; then
-    libdir_expanded="${PREFIX}/lib"
-else
-    # very non-general variable expansion
-    libdir_expanded=`echo "$LIBDIR" | sed "s|\\${prefix}|${PREFIX}|g; s|\\$prefix/|${PREFIX}/|; s|//*|/|g"`
-fi
-
 cat <<EOF
 Welcome to ${PROJECT}, ${PROJECT_BLURB}
 
@@ -193,8 +211,6 @@ else
     errors=$((errors + 1))
 fi
 
-libdir_in_ldconfig=0
-
 printf "Checking which platform we are on... "
 uname=`uname`
 if [ $uname = "Darwin" ] ; then
@@ -217,29 +233,6 @@ elif [ $uname = "Linux" ] || [ $uname = "GNU" ] ; then
     printf "$uname\n"
     platform="$uname"
     linker_resolves_library_dependencies=1
-
-    printf "Checking for $libdir_expanded in ldconfig... "
-    ldconfig_paths=$(/sbin/ldconfig -N -X -v 2>/dev/null | sed -n -e 's,^\(/.*\):\( (.*)\)\?$,\1,p')
-    # Separate ldconfig_paths only on newline (not on any potential
-    # embedded space characters in any filenames). Note, we use a
-    # literal newline in the source here rather than something like:
-    #
-    #  IFS=$(printf '\n')
-    #
-    # because the shell's command substitution deletes any trailing newlines.
-    IFS="
-"
-    for path in $ldconfig_paths; do
-       if [ "$path" = "$libdir_expanded" ]; then
-           libdir_in_ldconfig=1
-       fi
-    done
-    IFS=$DEFAULT_IFS
-    if [ "$libdir_in_ldconfig" = '0' ]; then
-       printf "No (will set RPATH)\n"
-    else
-       printf "Yes\n"
-    fi
 else
     printf "Unknown.\n"
     cat <<EOF
@@ -369,27 +362,20 @@ CC = ${CC}
 CFLAGS = ${CFLAGS}
 
 # Default FLAGS for the linker (can be overridden by user such as "make LDFLAGS=-znow")
-LDFLAGS = ${LDFLAGS} -ldl
+LDFLAGS = ${LDFLAGS}
 
 # Flags to enable warnings when using the C compiler
 WARN_CFLAGS=${WARN_CFLAGS}
 
 # The prefix to which ${PROJECT} should be installed
-# Note: If you change this value here, be sure to ensure that the
-# LIBDIR_IN_LDCONFIG value below is still set correctly.
 prefix = ${PREFIX}
 
+# The directory to which executables should be installed
+bindir = ${BINDIR:=\$(prefix)/bin}
+
 # The directory to which libraries should be installed
-# Note: If you change this value here, be sure to ensure that the
-# LIBDIR_IN_LDCONFIG value below is still set correctly.
 libdir = ${LIBDIR:=\$(prefix)/lib}
 
-# Whether libdir is in a path configured into ldconfig
-LIBDIR_IN_LDCONFIG = ${libdir_in_ldconfig}
-
-# The directory to which header files should be installed
-includedir = ${INCLUDEDIR:=\$(prefix)/include}
-
 # The directory to which man pages should be installed
 mandir = ${MANDIR:=\$(prefix)/share/man}
 
@@ -415,3 +401,11 @@ AS_NEEDED_LDFLAGS = ${as_needed_ldflags}
 CONFIGURE_CFLAGS = \$(TALLOC_CFLAGS)
 CONFIGURE_LDFLAGS = \$(TALLOC_LDFLAGS)
 EOF
+
+# construct config.h
+cat > config.h <<EOF
+/* Generated by configure. */
+
+/* Relative path from ${bindir} to ${libdir} */
+#define BINDIR_TO_LIBFIPSDIR "$(relative_path ${BINDIR} ${LIBDIR})/fips"
+EOF
index 9a3d1f75059b112c3e961da9680e81a2f0d4c5fc..b102a4a2b4b4945e8cc20dc1cfeadeb43f90f789 100644 (file)
--- a/execute.c
+++ b/execute.c
  * THE SOFTWARE.
  */
 
+#include "config.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <string.h>
+#include <errno.h>
+
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
 
+#include <talloc.h>
+
+#include <linux/limits.h>
+
+#include "execute.h"
+
+/* Terminate a string representing a filename at the final '/' to
+ * eliminate the final filename component, (leaving only the directory
+ * portions of the original path).
+ *
+ * Notes: A path containing no '/' character will not be modified.
+ *        A path consisting only of "/" will not be modified.
+ */
+static void
+chop_trailing_path_component (char *path)
+{
+       char *slash;
+
+       slash = strrchr (path, '/');
+
+       if (slash == NULL)
+               return;
+
+       if (slash == path)
+               return;
+
+       *slash = '\0';
+}
+
+/* Find the absolute path of the currently executing binary.
+ *
+ * Returns: a string talloc'ed to 'ctx'
+ */
+static char *
+get_bin_name (void *ctx)
+{
+       const char *link = "/proc/self/exe";
+       char *name;
+
+       /* Yes, PATH_MAX is cheesy. I would have preferred to have
+        * used lstat and read the resulting st_size, but everytime I
+        * did that with /proc/self/exe I got a value of 0, (whereas
+        * with a "real" symbolic link I make myself I get the length
+        * of the filename being linked to). Go figure. */
+       int name_len = PATH_MAX + 1;
+
+       name = talloc_size (ctx, name_len - 1);
+       if (name == NULL) {
+               fprintf (stderr, "Out of memory.\n");
+               exit (1);
+       }
+
+       name_len = readlink (link, name, name_len);
+       if (name_len < 0) {
+               fprintf (stderr, "Failed to readlink %s: %s\n", link,
+                        strerror (errno));
+               exit (1);
+       }
+
+       name[name_len + 1] = '\0';
+
+       return name;
+}
+
+/* Does path exist? */
 static int
-fork_exec_and_wait (char * const argv[])
+exists (const char *path)
+{
+       struct stat st;
+       int err;
+
+       err = stat (path, &st);
+
+       /* Failed to stat. It either doesn't exist, or might as well not. */
+       if (err == -1)
+               return 0;
+
+       return 1;
+}
+
+/* Given "library" filename resolve it to an absolute path to an
+ * existing file as follows:
+ *
+ *   1. Look in same directory as current executable image
+ *
+ *      This is to support running from the source directory, without
+ *      having installed anything.
+ *
+ *   2. Look in relative path from $(foo)/$(bindir) to
+ *      $(foo)/$(libdir)/fips based on $(foo) from current executable
+ *      image and configured $(bindir) and $(libdir).
+ *
+ *      We do this rather than looking directly at the configured
+ *      $(libdir) to support cases where the application may have been
+ *      moved after being installed, (in particular, we want to be
+ *      particularly careful not to mix one program with a different
+ *      wrapper---so this "nearest search" should most often be
+ *      correct.
+ *
+ * Returns: a string talloc'ed to 'ctx'
+ */
+static char *
+resolve_path (void *ctx, const char *library)
+{
+       char *bin_path, *lib_path;
+
+       bin_path = get_bin_name (ctx);
+
+       chop_trailing_path_component (bin_path);
+
+       lib_path = talloc_asprintf(ctx, "%s/%s", bin_path, library);
+
+       if (exists (lib_path))
+               return lib_path;
+
+       talloc_free (lib_path);
+
+       lib_path = talloc_asprintf(ctx, "%s/" BINDIR_TO_LIBFIPSDIR "/%s",
+                                  bin_path, library);
+
+       if (exists (lib_path))
+               return lib_path;
+
+       fprintf (stderr, "Error: Failed to find library %s.\n", library);
+       fprintf (stderr, "Looked in both:\n"
+                "\t%s\n"
+                "and\n"
+                "\t%s/" BINDIR_TO_LIBFIPSDIR "\n", bin_path, bin_path);
+       exit (1);
+}
+
+/* After forking, set LD_PRELOAD to preload "library" within child
+ * environment, then exec given arguments.
+ *
+ * The "library" argument is the filename (without path) of a shared
+ * library to load. The complete path will be resolved with
+ * resolve_library_path above. */
+static int
+fork_exec_with_preload_and_wait (char * const argv[], const char *library)
 {
        pid_t pid;
        int i, status;
@@ -36,6 +179,15 @@ fork_exec_and_wait (char * const argv[])
 
        /* Child */
        if (pid == 0) {
+               void *ctx = talloc_new (NULL);
+               char *lib_path;
+
+               lib_path = resolve_path (ctx, library);
+
+               setenv ("LD_PRELOAD", lib_path, 1);
+
+               talloc_free (ctx);
+               
                execvp (argv[0], argv);
                fprintf (stderr, "Failed to execute:");
                for (i = 0; argv[i]; i++) {
@@ -58,7 +210,7 @@ fork_exec_and_wait (char * const argv[])
 }
 
 int
-execute (int argc, char * const argv[])
+execute_with_preload (int argc, char * const argv[], const char *library)
 {
        char **execvp_args;
        int i;
@@ -76,5 +228,5 @@ execute (int argc, char * const argv[])
        /* execvp needs final NULL */
        execvp_args[i] = NULL;
 
-       return fork_exec_and_wait (execvp_args);
+       return fork_exec_with_preload_and_wait (execvp_args, library);
 }
index 8b9192f7ca5bfbe41e19221d324ad6fd1da6d411..03af7779a229aeb6fae5ec7a84d82572b56e7d42 100644 (file)
--- a/execute.h
+++ b/execute.h
 #ifndef EXECUTE_H
 #define EXECUTE_H
 
-/* Execute the program with arguments as specified.
+/* Execute the program with arguments as specified, but with the
+ * library specified in "library" pre-loaded. The library should be
+ * specified as the compiled filename (such as libfips.so).
  */
 int
-execute (int argc, char * const argv[]);
+execute_with_preload (int argc, char * const argv[], const char *library);
 
 #endif
diff --git a/fips.c b/fips.c
index dbb8158b6ea0eee2b79ffed8b1dbb31f46802acc..98cd9ba2ddf287ff3aeab11066b9f54c2124f723 100644 (file)
--- a/fips.c
+++ b/fips.c
@@ -74,7 +74,7 @@ main (int argc, char *argv[])
                exit (1);
        }
 
-       ret = execute (argc - optind, &argv[optind]);
+       ret = execute_with_preload (argc - optind, &argv[optind], "libfips.so");
 
        return ret;
 }
diff --git a/glxwrap.c b/glxwrap.c
new file mode 100644 (file)
index 0000000..51b5faf
--- /dev/null
+++ b/glxwrap.c
@@ -0,0 +1,96 @@
+/* Copyright © 2013, Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <dlfcn.h>
+
+#include <X11/Xlib.h>
+#include <GL/gl.h>
+#include <GL/glx.h>
+#include <GL/glext.h>
+
+#include <sys/time.h>
+
+typedef void (* fips_glXSwapBuffers_t)(Display *dpy, GLXDrawable drawable);
+
+static void *
+lookup (const char *name)
+{
+       const char *libgl_filename = "libGL.so.1";
+       static void *libgl_handle = NULL;
+
+       if (! libgl_handle) {
+               libgl_handle = dlopen (libgl_filename, RTLD_NOW);
+               if (! libgl_handle) {
+                       fprintf (stderr, "Error: Failed to dlopen %s\n",
+                                libgl_filename);
+                       exit (1);
+               }
+       }
+
+       return dlsym (libgl_handle, name);
+}
+
+static void
+call_glXSwapBuffers (Display *dpy, GLXDrawable drawable)
+{
+       static fips_glXSwapBuffers_t real_glXSwapBuffers = NULL;
+       const char *name = "glXSwapBuffers";
+
+       if (! real_glXSwapBuffers) {
+               real_glXSwapBuffers = (fips_glXSwapBuffers_t) lookup (name);
+               if (! real_glXSwapBuffers) {
+                       fprintf (stderr, "Error: Failed to find function %s.\n",
+                                name);
+                       return;
+               }
+       }
+       real_glXSwapBuffers (dpy, drawable);
+}      
+
+void
+glXSwapBuffers (Display *dpy, GLXDrawable drawable)
+{
+       static int initialized = 0;
+       static int frames;
+       static struct timeval tv_start, tv_now;
+
+       if (! initialized) {
+               frames = 0;
+               gettimeofday (&tv_start, NULL);
+               initialized = 1;
+       }
+
+       call_glXSwapBuffers (dpy, drawable);
+
+       frames++;
+       if (frames % 60 == 0) {
+               double fps;
+               gettimeofday (&tv_now, NULL);
+
+               fps = (double) frames / (tv_now.tv_sec - tv_start.tv_sec +
+                                        (tv_now.tv_usec - tv_start.tv_usec) / 1.0e6);
+
+               printf("FPS: %.3f\n", fps);
+       }
+}
diff --git a/libfips.sym b/libfips.sym
new file mode 100644 (file)
index 0000000..28fff84
--- /dev/null
@@ -0,0 +1,6 @@
+{
+global:
+       glXSwapBuffers;
+local:
+       *;
+};