diff --git a/bsd-user/main.c b/bsd-user/main.c
index cc84981..2facee0 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -33,6 +33,8 @@
 #include "tcg.h"
 #include "qemu/timer.h"
 #include "qemu/envlist.h"
+#include "instrument/cmdline.h"
+
 
 int singlestep;
 #if defined(CONFIG_USE_GUEST_BASE)
@@ -688,6 +690,15 @@ static void usage(void)
 #endif
            "-bsd type         select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
            "\n"
+#if defined(CONFIG_TRACE_INSTRUMENT)
+           "Tracing options:\n"
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+           "-instr path       load a dynamic trace instrumentation library\n"
+#endif
+           "-instr-args string\n"
+           "                  arguments to dynamic trace instrumentation library\n"
+           "\n"
+#endif
            "Debug options:\n"
            "-d item1[,...]    enable logging of specified items\n"
            "                  (use '-d help' for a list of log items)\n"
@@ -743,6 +754,8 @@ int main(int argc, char **argv)
     char **target_environ, **wrk;
     envlist_t *envlist = NULL;
     bsd_type = target_openbsd;
+    char *instrument_path = NULL;
+    char *instrument_args = NULL;
 
     if (argc <= 1)
         usage();
@@ -852,6 +865,14 @@ int main(int argc, char **argv)
             singlestep = 1;
         } else if (!strcmp(r, "strace")) {
             do_strace = 1;
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+        } else if (!strcmp(r, "instr")) {
+            instrument_path = argv[optind++];
+#endif
+#if defined(CONFIG_TRACE_INSTRUMENT)
+        } else if (!strcmp(r, "instr-args")) {
+            instrument_args = argv[optind++];
+#endif
         } else
         {
             usage();
@@ -1135,6 +1156,9 @@ int main(int argc, char **argv)
         gdbserver_start (gdbstub_port);
         gdb_handlesig(env, 0);
     }
+
+    instr_init(instrument_path, instrument_args);
+
     cpu_loop(env);
     /* never exits */
     return 0;
diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c
index 18b43f1..dbb6816 100644
--- a/bsd-user/syscall.c
+++ b/bsd-user/syscall.c
@@ -35,6 +35,8 @@
 
 #include "qemu.h"
 #include "qemu-common.h"
+#include "instrument/cmdline.h"
+
 
 //#define DEBUG
 
@@ -334,6 +336,7 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
         _mcleanup();
 #endif
         gdb_exit(cpu_env, arg1);
+        instr_fini();
         /* XXX: should free thread stack and CPU env */
         _exit(arg1);
         ret = 0; /* avoid warning */
@@ -429,6 +432,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
         _mcleanup();
 #endif
         gdb_exit(cpu_env, arg1);
+        instr_fini();
         /* XXX: should free thread stack and CPU env */
         _exit(arg1);
         ret = 0; /* avoid warning */
@@ -501,6 +505,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
         _mcleanup();
 #endif
         gdb_exit(cpu_env, arg1);
+        instr_fini();
         /* XXX: should free thread stack and CPU env */
         _exit(arg1);
         ret = 0; /* avoid warning */
diff --git a/instrument/Makefile.objs b/instrument/Makefile.objs
index ad9ca6d..9c7ea5c 100644
--- a/instrument/Makefile.objs
+++ b/instrument/Makefile.objs
@@ -64,6 +64,8 @@ endif
 ######################################################################
 # Control code
 
+target-obj-y += cmdline.o
+
 target-obj-y += control.o
 
 common-obj-$(CONFIG_SOFTMMU) += hmp.o
diff --git a/instrument/cmdline.c b/instrument/cmdline.c
new file mode 100644
index 0000000..eda6b9c
--- /dev/null
+++ b/instrument/cmdline.c
@@ -0,0 +1,94 @@
+/*
+ * Control dynamic trace instrumentation during program (de)initialization.
+ *
+ * Copyright (C) 2012 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "instrument/cmdline.h"
+
+#include <dlfcn.h>
+
+#include "qemu-common.h"
+#include "instrument/control.h"
+
+#if defined(CONFIG_TRACE_INSTRUMENT_STATIC)
+#include "instrument/qemu-instr/events.h"
+#endif
+
+
+static bool loaded = false;
+
+
+void instr_init(const char *path, const char *args)
+{
+    if (atexit(instr_fini) != 0) {
+        fprintf(stderr, "error: atexit: %s\n", strerror(errno));
+        abort();
+    }
+
+    if (path == NULL) {
+#if defined(CONFIG_TRACE_INSTRUMENT_STATIC)
+        loaded = true;
+        qi_init(args);
+#endif
+        return;
+    }
+
+    InstrLoadError err = instr_load(path, args);
+    switch (err) {
+    case INSTR_LOAD_OK:
+        loaded = true;
+        return;
+    case INSTR_LOAD_UNAVAILABLE:
+        fprintf(stderr, "error: instrument: not available\n");
+        break;
+    case INSTR_LOAD_LOADED:
+        fprintf(stderr, "error: instrument: already loaded\n");
+        break;
+    case INSTR_LOAD_DL:
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+        fprintf(stderr, "error: instrument: error loading library: %s\n", dlerror());
+#else
+        abort();
+#endif
+        break;
+    }
+
+    exit(-1);
+}
+
+void instr_fini(void)
+{
+    if (!loaded) {
+        return;
+    }
+
+#if defined(CONFIG_TRACE_INSTRUMENT_STATIC)
+    qi_fini();
+    return;
+#endif
+
+    InstrUnloadError err = instr_unload();
+    switch (err) {
+    case INSTR_UNLOAD_OK:
+        return;
+    case INSTR_UNLOAD_UNAVAILABLE:
+        fprintf(stderr, "error: not available\n");
+        break;
+    case INSTR_UNLOAD_UNLOADED:
+        /* the user might have already unloaded it */
+        return;
+    case INSTR_UNLOAD_DL:
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+        fprintf(stderr, "error: error unloading library: %s\n", dlerror());
+#else
+        abort();
+#endif
+        break;
+    }
+
+    exit(-1);
+}
diff --git a/instrument/cmdline.h b/instrument/cmdline.h
new file mode 100644
index 0000000..23bee5e
--- /dev/null
+++ b/instrument/cmdline.h
@@ -0,0 +1,41 @@
+/*
+ * Control dynamic trace instrumentation during program (de)initialization.
+ *
+ * Copyright (C) 2012 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef INSTRUMENT__CMDLINE_H
+#define INSTRUMENT__CMDLINE_H
+
+/**
+ * instr_init:
+ * @path: Path to dynamic trace instrumentation library.
+ * @args: Arbitrary string to pass to the library's #qi_init routine.
+ *
+ * Load and initialize the given instrumentation library.
+ *
+ * Automatically installs #instr_fini as an atexit callback.
+ *
+ * If path is %NULL and we're running with static instrumentation, the library
+ * is not loaded but just initialized.
+ *
+ * Pre-condition: There is no library already loaded.
+ */
+void instr_init(const char *path, const char *args);
+
+/**
+ * instr_fini:
+ *
+ * Deinitialize and unload the current instrumentation library.
+ *
+ * If we're running with static instrumentation, the library is not unloaded but
+ * just deinitialized.
+ *
+ * Pre-condition: There is an already loaded library.
+ */
+void instr_fini(void);
+
+#endif  /* INSTRUMENT__CMDLINE_H */
diff --git a/linux-user/main.c b/linux-user/main.c
index 29845f9..5ef766e 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -34,6 +34,8 @@
 #include "qemu/timer.h"
 #include "qemu/envlist.h"
 #include "elf.h"
+#include "instrument/cmdline.h"
+
 
 char *exec_path;
 
@@ -3242,6 +3244,22 @@ static void handle_arg_reserved_va(const char *arg)
 }
 #endif
 
+static const char *instrument_path = NULL;
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+static void handle_arg_instrument(const char *arg)
+{
+    instrument_path = arg;
+}
+#endif
+
+static const char *instrument_args = NULL;
+#if defined(CONFIG_TRACE_INSTRUMENT)
+static void handle_arg_instrument_args(const char *arg)
+{
+    instrument_args = arg;
+}
+#endif
+
 static void handle_arg_singlestep(const char *arg)
 {
     singlestep = 1;
@@ -3293,6 +3311,14 @@ static const struct qemu_argument arg_table[] = {
     {"R",          "QEMU_RESERVED_VA", true,  handle_arg_reserved_va,
      "size",       "reserve 'size' bytes for guest virtual address space"},
 #endif
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+    {"instr",      "QEMU_INSTR",       true,  handle_arg_instrument,
+     "path",       "load a dynamic trace instrumentation library"},
+#endif
+#if defined(CONFIG_TRACE_INSTRUMENT)
+    {"instr-args",  "QEMU_INSTR_ARGS", true,  handle_arg_instrument_args,
+     "string",     "arguments to dynamic trace instrumentation library"},
+#endif
     {"d",          "QEMU_LOG",         true,  handle_arg_log,
      "item[,...]", "enable logging of specified items "
      "(use '-d help' for a list of items)"},
@@ -3978,6 +4004,9 @@ int main(int argc, char **argv, char **envp)
         }
         gdb_handlesig(env, 0);
     }
+
+    instr_init(instrument_path, instrument_args);
+
     cpu_loop(env);
     /* never exits */
     return 0;
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 19630ea..f35d421 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -106,6 +106,8 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
 #include "cpu-uname.h"
 
 #include "qemu.h"
+#include "instrument/cmdline.h"
+
 
 #if defined(CONFIG_USE_NPTL)
 #define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \
@@ -5230,6 +5232,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         _mcleanup();
 #endif
         gdb_exit(cpu_env, arg1);
+        instr_fini();
         _exit(arg1);
         ret = 0; /* avoid warning */
         break;
@@ -7001,6 +7004,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         _mcleanup();
 #endif
         gdb_exit(cpu_env, arg1);
+        instr_fini();
         ret = get_errno(exit_group(arg1));
         break;
 #endif
diff --git a/qemu-options.hx b/qemu-options.hx
index 797d992..7c7e35b 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2942,6 +2942,24 @@ the @var{simple} tracing backend.
 @end table
 ETEXI
 
+DEF("instr", HAS_ARG, QEMU_OPTION_instr,
+    "-instr file=<file>[,args=<string>]\n"
+    "                load an instrumentation library\n",
+    QEMU_ARCH_ALL)
+STEXI
+@item -instr file=@var{file}[,args=@var{args}]
+@findex -instr
+
+Load a dynamic trace instrumentation library.
+
+@table @option
+@item file=@var{file}
+Load the given dynamic trace instrumentation library.
+@item args=@var{string}
+An arbitrary string passed as the argument to the library's @code{qi_init} routine.
+@end table
+ETEXI
+
 HXCOMM Internal use
 DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL)
 DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL)
diff --git a/qmp-commands.hx b/qmp-commands.hx
index f704221..d86d15c 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1519,6 +1519,7 @@ Load a dynamic instrumentation library.
 Arguments:
 
 - path: path to the dynamic instrumentation library
+- args: arguments to the dynamic instrumentation library
 
 Example:
 
@@ -1529,8 +1530,8 @@ EQMP
 
     {
         .name       = "instr-load",
-        .args_type  = "path:s",
-        .params     = "path",
+        .args_type  = "path:F,args:s?",
+        .params     = "path args",
         .mhandler.cmd_new = qmp_marshal_input_instr_load,
     },
 
diff --git a/vl.c b/vl.c
index febd2ea..90a3b39 100644
--- a/vl.c
+++ b/vl.c
@@ -162,6 +162,7 @@ int main(int argc, char **argv)
 
 #include "trace.h"
 #include "trace/control.h"
+#include "instrument/cmdline.h"
 #include "qemu/queue.h"
 #include "sysemu/cpus.h"
 #include "sysemu/arch_init.h"
@@ -349,6 +350,22 @@ static QemuOptsList qemu_trace_opts = {
     },
 };
 
+static QemuOptsList qemu_instr_opts = {
+    .name = "instr",
+    .implied_opt_name = "instr",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_instr_opts.head),
+    .desc = {
+        {
+            .name = "file",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "args",
+            .type = QEMU_OPT_STRING,
+        },
+        { /* end of list */ }
+    },
+};
+
 static QemuOptsList qemu_option_rom_opts = {
     .name = "option-rom",
     .implied_opt_name = "romfile",
@@ -2837,6 +2854,8 @@ int main(int argc, char **argv, char **envp)
     };
     const char *trace_events = NULL;
     const char *trace_file = NULL;
+    const char *instrument_path = NULL;
+    const char *instrument_args = NULL;
 
     atexit(qemu_run_exit_notifiers);
     error_set_progname(argv[0]);
@@ -2862,6 +2881,7 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_global_opts);
     qemu_add_opts(&qemu_mon_opts);
     qemu_add_opts(&qemu_trace_opts);
+    qemu_add_opts(&qemu_instr_opts);
     qemu_add_opts(&qemu_option_rom_opts);
     qemu_add_opts(&qemu_machine_opts);
     qemu_add_opts(&qemu_boot_opts);
@@ -3747,6 +3767,20 @@ int main(int argc, char **argv, char **envp)
                 trace_file = qemu_opt_get(opts, "file");
                 break;
             }
+            case QEMU_OPTION_instr:
+            {
+                olist = qemu_find_opts("instr");
+                if (!olist) {
+                    exit(1);
+                }
+                opts = qemu_opts_parse(olist, optarg, 0);
+                if (!opts) {
+                    exit(1);
+                }
+                instrument_path = qemu_opt_get(opts, "file");
+                instrument_args = qemu_opt_get(opts, "args");
+                break;
+            }
             case QEMU_OPTION_readconfig:
                 {
                     int ret = qemu_read_config_file(optarg);
@@ -4334,6 +4368,8 @@ int main(int argc, char **argv, char **envp)
         }
     }
 
+    instr_init(instrument_path, instrument_args);
+
     if (incoming) {
         Error *local_err = NULL;
         qemu_start_incoming_migration(incoming, &local_err);
